Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[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 authored April 22, 2009
8  django/contrib/admin/options.py
@@ -267,10 +267,12 @@ def get_form(self, request, obj=None, **kwargs):
267 267
             exclude = []
268 268
         else:
269 269
             exclude = list(self.exclude)
  270
+        # if exclude is an empty list we pass None to be consistant with the
  271
+        # default on modelform_factory
270 272
         defaults = {
271 273
             "form": self.form,
272 274
             "fields": fields,
273  
-            "exclude": exclude + kwargs.get("exclude", []),
  275
+            "exclude": (exclude + kwargs.get("exclude", [])) or None,
274 276
             "formfield_callback": self.formfield_for_dbfield,
275 277
         }
276 278
         defaults.update(kwargs)
@@ -803,12 +805,14 @@ def get_formset(self, request, obj=None, **kwargs):
803 805
             exclude = []
804 806
         else:
805 807
             exclude = list(self.exclude)
  808
+        # if exclude is an empty list we use None, since that's the actual
  809
+        # default
806 810
         defaults = {
807 811
             "form": self.form,
808 812
             "formset": self.formset,
809 813
             "fk_name": self.fk_name,
810 814
             "fields": fields,
811  
-            "exclude": exclude + kwargs.get("exclude", []),
  815
+            "exclude": (exclude + kwargs.get("exclude", [])) or None,
812 816
             "formfield_callback": self.formfield_for_dbfield,
813 817
             "extra": self.extra,
814 818
             "max_num": self.max_num,
43  django/forms/models.py
@@ -331,16 +331,34 @@ class ModelForm(BaseModelForm):
331 331
 
332 332
 def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
333 333
                        formfield_callback=lambda f: f.formfield()):
334  
-    # HACK: we should be able to construct a ModelForm without creating
335  
-    # and passing in a temporary inner class
336  
-    class Meta:
337  
-        pass
338  
-    setattr(Meta, 'model', model)
339  
-    setattr(Meta, 'fields', fields)
340  
-    setattr(Meta, 'exclude', exclude)
  334
+    # Create the inner Meta class. FIXME: ideally, we should be able to
  335
+    # construct a ModelForm without creating and passing in a temporary
  336
+    # inner class.
  337
+
  338
+    # Build up a list of attributes that the Meta object will have.
  339
+    attrs = {'model': model}
  340
+    if fields is not None:
  341
+        attrs['fields'] = fields
  342
+    if exclude is not None:
  343
+        attrs['exclude'] = exclude
  344
+
  345
+    # If parent form class already has an inner Meta, the Meta we're
  346
+    # creating needs to inherit from the parent's inner meta.
  347
+    parent = (object,)
  348
+    if hasattr(form, 'Meta'):
  349
+        parent = (form.Meta, object)
  350
+    Meta = type('Meta', parent, attrs)
  351
+
  352
+    # Give this new form class a reasonable name.
341 353
     class_name = model.__name__ + 'Form'
342  
-    return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
343  
-                              'formfield_callback': formfield_callback})
  354
+
  355
+    # Class attributes for the new form class.
  356
+    form_class_attrs = {
  357
+        'Meta': Meta,
  358
+        'formfield_callback': formfield_callback
  359
+    }
  360
+
  361
+    return ModelFormMetaclass(class_name, (form,), form_class_attrs)
344 362
 
345 363
 
346 364
 # ModelFormSets ##############################################################
@@ -578,13 +596,6 @@ def inlineformset_factory(parent_model, model, form=ModelForm,
578 596
     # enforce a max_num=1 when the foreign key to the parent model is unique.
579 597
     if fk.unique:
580 598
         max_num = 1
581  
-    if fields is not None:
582  
-        fields = list(fields)
583  
-        fields.append(fk.name)
584  
-    else:
585  
-        # get all the fields for this model that will be generated.
586  
-        fields = fields_for_model(model, fields, exclude, formfield_callback).keys()
587  
-        fields.append(fk.name)
588 599
     kwargs = {
589 600
         'form': form,
590 601
         'formfield_callback': formfield_callback,
47  tests/regressiontests/modeladmin/models.py
@@ -37,7 +37,7 @@ class ValidationTestInlineModel(models.Model):
37 37
 
38 38
 __test__ = {'API_TESTS': """
39 39
 
40  
->>> from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL
  40
+>>> from django.contrib.admin.options import ModelAdmin, TabularInline, HORIZONTAL, VERTICAL
41 41
 >>> from django.contrib.admin.sites import AdminSite
42 42
 
43 43
 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
262 262
 >>> list(cmafa.base_fields['transport'].widget.choices)
263 263
 [('', u'None'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')]
264 264
 
  265
+>>> class AdminConcertForm(forms.ModelForm):
  266
+...     class Meta:
  267
+...         model = Concert
  268
+...         exclude = ('transport',)
  269
+
  270
+>>> class ConcertAdmin(ModelAdmin):
  271
+...     form = AdminConcertForm
  272
+
  273
+>>> ma = ConcertAdmin(Concert, site)
  274
+>>> ks = ma.get_form(request).base_fields.keys()
  275
+>>> ks.sort()
  276
+>>> ks
  277
+['day', 'main_band', 'opening_band']
  278
+
  279
+>>> class AdminConcertForm(forms.ModelForm):
  280
+...     extra = forms.CharField()
  281
+...     class Meta:
  282
+...         model = Concert
  283
+...         fields = ['extra', 'transport']
  284
+
  285
+>>> class ConcertAdmin(ModelAdmin):
  286
+...     form = AdminConcertForm
  287
+
  288
+>>> ma = ConcertAdmin(Concert, site)
  289
+>>> ks = ma.get_form(request).base_fields.keys()
  290
+>>> ks.sort()
  291
+>>> ks
  292
+['extra', 'transport']
  293
+
  294
+>>> class ConcertInline(TabularInline):
  295
+...     form = AdminConcertForm
  296
+...     model = Concert
  297
+...     fk_name = 'main_band'
  298
+
  299
+>>> class BandAdmin(ModelAdmin):
  300
+...     inlines = [
  301
+...         ConcertInline
  302
+...     ]
  303
+
  304
+>>> ma = BandAdmin(Band, site)
  305
+>>> ks = list(ma.get_formsets(request))[0]().forms[0].fields.keys()
  306
+>>> ks.sort()
  307
+>>> ks
  308
+['DELETE', 'extra', 'id', 'main_band', 'transport']
  309
+
265 310
 >>> band.delete()
266 311
 
267 312
 # ModelAdmin Option Validation ################################################

0 notes on commit 421b22e

Please sign in to comment.
Something went wrong with that request. Please try again.