2016-07-12 3 views
2

Djangoモデルの管理リストビューで各行のステータス情報を取得する必要があるユースケースがあります。複数のフィールドをDjango Model Adminの読み込みフィールドに動的に追加する

私のようなコードを使用してデータを取り出すことができます。しかし、この「何とか」クラスがステータスと進行状況の両方を返し

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

    readonly_fields = ('id', 'status') 

。 、インスタンスを持つこの「何とか」クラスを呼び出す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

最初の2つのソリューションを試しましたが、うまく動作しませんでした。ラムダソリューションは、両方のフィールドの進捗状況を戻しました。第2のデコレータソリューションは、両方のフィールドのステータスを返しました。 – Mike91

+0

明らかに、それは 'get_info()[field]'が呼び出されると、ループはすでに終了しており、 'fields'はまだ最後の値を持っているからです。私は問題を修正した。 –

関連する問題