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