Fixed #9498 -- Handle a formset correctly when the foreign key is not available (for now).
This case pops up with generic foreign key inlines after [9297]. Added tests to handle future regressions with generic foreign key inlines in the admin. Thanks markus and danielr for patches. git-svn-id: http://code.djangoproject.com/svn/django/trunk@9412 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
dfa90aec1b
commit
f751d5b713
|
@ -108,8 +108,9 @@ class InlineAdminFormSet(object):
|
||||||
yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None)
|
yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None)
|
||||||
|
|
||||||
def fields(self):
|
def fields(self):
|
||||||
|
fk = getattr(self.formset, "fk", None)
|
||||||
for field_name in flatten_fieldsets(self.fieldsets):
|
for field_name in flatten_fieldsets(self.fieldsets):
|
||||||
if self.formset.fk.name == field_name:
|
if fk and fk.name == field_name:
|
||||||
continue
|
continue
|
||||||
yield self.formset.form.base_fields[field_name]
|
yield self.formset.form.base_fields[field_name]
|
||||||
|
|
||||||
|
@ -150,7 +151,11 @@ class InlineAdminForm(AdminForm):
|
||||||
return AdminField(self.form, self.formset._pk_field.name, False)
|
return AdminField(self.form, self.formset._pk_field.name, False)
|
||||||
|
|
||||||
def fk_field(self):
|
def fk_field(self):
|
||||||
return AdminField(self.form, self.formset.fk.name, False)
|
fk = getattr(self.formset, "fk", None)
|
||||||
|
if fk:
|
||||||
|
return AdminField(self.form, fk.name, False)
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
def deletion_field(self):
|
def deletion_field(self):
|
||||||
from django.forms.formsets import DELETION_FIELD_NAME
|
from django.forms.formsets import DELETION_FIELD_NAME
|
||||||
|
@ -166,8 +171,9 @@ class InlineFieldset(Fieldset):
|
||||||
super(InlineFieldset, self).__init__(*args, **kwargs)
|
super(InlineFieldset, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
|
fk = getattr(self.formset, "fk", None)
|
||||||
for field in self.fields:
|
for field in self.fields:
|
||||||
if self.formset.fk.name == field:
|
if fk and fk.name == field:
|
||||||
continue
|
continue
|
||||||
yield Fieldline(self.form, field)
|
yield Fieldline(self.form, field)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<django-objects version="1.0">
|
||||||
|
<object pk="1" model="generic_inline_admin.Episode">
|
||||||
|
<field type="CharField" name="name">This Week in Django</field>
|
||||||
|
</object>
|
||||||
|
<object pk="1" model="generic_inline_admin.Media">
|
||||||
|
<field type="ForeignKey" name="content_type">13</field>
|
||||||
|
<field type="PositiveIntegerField" name="object_id">1</field>
|
||||||
|
<field type="URLField" name="url">http://example.com/podcast.mp3</field>
|
||||||
|
</object>
|
||||||
|
</django-objects>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<django-objects version="1.0">
|
||||||
|
<object pk="100" model="auth.user">
|
||||||
|
<field type="CharField" name="username">super</field>
|
||||||
|
<field type="CharField" name="first_name">Super</field>
|
||||||
|
<field type="CharField" name="last_name">User</field>
|
||||||
|
<field type="CharField" name="email">super@example.com</field>
|
||||||
|
<field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
|
||||||
|
<field type="BooleanField" name="is_staff">True</field>
|
||||||
|
<field type="BooleanField" name="is_active">True</field>
|
||||||
|
<field type="BooleanField" name="is_superuser">True</field>
|
||||||
|
<field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
|
||||||
|
<field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
|
||||||
|
<field to="auth.group" name="groups" rel="ManyToManyRel"></field>
|
||||||
|
<field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
|
||||||
|
</object>
|
||||||
|
</django-objects>
|
|
@ -0,0 +1,30 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.contenttypes import generic
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
class Episode(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
class Media(models.Model):
|
||||||
|
"""
|
||||||
|
Media that can associated to any object.
|
||||||
|
"""
|
||||||
|
content_type = models.ForeignKey(ContentType)
|
||||||
|
object_id = models.PositiveIntegerField()
|
||||||
|
content_object = generic.GenericForeignKey()
|
||||||
|
url = models.URLField(verify_exists=False)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.url
|
||||||
|
|
||||||
|
class MediaInline(generic.GenericTabularInline):
|
||||||
|
model = Media
|
||||||
|
extra = 1
|
||||||
|
|
||||||
|
class EpisodeAdmin(admin.ModelAdmin):
|
||||||
|
inlines = [
|
||||||
|
MediaInline,
|
||||||
|
]
|
||||||
|
|
||||||
|
admin.site.register(Episode, EpisodeAdmin)
|
|
@ -0,0 +1,66 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
# local test models
|
||||||
|
from models import Episode, Media
|
||||||
|
|
||||||
|
class GenericAdminViewTest(TestCase):
|
||||||
|
fixtures = ['users.xml', 'model-data.xml']
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# set TEMPLATE_DEBUG to True to ensure {% include %} will raise
|
||||||
|
# exceptions since that is how inlines are rendered and #9498 will
|
||||||
|
# bubble up if it is an issue.
|
||||||
|
self.original_template_debug = settings.TEMPLATE_DEBUG
|
||||||
|
settings.TEMPLATE_DEBUG = True
|
||||||
|
self.client.login(username='super', password='secret')
|
||||||
|
|
||||||
|
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/1/')
|
||||||
|
self.failUnlessEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def testBasicAddPost(self):
|
||||||
|
"""
|
||||||
|
A smoke test to ensure POST on add_view works.
|
||||||
|
"""
|
||||||
|
post_data = {
|
||||||
|
"name": u"This Week in Django",
|
||||||
|
# inline data
|
||||||
|
"generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"1",
|
||||||
|
"generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"0",
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
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"1",
|
||||||
|
"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"",
|
||||||
|
}
|
||||||
|
response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/episode/1/', post_data)
|
||||||
|
self.failUnlessEqual(response.status_code, 302) # redirect somewhere
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
(r'^admin/(.*)', admin.site.root),
|
||||||
|
)
|
|
@ -23,6 +23,7 @@ urlpatterns = patterns('',
|
||||||
|
|
||||||
# admin view tests
|
# admin view tests
|
||||||
(r'^test_admin/', include('regressiontests.admin_views.urls')),
|
(r'^test_admin/', include('regressiontests.admin_views.urls')),
|
||||||
|
(r'^generic_inline_admin/', include('regressiontests.generic_inline_admin.urls')),
|
||||||
|
|
||||||
(r'^utils/', include('regressiontests.utils.urls')),
|
(r'^utils/', include('regressiontests.utils.urls')),
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue