2016-07-12 2 views
2

У меня есть случай, когда мне нужно получить информацию о состоянии для каждой строки в представлении списка admin в модели Django.Добавление нескольких полей в Django Model Admin readonly fields динамически

я могу получить данные с помощью кода, как:

def blah(admin.ModelAdmin): 
    @staticmethod 
    def status(instance): 
     return Blah(instance).get_info()['status'] 

    readonly_fields = ('id', 'status') 

Однако класс возвращает это «Ли» как статус и прогресс. Есть простой способ вызвать этот класс «Л» с экземпляром, возвращает поле статуса, а также поле прогресса и добавить как к readonly_fields кортежа без дублирования, как:

def blah(admin.ModelAdmin): 
    @staticmethod 
    def status(instance): 
     return Blah(instance).get_info()['status'] 

    @staticmethod 
    def progress(instance): 
     return Blah(instance).get_info()['progress'] 

    readonly_fields = ('id', 'status', 'progress') 

Благодарность

+0

Почему вы используете статические методы? –

+0

Им не обязательно быть статичными, мне не нужны были обычные методы, но их не было проблемой. – Mike91

+0

Правильно, если вы не используете 'self', им не нужно быть методом экземпляра. Но я был немного смущен, так как я привык к методу экземпляра для них. :) –

ответ

1

Я думаю, вы можете использовать декоратор класса.

def get_blah_info(field): 
    return staticmethod(lambda x: Blah(x).get_info()[field]) 

def blah_decorator(*fields): 
    def wrapper(cls): 
     for field in fields: 
      setattr(cls, field, get_blah_info(field)) 
      cls.readonly_fields.append(field) 
     return cls 
    return wrapper 

@blah_decorator('status', 'progress') 
class BlahAdmin(admin.ModelAdmin): 
    readonly_fields = ['id'] 

Но я не понимаю, почему вы используете статический метод.

Более сложный пример:

from django.utils.translation import ugettext_lazy as _ 

def get_blah_info(blah_class, field): 
    def get_info(self, instance): 
     return blah_class(instance).get_info()[field] 
    return get_info 

def blah_decorator(blah_class, **fields): 
    def wrapper(cls): 
     # Make sure readonly_fields is a list so that we can append elements 
     readonly_fields = getattr(cls, 'readonly_fields', []) 
     if not hasattr(readonly_fields, 'append'): 
      readonly_fields = list(readonly_fields) 

     for field, short_description in fields.items(): 
      # Define the method for each field and append it to readonly_fields 
      get_info = get_blah_info(blah_class, field) 
      get_info.__name__ = field 
      get_info.short_description = short_description 
      setattr(cls, field, get_info) 
      readonly_fields.append(field) 
     cls.readonly_fields = readonly_fields 
     return cls 
    return wrapper 

@blah_decorator(Blah, status=_("Status"), progress=_("Progress")) 
class BlahAdmin(admin.ModelAdmin): 
    readonly_fields = ['id'] 

Конечно, приведенный выше пример может быть адаптирован для использования статических методов, если вы предпочитаете.


Другим решением было бы использовать metaclass.

class BlahMetaclass(type): 

    @staticmethod 
    def get_blah_info(blah_class, field): 
     def get_info(self, instance): 
      return blah_class(instance).get_info()[field] 
     return get_info 

    def __new__(cls, cls_name, bases, attrs): 
     blah_class = attrs['blah_class'] 
     blah_fields = attrs['blah_fields'] 
     readonly_fields = attrs.get('readonly_fields', []) 
     if not hasattr(readonly_fields, 'append'): 
      readonly_fields = list(readonly_fields) 

     for field, short_description in blah_fields: 
      if field in attrs: 
       continue # Let the class have the precedence 
      get_info = cls.get_blah_info(blah_class, field) 
      get_info.__name__ = field 
      get_info.short_description = short_description 
      attrs[field] = get_info 
      if field not in readonly_fields: 
       # Do not add `field` to `readonly_fields` if it is already present. 
       # This enables to redefine the fields order rather than 
       # appending `blah_fields`. 
       readonly_fields.append(readonly_fields) 

     attrs['readonly_fields'] = readonly_fields 

     # Optionally remove `blah_class` and `blah_fields` if 
     # not useful any further. 
     del attrs['blah_class'] 
     del attrs['blah_fields'] 

     return super().__new__(cls, clsname, bases, attrs) 


class BlahModelAdmin(admin.ModelAdmin, metaclass=BlahMetaclass): 
    """Optionally, create a new base ModelAdmin.""" 


class BlahAdmin(BlahModelAdmin): 

    blah_class = Blah 
    blah_fields = [ 
     ('status' _("Status")), 
     ('progress', _("Progress")), 
    ] 

    readonly_fields = ['id'] 
    # Or, for instance: readonly_fields = ['status', 'id', 'progress'] 
    # If you want to change the order 
+0

Я пробовал первые два решения, но не мог заставить его работать. Лямбда-решение просто вернуло прогресс для обоих полей, а второе решение декоратора вернуло статус для обоих полей. – Mike91

+0

Да, очевидно, это потому, что, когда вызывается 'get_info() [field]', цикл уже завершен, а 'fields' имеет еще последнее значение. Я исправил проблему. –

Смежные вопросы