Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.0.X] Fixed #9498 -- Handle a formset correctly when the foreign ke…

…y 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.

Backport of [9412] from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@9413 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit f219136e11f56a5765b58af476c93e806b2f3ce0 1 parent c8dcbb0
Brian Rosner authored November 13, 2008
12  django/contrib/admin/helpers.py
@@ -108,8 +108,9 @@ def __iter__(self):
108 108
             yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None)
109 109
 
110 110
     def fields(self):
  111
+        fk = getattr(self.formset, "fk", None)
111 112
         for field_name in flatten_fieldsets(self.fieldsets):
112  
-            if self.formset.fk.name == field_name:
  113
+            if fk and fk.name == field_name:
113 114
                 continue
114 115
             yield self.formset.form.base_fields[field_name]
115 116
 
@@ -150,7 +151,11 @@ def pk_field(self):
150 151
         return AdminField(self.form, self.formset._pk_field.name, False)
151 152
     
152 153
     def fk_field(self):
153  
-        return AdminField(self.form, self.formset.fk.name, False)
  154
+        fk = getattr(self.formset, "fk", None)
  155
+        if fk:
  156
+            return AdminField(self.form, fk.name, False)
  157
+        else:
  158
+            return ""
154 159
 
155 160
     def deletion_field(self):
156 161
         from django.forms.formsets import DELETION_FIELD_NAME
@@ -166,8 +171,9 @@ def __init__(self, formset, *args, **kwargs):
166 171
         super(InlineFieldset, self).__init__(*args, **kwargs)
167 172
         
168 173
     def __iter__(self):
  174
+        fk = getattr(self.formset, "fk", None)
169 175
         for field in self.fields:
170  
-            if self.formset.fk.name == field:
  176
+            if fk and fk.name == field:
171 177
                 continue
172 178
             yield Fieldline(self.form, field)
173 179
             
0  tests/regressiontests/generic_inline_admin/__init__.py
No changes.
11  tests/regressiontests/generic_inline_admin/fixtures/model-data.xml
... ...
@@ -0,0 +1,11 @@
  1
+<?xml version="1.0" encoding="utf-8"?>
  2
+<django-objects version="1.0">
  3
+    <object pk="1" model="generic_inline_admin.Episode">
  4
+        <field type="CharField" name="name">This Week in Django</field>
  5
+    </object>
  6
+    <object pk="1" model="generic_inline_admin.Media">
  7
+        <field type="ForeignKey" name="content_type">13</field>
  8
+        <field type="PositiveIntegerField" name="object_id">1</field>
  9
+        <field type="URLField" name="url">http://example.com/podcast.mp3</field>
  10
+    </object>
  11
+</django-objects>
17  tests/regressiontests/generic_inline_admin/fixtures/users.xml
... ...
@@ -0,0 +1,17 @@
  1
+<?xml version="1.0" encoding="utf-8"?>
  2
+<django-objects version="1.0">
  3
+    <object pk="100" model="auth.user">
  4
+        <field type="CharField" name="username">super</field>
  5
+        <field type="CharField" name="first_name">Super</field>
  6
+        <field type="CharField" name="last_name">User</field>
  7
+        <field type="CharField" name="email">super@example.com</field>
  8
+        <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
  9
+        <field type="BooleanField" name="is_staff">True</field>
  10
+        <field type="BooleanField" name="is_active">True</field>
  11
+        <field type="BooleanField" name="is_superuser">True</field>
  12
+        <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
  13
+        <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
  14
+        <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
  15
+        <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
  16
+    </object>
  17
+</django-objects>
30  tests/regressiontests/generic_inline_admin/models.py
... ...
@@ -0,0 +1,30 @@
  1
+from django.db import models
  2
+from django.contrib import admin
  3
+from django.contrib.contenttypes import generic
  4
+from django.contrib.contenttypes.models import ContentType
  5
+
  6
+class Episode(models.Model):
  7
+    name = models.CharField(max_length=100)
  8
+
  9
+class Media(models.Model):
  10
+    """
  11
+    Media that can associated to any object.
  12
+    """
  13
+    content_type = models.ForeignKey(ContentType)
  14
+    object_id = models.PositiveIntegerField()
  15
