简体   繁体   English

如何将多个字段动态添加到 Django 模型管理 readonly_fields

[英]How to add multiple fields to Django Model Admin readonly_fields dynamically

I have a use case where I need to retrieve status information for each row in the Django model admin list view.我有一个用例,我需要在 Django 模型管理列表视图中检索每一行的状态信息。

I can retrieve data using code like:我可以使用以下代码检索数据:

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

However, this 'Blah' class returns both the status and progress.但是,这个 'Blah' 类返回状态和进度。 Is there an easy way to call this 'Blah' class with the instance, return the status field and also a progress field and add both to the readonly_fields tuple without duplication like:有没有一种简单的方法可以用实例调用这个“废话”类,返回状态字段和进度字段,并将两者都添加到 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')

I think you may use a class decorator.我认为您可以使用类装饰器。

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']

But I don't catch why you are using a static method.但我不明白你为什么使用静态方法。

A more advanced example:一个更高级的例子:

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']

Of course, the above example can be adapted to use static methods if you prefer.当然,如果您愿意,可以将上面的示例修改为使用静态方法。


Another solution would be to use a 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM