我有一个用例,我需要在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')
由于
答案 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