Commit 2c2871b7 authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #11042 -- Corrected admin inlines for inherited models. Thanks to...

Fixed #11042 -- Corrected admin inlines for inherited models. Thanks to jsmullyan for the report, and mir for helpful triage work. Patch includes regression test for #8093, and a commented out test for #10992.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10725 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent c40f7152
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -146,7 +146,13 @@ class InlineAdminForm(AdminForm):
            yield InlineFieldset(self.formset, self.form, name, **options)

    def has_auto_field(self):
        return self.form._meta.model._meta.has_auto_field
        if self.form._meta.model._meta.has_auto_field:
            return True
        # Also search any parents for an auto field.
        for parent in self.form._meta.model._meta.get_parent_list():
            if parent._meta.has_auto_field:
                return True
        return False

    def field_count(self):
        # tabular.html uses this function for colspan value.
+47 −0
Original line number Diff line number Diff line
@@ -357,6 +357,52 @@ class Recommendation(Title):
class RecommendationAdmin(admin.ModelAdmin):
    search_fields = ('titletranslation__text', 'recommender__titletranslation__text',)

class Collector(models.Model):
    name = models.CharField(max_length=100)

class Widget(models.Model):
    owner = models.ForeignKey(Collector)
    name = models.CharField(max_length=100)

class DooHickey(models.Model):
    code = models.CharField(max_length=10, primary_key=True)
    owner = models.ForeignKey(Collector)
    name = models.CharField(max_length=100)

class Grommet(models.Model):
    code = models.AutoField(primary_key=True)
    owner = models.ForeignKey(Collector)
    name = models.CharField(max_length=100)

class Whatsit(models.Model):
    index = models.IntegerField(primary_key=True)
    owner = models.ForeignKey(Collector)
    name = models.CharField(max_length=100)

class Doodad(models.Model):
    name = models.CharField(max_length=100)

class FancyDoodad(Doodad):
    owner = models.ForeignKey(Collector)
    expensive = models.BooleanField(default=True)

class WidgetInline(admin.StackedInline):
    model = Widget

class DooHickeyInline(admin.StackedInline):
    model = DooHickey

class GrommetInline(admin.StackedInline):
    model = Grommet

class WhatsitInline(admin.StackedInline):
    model = Whatsit

class FancyDoodadInline(admin.StackedInline):
    model = FancyDoodad

class CollectorAdmin(admin.ModelAdmin):
    inlines = [WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline, FancyDoodadInline]

admin.site.register(Article, ArticleAdmin)
admin.site.register(CustomArticle, CustomArticleAdmin)
@@ -379,6 +425,7 @@ admin.site.register(Picture, PictureAdmin)
admin.site.register(Language, LanguageAdmin)
admin.site.register(Recommendation, RecommendationAdmin)
admin.site.register(Recommender)
admin.site.register(Collector, CollectorAdmin)

# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
# That way we cover all four cases:
+241 −4
Original line number Diff line number Diff line
@@ -16,7 +16,8 @@ from django.utils.html import escape
from models import (Article, BarAccount, CustomArticle, EmptyModel,
                    ExternalSubscriber, FooAccount, Gallery,
                    ModelWithStringPrimaryKey, Person, Persona, Picture,
                    Podcast, Section, Subscriber, Vodcast, Language)
                    Podcast, Section, Subscriber, Vodcast, Language,
                    Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit)

try:
    set
@@ -1183,3 +1184,239 @@ class AdminInlineFileUploadTest(TestCase):
        }
        response = self.client.post('/test_admin/%s/admin_views/gallery/1/' % self.urlbit, post_data)
        self.failUnless(response._container[0].find("Currently:") > -1)


