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
This commit is contained in:
Russell Keith-Magee 2009-05-09 11:40:13 +00:00
parent c40f715257
commit 2c2871b7c3
3 changed files with 295 additions and 5 deletions

View File

@ -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.

View File

@ -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:

View File

@ -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
@ -236,20 +237,20 @@ class AdminViewBasicTest(TestCase):
class SaveAsTests(TestCase):
fixtures = ['admin-views-users.xml','admin-views-person.xml']
def setUp(self):
self.client.login(username='super', password='secret')
def tearDown(self):
self.client.logout()
def test_save_as_duplication(self):
"""Ensure save as actually creates a new person"""
post_data = {'_saveasnew':'', 'name':'John M', 'gender':1}
response = self.client.post('/test_admin/admin/admin_views/person/1/', post_data)
self.assertEqual(len(Person.objects.filter(name='John M')), 1)
self.assertEqual(len(Person.objects.filter(id=1)), 1)
def test_save_as_display(self):
"""
Ensure that 'save as' is displayed when activated and after submitting
@ -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")