#14496 closed Bug (fixed)
Conflict between ModelForm.Meta.exclude and ModelAdmin.readonly attributes
Description ¶
I have problem with contrib.admin application. Consider we have demo application with this three files:
demo/models.py:
from django.db import models class Demo(models.Model): name = models.CharField(max_length=100) note = models.TextField(blank=True, null=True)
demo/admin.py:
from django.contrib import admin from models import Demo from forms import DemoAdminForm class DemoAdmin(admin.ModelAdmin): readonly_fields = ('name', ) form = DemoAdminForm admin.site.register(Demo, DemoAdmin)
demo/forms.py:
from django import forms from models import Demo class DemoAdminForm(forms.ModelForm): class Meta: model = Demo exclude = ('note', )
If I go to administration, and edit some Demo object, I see two fields: editable Note and readonly Name. But! I should see only readonly Name (because I use own ModelForm class which excluded note field). Try to comment line "readonly_fields = ('name', )" at admin.py file. You will see, that in this situation (no readonly attribute on ModelAdmin) Django correctly accept Meta class on ModelForm and exclude field Note.
I think that there is a problem in the way, how Django construct ModelForm class. It don't respect Meta classes in Form, if there is at same time readonly attribute on ModelAdmin.
Change History (11)
comment:1 by , 14 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:2 by , 14 years ago
Component: | Contrib apps → django.contrib.admin |
---|
comment:3 by , 14 years ago
Severity: | → Normal |
---|---|
Type: | → Bug |
by , 14 years ago
Attachment: | 14496.custom-admin-form-exclude.diff added |
---|
comment:4 by , 14 years ago
Easy pickings: | unset |
---|---|
Has patch: | set |
by , 14 years ago
Attachment: | 14496.custom-admin-form-exclude.alternative.diff added |
---|
comment:5 by , 14 years ago
I've had second thoughts about this and posted an alternative patch.
I still think this ticket is a valid bug. The confusion mostly comes from the fact that defining readonly_fields
makes the ModelAdmin
ignore the ModelForm
's Meta.exclude
option even if the ModelAdmin
doesn't define its own exclude
. Both patches fix that bug.
However, while the first patch systematically considers the ModelForm.Meta.exclude
option, the new patch only considers it if ModelAdmin
doesn't define its own exclude
. If ModelAdmin
does define exclude
, then it takes precedence. This is a way of transferring control from ModelForm
to ModelAdmin
, which seems like a more explicit way of handling things.
comment:6 by , 14 years ago
UI/UX: | unset |
---|
The patch and tests look good. It'll be nice to have the precedence of exclude options defined and documented.
I also added a parallel patch for generic inlines at #15907.
comment:7 by , 14 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
comment:9 by , 13 years ago
In case someone wants code to work around this, put this in your ModelAdmin subclass:
def get_form(self, request, obj=None, **kwargs): """Work around https://code.djangoproject.com/ticket/14496 (not fixed in Django 1.3)""" form = super(OffcutAdminRestricted, self).get_form(request, obj, **kwargs) for e in self.form._meta.exclude: if e in form.base_fields: del form.base_fields[e] form._meta.exclude.append(e) return form
The reason this is happening is that
django.forms.models.modelform_factory()
systematically overrides the form'sMeta.exclude
attribute with theexclude
kwarg that it receives. So, in this case,DemoAdminForm.Meta.exclude
is overridden by('name', )
which comes from theDemoAdmin.readonly_fields
declaration, and the "note" field is therefore included in the form.modelform_factory
's behaviour in itself is actually correct, but the problem is thatModelAdmin.get_form()
does not take it properly into account.Like the reporter I think it makes sense to assume that, if a custom form is provided, then that form's
exclude
option should be considered and those fields should be excluded from the generated form, as well as the fields defined inModelAdmin.exclude
andModelAdmin.readonly_fields
. The attached patch rectifies this behaviour both forModelAdmin.get_form()
andInlineModelAdmin.get_formset()
.