Admin change list view customization with ListColumn
Related ticket: #8054 Move method properties for admin list customization to ModelAdmin
Ticket goals are:
- to change list_display syntax;
- add to list_display_links the same items type support as list_display has (Now Django allows to use in list_display_links only model fields, properties or methods).
The current approach for customising list_display seems contrary to the spirit of newforms-admin. Method properties such as .short_description, .boolean and .allow_tags rarely belong in the model definition.
In fact, I noted that these options, for customising the look of the list column, duplicate filter functionality. .allow_tags is exactly |safe; |boolean could be a filter provided by django.contrib.admin.templatetags. The case is much more convincing when you take into account user-defined filters: if I already have a filter for presenting values for user consumption, I should not need to provide a model method to replicate or apply that filter.
ListColumn API
Arguments:
- field_name - callable function or string with model field, Model method name or current ModelAdmin method name.
Keyword arguments:
- header - change list column header. If not provided standard algorithm to header column calculation will be used.
- filter - template filters will be applied to value on output. It is string like this 'filter1|filter2:"arg"'.
- load_filters - list of required template tags libraries. This libraries will be load before apply template filters to output value. Eg.: ['tagging_tags', 'tagging_autocomplete_tags'].
Note: In admin area you can use "boolean_icon" template filter (it is provided by "admin_list" template library, this used to render admin change list). It is useful to display values of boolean fields or callables. It show green or red mark instead of True or False values. It is applied for boolean model fields by default (but not for callables) on render admin change list if no any filters is specified.
if no any filters is specified in filter attribute, for some model fields will be applied default filters according to table below:
model field | applied filter |
BooleanField | boolean_icon |
NullBooleanField | boolean_icon |
DateTimeField | date:"DATETIME_FORMAT" |
DateField | date:"DATE_FORMAT" |
TimeField | time:"TIME_FORMAT" |
DecimalField | floatformat:%d |
For other model fields default filter is not specified and no any filter will be applied automatic if filter attribute isn't provided in the ListColumn instance.
- order_field - string with model field name witch will be used to provide order by this column. If it provided for callable functions, its will be called with this field value in first argument instead of current object instance (this feature exists at least at svn rev. 14188 but currently is not documented).
- choices - choices used to mapping display value. This can redefine choices from model field for shown in admin change list. If filter argument is provided, this is applied before template filters.
Work with list_display_link ModelAdmin property
If list_display_link ModelAdmin property isn't provided, a first of list_display items will be used as link on object change form.
In list_display_link you can use the same items as you can use in list_display. It can be a model field, property or methods, ModelAdmin methods, functions or ListColumn instances. Note: all items of list_display_link must be exists in list_display too.
Example
Current way to admin change list view customization:
class Account(models.Model): foo = model.BooleanField(...) bar = model.CharField(...) baz = model.CharField(...) bonk = model.CharField(...) ends = models.DateTimeField() def get_bar_column(self): return ... get_bar_column.allow_tags = True get_bar_column.short_description = 'Bar' def timeuntil_ends(): return ... class AccountAdmin(admin.ModelAdmin): list_display = ['foo', 'get_bar_column', 'baz', 'bonk', 'timeuntil_ends']
Proposed way:
class Account(models.Model): foo = model.BooleanField(...) bar = model.CharField(...) baz = model.CharField(...) bonk = model.CharField(...) ends = models.DateTimeField() def get_bar(self): return ... class AccountAdmin(admin.ModelAdmin): def formated_bonk(bonk): return ... bar_column = admin.ListColumn('get_bar', filter='safe', order_field='bar') formated_bonk = admin.ListColumn('formated_bonk', order_field="bonk") list_display = [ bar_column, admin.ListColumn('foo', header='Foo Description', filter='boolean_icon'), admin.ListColumn('baz', filter='truncatewords:"2"|slugify'), 'bonk', formated_bonk, admin.ListColumn('ends', filter='timeuntil', header="Ending in") ] list_display_link = [bar_column, 'bonk']
In the example
admin.ListColumn('baz', filter='truncatewords:"2"|slugify'),
would render exactly the same as
{{row.baz|truncatewords:"2"|slugify}}
Links will be only on 1st (bar_column is the property of AccountAdmin and ListColumn instance) and 4th ('bonk' is the Acount model field) columns. formated_bonk is not in list_display_link and not will be with link, although formated_bonk and 'bonk' are the same model field. 'bonk' and formated_bonk is defferent essence in list_display and in list_display_link too.