[英]How to add multiple fields to Django Model Admin readonly_fields dynamically
我有一个用例,我需要在 Django 模型管理列表视图中检索每一行的状态信息。
我可以使用以下代码检索数据:
def blah(admin.ModelAdmin):
@staticmethod
def status(instance):
return Blah(instance).get_info()['status']
readonly_fields = ('id', 'status')
但是,这个 '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')
我认为您可以使用类装饰器。
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']
当然,如果您愿意,可以将上面的示例修改为使用静态方法。
另一种解决方案是使用元类。
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.