2016-07-12 99 views
2

我有一個用例,我需要在Django模型管理列表視圖中檢索每一行的狀態信息。動態添加多個字段到Django Model Admin只讀字段

我可以使用如下代碼檢索數據:

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

我嘗試了前兩個解決方案,但無法讓它工作。 lambda解決方案只是返回了兩個字段的進度,第二個裝飾器解決方案返回了兩個字段的狀態。 – Mike91

+0

很明顯,這是因爲當調用'get_info()[field]'時,循環已經結束,'fields'仍然是最後一個值。我解決了這個問題。 –