Fixed #12475 -- Fixed an edge case with hidden fields in ModelAdmin changelists when used in conjunction with list_display_links or list_editable. Thanks, Simon Meers, Julien Phalip, Karen and master.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15722 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2011-03-03 13:20:45 +00:00
parent c0fb9bd00b
commit b921f1bac0
4 changed files with 59 additions and 3 deletions

View File

@ -1,5 +1,5 @@
{% if result_hidden_fields %} {% if result_hidden_fields %}
<div class="hiddenfields"> {# DIV for HTML validation #} <div class="hiddenfields">{# DIV for HTML validation #}
{% for item in result_hidden_fields %}{{ item }}{% endfor %} {% for item in result_hidden_fields %}{{ item }}{% endfor %}
</div> </div>
{% endif %} {% endif %}

View File

@ -188,7 +188,9 @@ def items_for_result(cl, result, form):
# By default the fields come from ModelAdmin.list_editable, but if we pull # By default the fields come from ModelAdmin.list_editable, but if we pull
# the fields out of the form instead of list_editable custom admins # the fields out of the form instead of list_editable custom admins
# can provide fields on a per request basis # can provide fields on a per request basis
if form and field_name in form.fields: if (form and field_name in form.fields and not (
field_name == cl.model._meta.pk.name and
form[cl.model._meta.pk.name].is_hidden)):
bf = form[field_name] bf = form[field_name]
result_repr = mark_safe(force_unicode(bf.errors) + force_unicode(bf)) result_repr = mark_safe(force_unicode(bf.errors) + force_unicode(bf))
else: else:

View File

@ -735,6 +735,28 @@ class CoverLetterAdmin(admin.ModelAdmin):
#return super(CoverLetterAdmin, self).queryset(request).only('author') #return super(CoverLetterAdmin, self).queryset(request).only('author')
return super(CoverLetterAdmin, self).queryset(request).defer('date') return super(CoverLetterAdmin, self).queryset(request).defer('date')
class Story(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
class StoryForm(forms.ModelForm):
class Meta:
widgets = {'title': forms.HiddenInput}
class StoryAdmin(admin.ModelAdmin):
list_display = ('id', 'title', 'content')
list_display_links = ('title',) # 'id' not in list_display_links
list_editable = ('content', )
form = StoryForm
class OtherStory(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
class OtherStoryAdmin(admin.ModelAdmin):
list_display = ('id', 'title', 'content')
list_display_links = ('title', 'id') # 'id' in list_display_links
list_editable = ('content', )
admin.site.register(Article, ArticleAdmin) admin.site.register(Article, ArticleAdmin)
admin.site.register(CustomArticle, CustomArticleAdmin) admin.site.register(CustomArticle, CustomArticleAdmin)
@ -776,6 +798,8 @@ admin.site.register(FoodDelivery, FoodDeliveryAdmin)
admin.site.register(RowLevelChangePermissionModel, RowLevelChangePermissionModelAdmin) admin.site.register(RowLevelChangePermissionModel, RowLevelChangePermissionModelAdmin)
admin.site.register(Paper, PaperAdmin) admin.site.register(Paper, PaperAdmin)
admin.site.register(CoverLetter, CoverLetterAdmin) admin.site.register(CoverLetter, CoverLetterAdmin)
admin.site.register(Story, StoryAdmin)
admin.site.register(OtherStory, OtherStoryAdmin)
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
# That way we cover all four cases: # That way we cover all four cases:

View File

@ -36,7 +36,7 @@ from models import (Article, BarAccount, CustomArticle, EmptyModel,
Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit,
Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee, Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee,
Question, Answer, Inquisition, Actor, FoodDelivery, Question, Answer, Inquisition, Actor, FoodDelivery,
RowLevelChangePermissionModel, Paper, CoverLetter) RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory)
class AdminViewBasicTest(TestCase): class AdminViewBasicTest(TestCase):
@ -1678,6 +1678,36 @@ class AdminViewListEditable(TestCase):
response = self.client.get('/test_admin/admin/admin_views/person/?%s' % IS_POPUP_VAR) response = self.client.get('/test_admin/admin/admin_views/person/?%s' % IS_POPUP_VAR)
self.assertEqual(response.context['cl'].list_editable, ()) self.assertEqual(response.context['cl'].list_editable, ())
def test_pk_hidden_fields(self):
""" Ensure that hidden pk fields aren't displayed in the table body and
that their corresponding human-readable value is displayed instead.
Note that the hidden pk fields are in fact be displayed but
separately (not in the table), and only once.
Refs #12475.
"""
Story.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...')
Story.objects.create(title='Crouching Tiger, Hidden Python', content='The Python was sneaking into...')
response = self.client.get('/test_admin/admin/admin_views/story/')
self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table.
self.assertContains(response, 'id="id_form-1-id"', 1)
self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="1" id="id_form-1-id" />\n</div>')
self.assertContains(response, '<td>1</td>', 1)
self.assertContains(response, '<td>2</td>', 1)
def test_pk_hidden_fields_with_list_display_links(self):
""" Similarly as test_pk_hidden_fields, but when the hidden pk fields are
referenced in list_display_links.
Refs #12475.
"""
OtherStory.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...')
OtherStory.objects.create(title='Crouching Tiger, Hidden Python', content='The Python was sneaking into...')
response = self.client.get('/test_admin/admin/admin_views/otherstory/')
self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table.
self.assertContains(response, 'id="id_form-1-id"', 1)
self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="1" id="id_form-1-id" />\n</div>')
self.assertContains(response, '<th><a href="1/">1</a></th>', 1)
self.assertContains(response, '<th><a href="2/">2</a></th>', 1)
class AdminSearchTest(TestCase): class AdminSearchTest(TestCase):
fixtures = ['admin-views-users', 'multiple-child-classes', fixtures = ['admin-views-users', 'multiple-child-classes',