+    content_object = generic.GenericForeignKey()
  16
+    url = models.URLField(verify_exists=False)
  17
+
  18
+    def __unicode__(self):
  19
+        return self.url
  20
+
  21
+class MediaInline(generic.GenericTabularInline):
  22
+    model = Media
  23
+    extra = 1
  24
+    
  25
+class EpisodeAdmin(admin.ModelAdmin):
  26
+    inlines = [
  27
+        MediaInline,
  28
+    ]
  29
+
  30
+admin.site.register(Episode, EpisodeAdmin)
66  tests/regressiontests/generic_inline_admin/tests.py
... ...
@@ -0,0 +1,66 @@
  1
+# coding: utf-8
  2
+
  3
+from django.test import TestCase
  4
+from django.conf import settings
  5
+
  6
+# local test models
  7
+from models import Episode, Media
  8
+
  9
+class GenericAdminViewTest(TestCase):
  10
+    fixtures = ['users.xml', 'model-data.xml']
  11
+
  12
+    def setUp(self):
  13
+        # set TEMPLATE_DEBUG to True to ensure {% include %} will raise
  14
+        # exceptions since that is how inlines are rendered and #9498 will
  15
+        # bubble up if it is an issue.
  16
+        self.original_template_debug = settings.TEMPLATE_DEBUG
  17
+        settings.TEMPLATE_DEBUG = True
  18
+        self.client.login(username='super', password='secret')
  19
+    
  20
+    def tearDown(self):
  21
+        self.client.logout()
  22
+        settings.TEMPLATE_DEBUG = self.original_template_debug
  23
+    
  24
+    def testBasicAddGet(self):
  25
+        """
  26
+        A smoke test to ensure GET on the add_view works.
  27
+        """
  28
+        response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/add/')
  29
+        self.failUnlessEqual(response.status_code, 200)
  30
+    
  31
+    def testBasicEditGet(self):
  32
+        """
  33
+        A smoke test to ensure GET on the change_view works.
  34
+        """
  35
+        response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/1/')
  36
+        self.failUnlessEqual(response.status_code, 200)
  37
+    
  38
+    def testBasicAddPost(self):
  39
+        """
  40
+        A smoke test to ensure POST on add_view works.
  41
+        """
  42
+        post_data = {
  43
+            "name": u"This Week in Django",
  44
+            # inline data
  45
+            "generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"1",
  46
+            "generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"0",
  47
+        }
  48
+        response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/episode/add/', post_data)
  49
+        self.failUnlessEqual(response.status_code, 302) # redirect somewhere
  50
+    
  51
+    def testBasicEditPost(self):
  52
+        """
  53
+        A smoke test to ensure POST on edit_view works.
  54
+        """
  55
+        post_data = {
  56
+            "name": u"This Week in Django",
  57
+            # inline data
  58
+            "generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"2",
  59
+            "generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"1",
  60
+            "generic_inline_admin-media-content_type-object_id-0-id": u"1",
  61
+            "generic_inline_admin-media-content_type-object_id-0-url": u"http://example.com/podcast.mp3",
  62
+            "generic_inline_admin-media-content_type-object_id-1-id": u"",
  63
+            "generic_inline_admin-media-content_type-object_id-1-url": u"",
  64
+        }
  65
+        response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/episode/1/', post_data)
  66
+        self.failUnlessEqual(response.status_code, 302) # redirect somewhere
6  tests/regressiontests/generic_inline_admin/urls.py
... ...
@@ -0,0 +1,6 @@
  1
+from django.conf.urls.defaults import *
  2
+from django.contrib import admin
  3
+
  4
+urlpatterns = patterns('',
  5
+    (r'^admin/(.*)', admin.site.root),
  6
+)
1  tests/urls.py
@@ -23,6 +23,7 @@
23 23
     
24 24
     # admin view tests
25 25
     (r'^test_admin/', include('regressiontests.admin_views.urls')),
  26
+    (r'^generic_inline_admin/', include('regressiontests.generic_inline_admin.urls')),
26 27
 
27 28
     (r'^utils/', include('regressiontests.utils.urls')),
28 29
 

0 notes on commit f219136

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