Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #10208: `ModelAdmin` now respects the `exclude` and `field` atr…

…ibutes of custom `ModelForm`s. Thanks, Alex Gaynor.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10619 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 6c15b5db6014a7dadb0c237e689bdaed4761fb16 1 parent 71233bc
Jacob Kaplan-Moss authored April 22, 2009
8  django/contrib/admin/options.py
@@ -336,10 +336,12 @@ def get_form(self, request, obj=None, **kwargs):
336 336
             exclude = []
337 337
         else:
338 338
             exclude = list(self.exclude)
  339
+        # if exclude is an empty list we pass None to be consistant with the
  340
+        # default on modelform_factory
339 341
         defaults = {
340 342
             "form": self.form,
341 343
             "fields": fields,
342  
-            "exclude": exclude + kwargs.get("exclude", []),
  344
+            "exclude": (exclude + kwargs.get("exclude", [])) or None,
343 345
             "formfield_callback": curry(self.formfield_for_dbfield, request=request),
344 346
         }
345 347
         defaults.update(kwargs)
@@ -1138,12 +1140,14 @@ def get_formset(self, request, obj=None, **kwargs):
1138 1140
             exclude = []
1139 1141
         else:
1140 1142
             exclude = list(self.exclude)
  1143
+        # if exclude is an empty list we use None, since that's the actual
  1144
+        # default
1141 1145
         defaults = {
1142 1146
             "form": self.form,
1143 1147
             "formset": self.formset,
1144 1148
             "fk_name": self.fk_name,
1145 1149
             "fields": fields,
1146  
-            "exclude": exclude + kwargs.get("exclude", []),
  1150
+            "exclude": (exclude + kwargs.get("exclude", [])) or None,
1147 1151
             "formfield_callback": curry(self.formfield_for_dbfield, request=request),
1148 1152
             "extra": self.extra,
1149 1153
             "max_num": self.max_num,
43  django/forms/models.py
@@ -344,16 +344,34 @@ class ModelForm(BaseModelForm):
344 344
 
345 345
 def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
346 346
                        formfield_callback=lambda f: f.formfield()):
347  
-    # HACK: we should be able to construct a ModelForm without creating
348  
-    # and passing in a temporary inner class
349  
-    class Meta:
350  
-        pass
351  
-    setattr(Meta, 'model', model)
352  
-    setattr(Meta, 'fields', fields)
353  
-    setattr(Meta, 'exclude', exclude)
  347
+    # Create the inner Meta class. FIXME: ideally, we should be able to
  348
+    # construct a ModelForm without creating and passing in a temporary
  349
+    # inner class.
  350
+
  351
+    # Build up a list of attributes that the Meta object will have.
  352
+    attrs = {'model': model}
  353
+    if fields is not None:
  354
+        attrs['fields'] = fields
  355
+    if exclude is not None:
  356
+        attrs['exclude'] = exclude
  357
+
  358
+    # If parent form class already has an inner Meta, the Meta we're
  359
+    # creating needs to inherit from the parent's inner meta.
  360
+    parent = (object,)
  361
+    if hasattr(form, 'Meta'):
  362
+        parent = (form.Meta, object)
  363
+    Meta = type('Meta', parent, attrs)
  364
+
  365
+    # Give this new form class a reasonable name.
354 366
     class_name = model.__name__ + 'Form'
355  
-    return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
356  
-                              'formfield_callback': formfield_callback})
  367
+
  368
+    # Class attributes for the new form class.
  369
+    form_class_attrs = {
  370
+        'Meta': Meta,
  371
+        'formfield_callback': formfield_callback
  372
+    }
  373
+
  374
+    return ModelFormMetaclass(class_name, (form,), form_class_attrs)
357 375
 
358 376
 
359 377
 # ModelFormSets ##############################################################
@@ -617,13 +635,6 @@ def inlineformset_factory(parent_model, model, form=ModelForm,
617 635
     # enforce a max_num=1 when the foreign key to the parent model is unique.
618 636
     if fk.unique:
619 637
         max_num = 1
620  
-    if fields is not None:
621  
-        fields = list(fields)
622  
-        fields.append(fk.name)
623  
-    else:
624  
-        # get all the fields for this model that will be generated.
625  
-        fields = fields_for_model(model, fields, exclude, formfield_callback).keys()
626  
-        fields.append(fk.name)
627 638
     kwargs = {
628 639
         'form': form,
629 640
         'formfield_callback': formfield_callback,
42  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,46 @@ 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
+>>> ma.get_form(request).base_fields.keys()
  275
+['main_band', 'opening_band', 'day']
  276
+
  277
+>>> class AdminConcertForm(forms.ModelForm):
  278
+...     extra = forms.CharField()
  279
+...     class Meta:
  280
+...         model = Concert
  281
+...         fields = ['extra', 'transport']
  282
+
  283
+>>> class ConcertAdmin(ModelAdmin):
  284
+...     form = AdminConcertForm
  285
+
  286
+>>> ma = ConcertAdmin(Concert, site)
  287
+>>> ma.get_form(request).base_fields.keys()
  288
+['extra', 'transport']
  289
+
  290
+>>> class ConcertInline(TabularInline):
  291
+...     form = AdminConcertForm
  292
+...     model = Concert
  293
+...     fk_name = 'main_band'
  294
+
  295
+>>> class BandAdmin(ModelAdmin):
  296
+...     inlines = [
  297
+...         ConcertInline
  298
+...     ]
  299
+
  300
+>>> ma = BandAdmin(Band, site)
  301
+>>> list(ma.get_formsets(request))[0]().forms[0].fields.keys()
  302
+['extra', 'transport', 'id', 'DELETE', 'main_band']
  303
+
  304
+
265 305
 >>> band.delete()
266 306
 
267 307
 # ModelAdmin Option Validation ################################################

0 notes on commit 6c15b5d

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