Fixed #20934 -- Avoided NoReverseMatch in ModelAdmin.changelist_view

The view tried to display links to a ModelAdmin's change_view, which
resulted in NoReverseMatches if get_urls was overridden to remove the
corresponding url.
This commit is contained in:
Rainer Koirikivi 2013-08-21 19:22:22 +03:00 committed by Tim Graham
parent 6af05e7a0f
commit 65cf82bd08
2 changed files with 40 additions and 15 deletions

View File

@ -9,6 +9,7 @@ from django.contrib.admin.views.main import (ALL_VAR, EMPTY_CHANGELIST_VALUE,
ORDER_VAR, PAGE_VAR, SEARCH_VAR) ORDER_VAR, PAGE_VAR, SEARCH_VAR)
from django.contrib.admin.templatetags.admin_static import static from django.contrib.admin.templatetags.admin_static import static
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import NoReverseMatch
from django.db import models from django.db import models
from django.utils import formats from django.utils import formats
from django.utils.html import escapejs, format_html from django.utils.html import escapejs, format_html
@ -216,25 +217,36 @@ def items_for_result(cl, result, form):
row_class = mark_safe(' class="%s"' % ' '.join(row_classes)) row_class = mark_safe(' class="%s"' % ' '.join(row_classes))
# If list_display_links not defined, add the link tag to the first field # If list_display_links not defined, add the link tag to the first field
if (first and not cl.list_display_links) or field_name in cl.list_display_links: if (first and not cl.list_display_links) or field_name in cl.list_display_links:
table_tag = {True:'th', False:'td'}[first] table_tag = 'th' if first else 'td'
first = False first = False
url = cl.url_for_result(result)
url = add_preserved_filters({'preserved_filters': cl.preserved_filters, 'opts': cl.opts}, url) # Display link to the result's change_view if the url exists, else
# Convert the pk to something that can be used in Javascript. # display just the result's representation.
# Problem cases are long ints (23L) and non-ASCII strings. try:
if cl.to_field: url = cl.url_for_result(result)
attr = str(cl.to_field) except NoReverseMatch:
link_or_text = result_repr
else: else:
attr = pk url = add_preserved_filters({'preserved_filters': cl.preserved_filters, 'opts': cl.opts}, url)
value = result.serializable_value(attr) # Convert the pk to something that can be used in Javascript.
result_id = escapejs(value) # Problem cases are long ints (23L) and non-ASCII strings.
yield format_html('<{0}{1}><a href="{2}"{3}>{4}</a></{5}>', if cl.to_field:
attr = str(cl.to_field)
else:
attr = pk
value = result.serializable_value(attr)
result_id = escapejs(value)
link_or_text = format_html(
'<a href="{0}"{1}>{2}</a>',
url,
format_html(' onclick="opener.dismissRelatedLookupPopup(window, &#39;{0}&#39;); return false;"', result_id)
if cl.is_popup else '',
result_repr)
yield format_html('<{0}{1}>{2}</{3}>',
table_tag, table_tag,
row_class, row_class,
url, link_or_text,
format_html(' onclick="opener.dismissRelatedLookupPopup(window, &#39;{0}&#39;); return false;"', result_id)
if cl.is_popup else '',
result_repr,
table_tag) table_tag)
else: else:
# 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

View File

@ -630,6 +630,19 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
with self.assertRaises(AttributeError): with self.assertRaises(AttributeError):
self.client.get('/test_admin/%s/admin_views/simple/' % self.urlbit) self.client.get('/test_admin/%s/admin_views/simple/' % self.urlbit)
def test_changelist_with_no_change_url(self):
"""
ModelAdmin.changelist_view shouldn't result in a NoReverseMatch if url
for change_view is removed from get_urls
Regression test for #20934
"""
UnchangeableObject.objects.create()
response = self.client.get('/test_admin/admin/admin_views/unchangeableobject/')
self.assertEqual(response.status_code, 200)
# Check the format of the shown object -- shouldn't contain a change link
self.assertContains(response, '<th class="field-__str__">UnchangeableObject object</th>', html=True)
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class AdminViewFormUrlTest(TestCase): class AdminViewFormUrlTest(TestCase):