Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[1.0.X] Fixed #10208: `ModelAdmin` now respects the `exclude` and `fi…

…eld` atributes of custom `ModelForm`s. Thanks, Alex Gaynor. Backport of r10619 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@10620 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 421b22e8eed0094669f6b197ac52bace5b5f29d9 1 parent cb92893
Jacob Kaplan-Moss jacobian authored
8 django/contrib/admin/options.py
View
@@ -267,10 +267,12 @@ def get_form(self, request, obj=None, **kwargs):
exclude = []
else:
exclude = list(self.exclude)
+ # if exclude is an empty list we pass None to be consistant with the
+ # default on modelform_factory
defaults = {
"form": self.form,
"fields": fields,
- "exclude": exclude + kwargs.get("exclude", []),
+ "exclude": (exclude + kwargs.get("exclude", [])) or None,
"formfield_callback": self.formfield_for_dbfield,
}
defaults.update(kwargs)
@@ -803,12 +805,14 @@ def get_formset(self, request, obj=None, **kwargs):
exclude = []
else:
exclude = list(self.exclude)
+ # if exclude is an empty list we use None, since that's the actual
+ # default
defaults = {
"form": self.form,
"formset": self.formset,
"fk_name": self.fk_name,
"fields": fields,
- "exclude": exclude + kwargs.get("exclude", []),
+ "exclude": (exclude + kwargs.get("exclude", [])) or None,
"formfield_callback": self.formfield_for_dbfield,
"extra": self.extra,
"max_num": self.max_num,
43 django/forms/models.py
View
@@ -331,16 +331,34 @@ class ModelForm(BaseModelForm):
def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
formfield_callback=lambda f: f.formfield()):
- # HACK: we should be able to construct a ModelForm without creating
- # and passing in a temporary inner class
- class Meta:
- pass
- setattr(Meta, 'model', model)
- setattr(Meta, 'fields', fields)
- setattr(Meta, 'exclude', exclude)
+ # Create the inner Meta class. FIXME: ideally, we should be able to
+ # construct a ModelForm without creating and passing in a temporary
+ # inner class.
+
+ # Build up a list of attributes that the Meta object will have.
+ attrs = {'model': model}
+ if fields is not None:
+ attrs['fields'] = fields
+ if exclude is not None:
+ attrs['exclude'] = exclude
+
+ # If parent form class already has an inner Meta, the Meta we're
+ # creating needs to inherit from the parent's inner meta.
+ parent = (object,)
+ if hasattr(form, 'Meta'):
+ parent = (form.Meta, object)
+ Meta = type('Meta', parent, attrs)
+
+ # Give this new form class a reasonable name.
class_name = model.__name__ + 'Form'
- return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
- 'formfield_callback': formfield_callback})
+
+ # Class attributes for the new form class.
+ form_class_attrs = {
+ 'Meta': Meta,
+ 'formfield_callback': formfield_callback
+ }
+
+ return ModelFormMetaclass(class_name, (form,), form_class_attrs)
# ModelFormSets ##############################################################
@@ -578,13 +596,6 @@ def inlineformset_factory(parent_model, model, form=ModelForm,
# enforce a max_num=1 when the foreign key to the parent model is unique.
if fk.unique:
max_num = 1
- if fields is not None:
- fields = list(fields)
- fields.append(fk.name)
- else:
- # get all the fields for this model that will be generated.
- fields = fields_for_model(model, fields, exclude, formfield_callback).keys()
- fields.append(fk.name)
kwargs = {
'form': form,
'formfield_callback': formfield_callback,
47 tests/regressiontests/modeladmin/models.py
View
@@ -37,7 +37,7 @@ class ValidationTestInlineModel(models.Model):
__test__ = {'API_TESTS': """
->>> from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL
+>>> from django.contrib.admin.options import ModelAdmin, TabularInline, HORIZONTAL, VERTICAL
>>> from django.contrib.admin.sites import AdminSite
None of the following tests really depend on the content of the request, so
@@ -262,6 +262,51 @@ class and an AdminSite instance, so let's just go ahead and do that manually
>>> list(cmafa.base_fields['transport'].widget.choices)
[('', u'None'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')]
+>>> class AdminConcertForm(forms.ModelForm):
+... class Meta:
+... model = Concert
+... exclude = ('transport',)
+
+>>> class ConcertAdmin(ModelAdmin):
+... form = AdminConcertForm
+
+>>> ma = ConcertAdmin(Concert, site)
+>>> ks = ma.get_form(request).base_fields.keys()
+>>> ks.sort()
+>>> ks
+['day', 'main_band', 'opening_band']
+
+>>> class AdminConcertForm(forms.ModelForm):
+... extra = forms.CharField()
+... class Meta:
+... model = Concert
+... fields = ['extra', 'transport']
+
+>>> class ConcertAdmin(ModelAdmin):
+... form = AdminConcertForm
+
+>>> ma = ConcertAdmin(Concert, site)
+>>> ks = ma.get_form(request).base_fields.keys()
+>>> ks.sort()
+>>> ks
+['extra', 'transport']
+
+>>> class ConcertInline(TabularInline):
+... form = AdminConcertForm
+... model = Concert
+... fk_name = 'main_band'
+
+>>> class BandAdmin(ModelAdmin):
+... inlines = [
+... ConcertInline
+... ]
+
+>>> ma = BandAdmin(Band, site)
+>>> ks = list(ma.get_formsets(request))[0]().forms[0].fields.keys()
+>>> ks.sort()
+>>> ks
+['DELETE', 'extra', 'id', 'main_band', 'transport']
+
>>> band.delete()
# ModelAdmin Option Validation ################################################
Please sign in to comment.
Something went wrong with that request. Please try again.