diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index b9b487bcf2d..38bde774287 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -292,15 +292,27 @@ class BaseGenericInlineFormSet(BaseModelFormSet): ct_field_name = "content_type" ct_fk_field_name = "object_id" - def __init__(self, data=None, files=None, instance=None, save_as_new=None, prefix=None): + def __init__(self, data=None, files=None, instance=None, save_as_new=None, + prefix=None, queryset=None): + # Avoid a circular import. + from django.contrib.contenttypes.models import ContentType opts = self.model._meta self.instance = instance self.rel_name = '-'.join(( opts.app_label, opts.object_name.lower(), self.ct_field.name, self.ct_fk_field.name, )) + if self.instance is None or self.instance.pk is None: + qs = self.model._default_manager.none() + else: + if queryset is None: + queryset = self.model._default_manager + qs = queryset.filter(**{ + self.ct_field.name: ContentType.objects.get_for_model(self.instance), + self.ct_fk_field.name: self.instance.pk, + }) super(BaseGenericInlineFormSet, self).__init__( - queryset=self.get_queryset(), data=data, files=files, + queryset=qs, data=data, files=files, prefix=prefix ) @@ -312,19 +324,6 @@ class BaseGenericInlineFormSet(BaseModelFormSet): )) get_default_prefix = classmethod(get_default_prefix) - def get_queryset(self): - # Avoid a circular import. - from django.contrib.contenttypes.models import ContentType - if self.instance is None or self.instance.pk is None: - return self.model._default_manager.none() - qs = self.model._default_manager.filter(**{ - self.ct_field.name: ContentType.objects.get_for_model(self.instance), - self.ct_fk_field.name: self.instance.pk, - }) - if not qs.ordered: - qs = qs.order_by(self.model._meta.pk.name) - return qs - def save_new(self, form, commit=True): # Avoid a circular import. from django.contrib.contenttypes.models import ContentType diff --git a/tests/regressiontests/generic_inline_admin/models.py b/tests/regressiontests/generic_inline_admin/models.py index bc6764d8b2d..9f1bbadd3eb 100644 --- a/tests/regressiontests/generic_inline_admin/models.py +++ b/tests/regressiontests/generic_inline_admin/models.py @@ -20,7 +20,7 @@ class Media(models.Model): class MediaInline(generic.GenericTabularInline): model = Media - + class EpisodeAdmin(admin.ModelAdmin): inlines = [ MediaInline, @@ -56,7 +56,7 @@ class MediaMaxNumInline(generic.GenericTabularInline): model = Media extra = 5 max_num = 2 - + admin.site.register(EpisodeMaxNum, inlines=[MediaMaxNumInline]) # diff --git a/tests/regressiontests/generic_inline_admin/tests.py b/tests/regressiontests/generic_inline_admin/tests.py index d28cd16e1d4..83542d15285 100644 --- a/tests/regressiontests/generic_inline_admin/tests.py +++ b/tests/regressiontests/generic_inline_admin/tests.py @@ -17,7 +17,7 @@ class GenericAdminViewTest(TestCase): self.original_template_debug = settings.TEMPLATE_DEBUG settings.TEMPLATE_DEBUG = True self.client.login(username='super', password='secret') - + # Can't load content via a fixture (since the GenericForeignKey # relies on content type IDs, which will vary depending on what # other tests have been run), thus we do it here. @@ -25,26 +25,30 @@ class GenericAdminViewTest(TestCase): self.episode_pk = e.pk m = Media(content_object=e, url='http://example.com/podcast.mp3') m.save() - self.media_pk = m.pk - + self.mp3_media_pk = m.pk + + m = Media(content_object=e, url='http://example.com/logo.png') + m.save() + self.png_media_pk = m.pk + def tearDown(self): self.client.logout() settings.TEMPLATE_DEBUG = self.original_template_debug - + def testBasicAddGet(self): """ A smoke test to ensure GET on the add_view works. """ response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/add/') self.failUnlessEqual(response.status_code, 200) - + def testBasicEditGet(self): """ A smoke test to ensure GET on the change_view works. """ response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk) self.failUnlessEqual(response.status_code, 200) - + def testBasicAddPost(self): """ A smoke test to ensure POST on add_view works. @@ -57,7 +61,7 @@ class GenericAdminViewTest(TestCase): } response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/episode/add/', post_data) self.failUnlessEqual(response.status_code, 302) # redirect somewhere - + def testBasicEditPost(self): """ A smoke test to ensure POST on edit_view works. @@ -65,17 +69,45 @@ class GenericAdminViewTest(TestCase): post_data = { "name": u"This Week in Django", # inline data - "generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"2", - "generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"1", - "generic_inline_admin-media-content_type-object_id-0-id": u"%d" % self.media_pk, + "generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"3", + "generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"2", + "generic_inline_admin-media-content_type-object_id-0-id": u"%d" % self.mp3_media_pk, "generic_inline_admin-media-content_type-object_id-0-url": u"http://example.com/podcast.mp3", - "generic_inline_admin-media-content_type-object_id-1-id": u"", - "generic_inline_admin-media-content_type-object_id-1-url": u"", + "generic_inline_admin-media-content_type-object_id-1-id": u"%d" % self.png_media_pk, + "generic_inline_admin-media-content_type-object_id-1-url": u"http://example.com/logo.png", + "generic_inline_admin-media-content_type-object_id-2-id": u"", + "generic_inline_admin-media-content_type-object_id-2-url": u"", } url = '/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk response = self.client.post(url, post_data) self.failUnlessEqual(response.status_code, 302) # redirect somewhere - + + def testGenericInlineFormset(self): + EpisodeMediaFormSet = generic_inlineformset_factory(Media, can_delete=False, extra=3) + e = Episode.objects.get(name='This Week in Django') + + # Works with no queryset + formset = EpisodeMediaFormSet(instance=e) + self.assertEquals(len(formset.forms), 5) + self.assertEquals(formset.forms[0].as_p(), '
') + self.assertEquals(formset.forms[1].as_p(), '
') + self.assertEquals(formset.forms[2].as_p(), '
') + + # A queryset can be used to alter display ordering + formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.order_by('url')) + self.assertEquals(len(formset.forms), 5) + self.assertEquals(formset.forms[0].as_p(), '
') + self.assertEquals(formset.forms[1].as_p(), '
') + self.assertEquals(formset.forms[2].as_p(), '
') + + + # Works with a queryset that omits items + formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.filter(url__endswith=".png")) + self.assertEquals(len(formset.forms), 4) + self.assertEquals(formset.forms[0].as_p(), '
') + self.assertEquals(formset.forms[1].as_p(), '
') + + def testGenericInlineFormsetFactory(self): # Regression test for #10522. inline_formset = generic_inlineformset_factory(Media,