将多个字段动态添加到Django Model Admin只读字段

时间:2016-07-12 16:58:49

标签: python django django-admin

我有一个用例,我需要在Django模型管理列表视图中检索每一行的状态信息。

我可以使用以下代码检索数据:

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

    readonly_fields = ('id', 'status')

然而,这个' Blah' class返回状态和进度。有没有一种简单的方法可以称之为“Blah'使用实例的类,返回状态字段以及进度字段,并将两者都添加到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')

由于

1 个答案:

答案 0 :(得分: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