Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #15907 -- Fixed another conflict between the ModelForm exclude …

…and the GenericInline. Thanks, leonelfreire and prestontimmons.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16603 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 386b12c1c668dd54d24704ef974e17b38abb825d 1 parent 3d027b7
Jannis Leidel authored August 12, 2011
4  django/contrib/contenttypes/generic.py
@@ -406,6 +406,10 @@ def get_formset(self, request, obj=None, **kwargs):
406 406
         else:
407 407
             exclude = list(self.exclude)
408 408
         exclude.extend(self.get_readonly_fields(request, obj))
  409
+        if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
  410
+            # Take the custom ModelForm's Meta.exclude into account only if the
  411
+            # GenericInlineModelAdmin doesn't define its own.
  412
+            exclude.extend(self.form._meta.exclude)
409 413
         exclude = exclude or None
410 414
         defaults = {
411 415
             "ct_field": self.ct_field,
16  tests/regressiontests/generic_inline_admin/models.py
@@ -5,6 +5,8 @@
5 5
 
6 6
 class Episode(models.Model):
7 7
     name = models.CharField(max_length=100)
  8
+    length = models.CharField(max_length=100, blank=True)
  9
+    author = models.CharField(max_length=100, blank=True)
8 10
 
9 11
 class Media(models.Model):
10 12
     """
@@ -14,6 +16,8 @@ class Media(models.Model):
14 16
     object_id = models.PositiveIntegerField()
15 17
     content_object = generic.GenericForeignKey()
16 18
     url = models.URLField(verify_exists=False)
  19
+    description = models.CharField(max_length=100, blank=True)
  20
+    keywords = models.CharField(max_length=100, blank=True)
17 21
 
18 22
     def __unicode__(self):
19 23
         return self.url
@@ -59,18 +63,6 @@ class MediaMaxNumInline(generic.GenericTabularInline):
59 63
 
60 64
 admin.site.register(EpisodeMaxNum, inlines=[MediaMaxNumInline])
61 65
 
62  
-#
63  
-# Generic inline with exclude
64  
-#
65  
-
66  
-class EpisodeExclude(Episode):
67  
-    pass
68  
-
69  
-class MediaExcludeInline(generic.GenericTabularInline):
70  
-    model = Media
71  
-    exclude = ['url']
72  
-
73  
-admin.site.register(EpisodeExclude, inlines=[MediaExcludeInline])
74 66
 
75 67
 #
76 68
 # Generic inline with unique_together
102  tests/regressiontests/generic_inline_admin/tests.py
... ...
@@ -1,13 +1,16 @@
1 1
 # coding: utf-8
2 2
 
3 3
 from django.conf import settings
  4
+from django.contrib import admin
4 5
 from django.contrib.admin.sites import AdminSite
5  
-from django.contrib.contenttypes.generic import generic_inlineformset_factory
  6
+from django.contrib.contenttypes.generic import (
  7
+    generic_inlineformset_factory, GenericTabularInline)
  8
+from django.forms.models import ModelForm
6 9
 from django.test import TestCase
7 10
 
8 11
 # local test models
9  
-from models import (Episode, EpisodeExtra, EpisodeMaxNum, EpisodeExclude,
10  
-    Media, MediaInline, EpisodePermanent, MediaPermanentInline, Category)
  12
+from models import (Episode, EpisodeExtra, EpisodeMaxNum, Media,
  13
+    MediaInline, EpisodePermanent, MediaPermanentInline, Category)
11 14
 
12 15
 
13 16
 class GenericAdminViewTest(TestCase):
@@ -88,7 +91,7 @@ def testBasicEditPost(self):
88 91
         self.assertEqual(response.status_code, 302) # redirect somewhere
89 92
 
90 93
     def testGenericInlineFormset(self):
91  
-        EpisodeMediaFormSet = generic_inlineformset_factory(Media, can_delete=False, extra=3)
  94
+        EpisodeMediaFormSet = generic_inlineformset_factory(Media, can_delete=False, exclude=['description', 'keywords'], extra=3)
92 95
         e = Episode.objects.get(name='This Week in Django')
93 96
 
94 97
         # Works with no queryset
@@ -105,7 +108,6 @@ def testGenericInlineFormset(self):
105 108
         self.assertEqual(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="text" name="generic_inline_admin-media-content_type-object_id-1-url" value="http://example.com/podcast.mp3" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>' % self.mp3_media_pk)
106 109
         self.assertEqual(formset.forms[2].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-2-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-2-url" type="text" name="generic_inline_admin-media-content_type-object_id-2-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-2-id" id="id_generic_inline_admin-media-content_type-object_id-2-id" /></p>')
107 110
 
108  
-
109 111
         # Works with a queryset that omits items
110 112
         formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.filter(url__endswith=".png"))
111 113
         self.assertEqual(len(formset.forms), 4)
@@ -173,14 +175,6 @@ def testMaxNumParam(self):
173 175
         self.assertEqual(formset.total_form_count(), 2)
174 176
         self.assertEqual(formset.initial_form_count(), 1)
175 177
 
176  
-    def testExcludeParam(self):
177  
-        """
178  
-        Generic inline formsets should respect include.
179  
-        """
180  
-        e = self._create_object(EpisodeExclude)
181  
-        response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episodeexclude/%s/' % e.pk)
182  
-        formset = response.context['inline_admin_formsets'][0].formset
183  
-        self.assertFalse('url' in formset.forms[0], 'The formset has excluded "url" field.')
184 178
 
185 179
 class GenericInlineAdminWithUniqueTogetherTest(TestCase):
186 180
     fixtures = ['users.xml']
@@ -218,6 +212,9 @@ def test_no_deletion(self):
218 212
 
219 213
 class GenericInlineModelAdminTest(TestCase):
220 214
 
  215
+    def setUp(self):
  216
+        self.site = AdminSite()
  217
+
221 218
     def test_get_formset_kwargs(self):
222 219
         media_inline = MediaInline(Media, AdminSite())
223 220
 
@@ -230,3 +227,82 @@ def test_get_formset_kwargs(self):
230 227
         formset = media_inline.get_formset(None, max_num=100, can_order=True)
231 228
         self.assertEqual(formset.max_num, 100)
232 229
         self.assertEqual(formset.can_order, True)
  230
+
  231
+    def test_custom_form_meta_exclude_with_readonly(self):
  232
+        """
  233
+        Ensure that the custom ModelForm's `Meta.exclude` is respected when
  234
+        used in conjunction with `GenericInlineModelAdmin.readonly_fields`
  235
+        and when no `ModelAdmin.exclude` is defined.
  236
+        """
  237
+
  238
+        request = None
  239
+
  240
+        class MediaForm(ModelForm):
  241
+
  242
+            class Meta:
  243
+                model = Media
  244
+                exclude = ['url']
  245
+
  246
+        class MediaInline(GenericTabularInline):
  247
+            readonly_fields = ['description']
  248
+            form = MediaForm
  249
+            model = Media
  250
+
  251
+        class EpisodeAdmin(admin.ModelAdmin):
  252
+            inlines = [
  253
+                MediaInline
  254
+            ]
  255
+
  256
+        ma = EpisodeAdmin(Episode, self.site)
  257
+        self.assertEqual(
  258
+            list(ma.get_formsets(request))[0]().forms[0].fields.keys(),
  259
+            ['keywords', 'id', 'DELETE'])
  260
+
  261
+    def test_custom_form_meta_exclude(self):
  262
+        """
  263
+        Ensure that the custom ModelForm's `Meta.exclude` is respected by
  264
+        `GenericInlineModelAdmin.get_formset`, and overridden if
  265
+        `ModelAdmin.exclude` or `GenericInlineModelAdmin.exclude` are defined.
  266
+        Refs #15907.
  267
+        """
  268
+
  269
+        request = None
  270
+
  271
+        # First with `GenericInlineModelAdmin`  -----------------
  272
+
  273
+        class MediaForm(ModelForm):
  274
+
  275
+            class Meta:
  276
+                model = Media
  277
+                exclude = ['url']
  278
+
  279
+        class MediaInline(GenericTabularInline):
  280
+            exclude = ['description']
  281
+            form = MediaForm
  282
+            model = Media
  283
+
  284
+        class EpisodeAdmin(admin.ModelAdmin):
  285
+            inlines = [
  286
+                MediaInline
  287
+            ]
  288
+
  289
+        ma = EpisodeAdmin(Episode, self.site)
  290
+        self.assertEqual(
  291
+            list(ma.get_formsets(request))[0]().forms[0].fields.keys(),
  292
+            ['url', 'keywords', 'id', 'DELETE'])
  293
+
  294
+        # Then, only with `ModelForm`  -----------------
  295
+
  296
+        class MediaInline(GenericTabularInline):
  297
+            form = MediaForm
  298
+            model = Media
  299
+
  300
+        class EpisodeAdmin(admin.ModelAdmin):
  301
+            inlines = [
  302
+                MediaInline
  303
+            ]
  304
+
  305
+        ma = EpisodeAdmin(Episode, self.site)
  306
+        self.assertEqual(
  307
+            list(ma.get_formsets(request))[0]().forms[0].fields.keys(),
  308
+            ['description', 'keywords', 'id', 'DELETE'])
2  tests/regressiontests/generic_views/edit.py
@@ -10,7 +10,7 @@
10 10
 class ModelFormMixinTests(TestCase):
11 11
     def test_get_form(self):
12 12
         form_class = views.AuthorGetQuerySetFormView().get_form_class()
13  
-        self.assertEqual(form_class.Meta.model, Author)
  13
+        self.assertEqual(form_class._meta.model, Author)
14 14
 
15 15
 class CreateViewTests(TestCase):
16 16
     urls = 'regressiontests.generic_views.urls'

0 notes on commit 386b12c

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