I have a model, Foo
. It has several database properties, and several properties that are calculated based on a combination of factors. I would like to present these calculated properties to the user as if they were database properties. (The backing factors would be changed to reflect user input.) Is there a way to do this with the Django admin interface?
The way to override fields is to create a Form for use with the ModelAdmin object. This didn't work for me, you need to pass an instance of the widget, rather than the class. The instance worked perfectly, though. I typically would create custom admin forms in the admin.py and not mix them in with forms.py.
editable=False will make the field disappear from all forms including admin and ModelForm i.e., it can not be edited using any form. The field will not be displayed in the admin or any other ModelForm.
The admin interface is also customizable in many ways. This post is going to focus on one such customization, something called inlines. When two Django models share a foreign key relation, inlines can be used to expose the related model on the parent model page. This can be extremely useful for many applications.
It's easy enough to get arbitrary data to show up in change list or make a field show up in the form: list_display
arbitrarily takes either actual model properties, or methods defined on the model or the modeladmin, and you can subclass forms.ModelForm
to add any field type you'd like to the change form.
What's far more difficult/impossible is combining the two, i.e. having an arbitrary piece of data on the change list that you can edit in-place by specifying list_editable
. Django seems to only accept a true model property that corresponds to a database field. (even using @property
on the method in the model definition is not enough).
Has anyone found a way to edit a field not actually present on the model right from the change list page?
I would suggest you subclass a modelform for Foo
(FooAdminForm) to add your own fields not backed by the database. Your custom validation can reside in the clean_*
methods of ModelForm.
Inside the save_model
method of FooAdmin
you get the request, an instance of Foo
and the form data, so you could do all processing of the data before/after saving the instance.
Here is an example for a model with a custom form registered with django admin:
from django import forms
from django.db import models
from django.contrib import admin
class Foo(models.Model):
name = models.CharField(max_length=30)
class FooAdminForm(forms.ModelForm):
# custom field not backed by database
calculated = forms.IntegerField()
class Meta:
model = Foo
class FooAdmin(admin.ModelAdmin):
# use the custom form instead of a generic modelform
form = FooAdminForm
# your own processing
def save_model(self, request, obj, form, change):
# for example:
obj.name = 'Foo #%d' % form.cleaned_data['calculated']
obj.save()
admin.site.register(Foo, FooAdmin)
Providing initial values for custom fields based on instance data
(I'm not sure if this is the best solution, but it should work.)
When a modelform for a existing model instance in the database is constructed, it gets passed this instance. So in FooAdminForm's __init__
one can change the fields attributes based on instance data.
def __init__(self, *args, **kwargs):
super(FooAdminForm, self).__init__(*args, **kwargs)
# only change attributes if an instance is passed
instance = kwargs.get('instance')
if instance:
self.fields['calculated'].initial = (instance.bar == 42)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With