class AdminInlineTests(TestCase):
    fixtures = ['admin-views-users.xml']

    def setUp(self):
        self.post_data = {
            "name": u"Test Name",

            "widget_set-TOTAL_FORMS": "3",
            "widget_set-INITIAL_FORMS": u"0",
            "widget_set-0-id": "",
            "widget_set-0-owner": "1",
            "widget_set-0-name": "",
            "widget_set-1-id": "",
            "widget_set-1-owner": "1",
            "widget_set-1-name": "",
            "widget_set-2-id": "",
            "widget_set-2-owner": "1",
            "widget_set-2-name": "",

            "doohickey_set-TOTAL_FORMS": "3",
            "doohickey_set-INITIAL_FORMS": u"0",
            "doohickey_set-0-owner": "1",
            "doohickey_set-0-code": "",
            "doohickey_set-0-name": "",
            "doohickey_set-1-owner": "1",
            "doohickey_set-1-code": "",
            "doohickey_set-1-name": "",
            "doohickey_set-2-owner": "1",
            "doohickey_set-2-code": "",
            "doohickey_set-2-name": "",

            "grommet_set-TOTAL_FORMS": "3",
            "grommet_set-INITIAL_FORMS": u"0",
            "grommet_set-0-code": "",
            "grommet_set-0-owner": "1",
            "grommet_set-0-name": "",
            "grommet_set-1-code": "",
            "grommet_set-1-owner": "1",
            "grommet_set-1-name": "",
            "grommet_set-2-code": "",
            "grommet_set-2-owner": "1",
            "grommet_set-2-name": "",

            "whatsit_set-TOTAL_FORMS": "3",
            "whatsit_set-INITIAL_FORMS": u"0",
            "whatsit_set-0-owner": "1",
            "whatsit_set-0-index": "",
            "whatsit_set-0-name": "",
            "whatsit_set-1-owner": "1",
            "whatsit_set-1-index": "",
            "whatsit_set-1-name": "",
            "whatsit_set-2-owner": "1",
            "whatsit_set-2-index": "",
            "whatsit_set-2-name": "",

            "fancydoodad_set-TOTAL_FORMS": "3",
            "fancydoodad_set-INITIAL_FORMS": u"0",
            "fancydoodad_set-0-doodad_ptr": "",
            "fancydoodad_set-0-owner": "1",
            "fancydoodad_set-0-name": "",
            "fancydoodad_set-0-expensive": "on",
            "fancydoodad_set-1-doodad_ptr": "",
            "fancydoodad_set-1-owner": "1",
            "fancydoodad_set-1-name": "",
            "fancydoodad_set-1-expensive": "on",
            "fancydoodad_set-2-doodad_ptr": "",
            "fancydoodad_set-2-owner": "1",
            "fancydoodad_set-2-name": "",
            "fancydoodad_set-2-expensive": "on",
        }

        result = self.client.login(username='super', password='secret')
        self.failUnlessEqual(result, True)
        Collector(pk=1,name='John Fowles').save()

    def tearDown(self):
        self.client.logout()

    def test_simple_inline(self):
        "A simple model can be saved as inlines"
        # First add a new inline
        self.post_data['widget_set-0-name'] = "Widget 1"
        response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
        self.failUnlessEqual(response.status_code, 302)
        self.failUnlessEqual(Widget.objects.count(), 1)
        self.failUnlessEqual(Widget.objects.all()[0].name, "Widget 1")

        # Check that the PK link exists on the rendered form
        response = self.client.get('/test_admin/admin/admin_views/collector/1/')
        self.assertContains(response, 'name="widget_set-0-id"')

        # Now resave that inline
        self.post_data['widget_set-INITIAL_FORMS'] = "1"
        self.post_data['widget_set-0-id'] = "1"
        self.post_data['widget_set-0-name'] = "Widget 1"
        response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
        self.failUnlessEqual(response.status_code, 302)
        self.failUnlessEqual(Widget.objects.count(), 1)
        self.failUnlessEqual(Widget.objects.all()[0].name, "Widget 1")

        # Now modify that inline
        self.post_data['widget_set-INITIAL_FORMS'] = "1"
        self.post_data['widget_set-0-id'] = "1"
        self.post_data['widget_set-0-name'] = "Widget 1 Updated"
        response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
        self.failUnlessEqual(response.status_code, 302)
        self.failUnlessEqual(Widget.objects.count(), 1)
        self.failUnlessEqual(Widget.objects.all()[0].name, "Widget 1 Updated")

    def test_explicit_autofield_inline(self):
        "A model with an explicit autofield primary key can be saved as inlines. Regression for #8093"
        # First add a new inline
        self.post_data['grommet_set-0-name'] = "Grommet 1"
        response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
        self.failUnlessEqual(response.status_code, 302)
        self.failUnlessEqual(Grommet.objects.count(), 1)
        self.failUnlessEqual(Grommet.objects.all()[0].name, "Grommet 1")

        # Check that the PK link exists on the rendered form
        response = self.client.get('/test_admin/admin/admin_views/collector/1/')
        self.assertContains(response, 'name="grommet_set-0-code"')

        # Now resave that inline
        self.post_data['grommet_set-INITIAL_FORMS'] = "1"
        self.post_data['grommet_set-0-code'] = "1"
        self.post_data['grommet_set-0-name'] = "Grommet 1"
        response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
        self.failUnlessEqual(response.status_code, 302)
        self.failUnlessEqual(Grommet.objects.count(), 1)
        self.failUnlessEqual(Grommet.objects.all()[0].name, "Grommet 1")

        # Now modify that inline
        self.post_data['grommet_set-INITIAL_FORMS'] = "1"
        self.post_data['grommet_set-0-code'] = "1"
        self.post_data['grommet_set-0-name'] = "Grommet 1 Updated"
        response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
        self.failUnlessEqual(response.status_code, 302)
        self.failUnlessEqual(Grommet.objects.count(), 1)
        self.failUnlessEqual(Grommet.objects.all()[0].name, "Grommet 1 Updated")

    # def test_char_pk_inline(self):
    #     "A model with a character PK can be saved as inlines. Regression for #10992"
    #     # First add a new inline
    #     self.post_data['doohickey_set-0-code'] = "DH1"
    #     self.post_data['doohickey_set-0-name'] = "Doohickey 1"
    #     response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
    #     self.failUnlessEqual(response.status_code, 302)
    #     self.failUnlessEqual(DooHickey.objects.count(), 1)
    #     self.failUnlessEqual(DooHickey.objects.all()[0].name, "Doohickey 1")
    #
    #     # Check that the PK link exists on the rendered form
    #     response = self.client.get('/test_admin/admin/admin_views/collector/1/')
    #     self.assertContains(response, 'name="doohickey_set-0-code"')
    #
    #     # Now resave that inline
    #     self.post_data['doohickey_set-INITIAL_FORMS'] = "1"
    #     self.post_data['doohickey_set-0-code'] = "DH1"
    #     self.post_data['doohickey_set-0-name'] = "Doohickey 1"
    #     response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
    #     self.failUnlessEqual(response.status_code, 302)
    #     self.failUnlessEqual(DooHickey.objects.count(), 1)
    #     self.failUnlessEqual(DooHickey.objects.all()[0].name, "Doohickey 1")
    #
    #     # Now modify that inline
    #     self.post_data['doohickey_set-INITIAL_FORMS'] = "1"
    #     self.post_data['doohickey_set-0-code'] = "DH1"
    #     self.post_data['doohickey_set-0-name'] = "Doohickey 1 Updated"
    #     response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
    #     self.failUnlessEqual(response.status_code, 302)
    #     self.failUnlessEqual(DooHickey.objects.count(), 1)
    #     self.failUnlessEqual(DooHickey.objects.all()[0].name, "Doohickey 1 Updated")

    # def test_integer_pk_inline(self):
    #     "A model with an integer PK can be saved as inlines. Regression for #10992"
    #     # First add a new inline
    #     self.post_data['whatsit_set-0-index'] = "42"
    #     self.post_data['whatsit_set-0-name'] = "Whatsit 1"
    #     response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
    #     self.failUnlessEqual(response.status_code, 302)
    #     self.failUnlessEqual(Whatsit.objects.count(), 1)
    #     self.failUnlessEqual(Whatsit.objects.all()[0].name, "Whatsit 1")
    #
    #     # Check that the PK link exists on the rendered form
    #     response = self.client.get('/test_admin/admin/admin_views/collector/1/')
    #     self.assertContains(response, 'name="whatsit_set-0-index"')
    #
    #     # Now resave that inline
    #     self.post_data['whatsit_set-INITIAL_FORMS'] = "1"
    #     self.post_data['whatsit_set-0-index'] = "42"
    #     self.post_data['whatsit_set-0-name'] = "Whatsit 1"
    #     response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
    #     self.failUnlessEqual(response.status_code, 302)
    #     self.failUnlessEqual(Whatsit.objects.count(), 1)
    #     self.failUnlessEqual(Whatsit.objects.all()[0].name, "Whatsit 1")
    #
    #     # Now modify that inline
    #     self.post_data['whatsit_set-INITIAL_FORMS'] = "1"
    #     self.post_data['whatsit_set-0-index'] = "42"
    #     self.post_data['whatsit_set-0-name'] = "Whatsit 1 Updated"
    #     response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
    #     self.failUnlessEqual(response.status_code, 302)
    #     self.failUnlessEqual(Whatsit.objects.count(), 1)
    #     self.failUnlessEqual(Whatsit.objects.all()[0].name, "Whatsit 1 Updated")

    def test_inherited_inline(self):
        "An inherited model can be saved as inlines. Regression for #11042"
        # First add a new inline
        self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1"
        response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
        self.failUnlessEqual(response.status_code, 302)
        self.failUnlessEqual(FancyDoodad.objects.count(), 1)
        self.failUnlessEqual(FancyDoodad.objects.all()[0].name, "Fancy Doodad 1")

        # Check that the PK link exists on the rendered form
        response = self.client.get('/test_admin/admin/admin_views/collector/1/')
        self.assertContains(response, 'name="fancydoodad_set-0-doodad_ptr"')

        # Now resave that inline
        self.post_data['fancydoodad_set-INITIAL_FORMS'] = "1"
        self.post_data['fancydoodad_set-0-doodad_ptr'] = "1"
        self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1"
        response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
        self.failUnlessEqual(response.status_code, 302)
        self.failUnlessEqual(FancyDoodad.objects.count(), 1)
        self.failUnlessEqual(FancyDoodad.objects.all()[0].name, "Fancy Doodad 1")

        # Now modify that inline
        self.post_data['fancydoodad_set-INITIAL_FORMS'] = "1"
        self.post_data['fancydoodad_set-0-doodad_ptr'] = "1"
        self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1 Updated"
        response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
        self.failUnlessEqual(response.status_code, 302)
        self.failUnlessEqual(FancyDoodad.objects.count(), 1)
        self.failUnlessEqual(FancyDoodad.objects.all()[0].name, "Fancy Doodad 1 Updated")