mirror of
https://github.com/django/django.git
synced 2025-06-05 03:29:12 +00:00
Fixed #18072 -- Made more admin links use reverse() instead of hard-coded relative URLs.
Thanks kmike for the report and initial patch for the changelist->edit object view link URL. Other affected links include the delete object one and object history one (in this case the change had been implemented in commit 5a9e127, this commit adds admin-quoting of the object PK in a way similar to a222d6e.) Refs #15294.
This commit is contained in:
parent
515fd6a5de
commit
f51eab796d
@ -29,7 +29,7 @@
|
|||||||
{% if change %}{% if not is_popup %}
|
{% if change %}{% if not is_popup %}
|
||||||
<ul class="object-tools">
|
<ul class="object-tools">
|
||||||
{% block object-tools-items %}
|
{% block object-tools-items %}
|
||||||
<li><a href="{% url opts|admin_urlname:'history' original.pk %}" class="historylink">{% trans "History" %}</a></li>
|
<li><a href="{% url opts|admin_urlname:'history' original.pk|admin_urlquote %}" class="historylink">{% trans "History" %}</a></li>
|
||||||
{% if has_absolute_url %}<li><a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
|
{% if has_absolute_url %}<li><a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{% load i18n %}
|
{% load i18n admin_urls %}
|
||||||
<div class="submit-row">
|
<div class="submit-row">
|
||||||
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" {{ onclick_attrib }}/>{% endif %}
|
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" {{ onclick_attrib }}/>{% endif %}
|
||||||
{% if show_delete_link %}<p class="deletelink-box"><a href="delete/" class="deletelink">{% trans "Delete" %}</a></p>{% endif %}
|
{% if show_delete_link %}<p class="deletelink-box"><a href="{% url opts|admin_urlname:'delete' original.pk|admin_urlquote %}" class="deletelink">{% trans "Delete" %}</a></p>{% endif %}
|
||||||
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" {{ onclick_attrib }}/>{%endif%}
|
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" {{ onclick_attrib }}/>{%endif%}
|
||||||
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" {{ onclick_attrib }} />{% endif %}
|
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" {{ onclick_attrib }}/>{% endif %}
|
||||||
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" {{ onclick_attrib }}/>{% endif %}
|
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" {{ onclick_attrib }}/>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,7 +28,8 @@ def submit_row(context):
|
|||||||
change = context['change']
|
change = context['change']
|
||||||
is_popup = context['is_popup']
|
is_popup = context['is_popup']
|
||||||
save_as = context['save_as']
|
save_as = context['save_as']
|
||||||
return {
|
ctx = {
|
||||||
|
'opts': opts,
|
||||||
'onclick_attrib': (opts.get_ordered_objects() and change
|
'onclick_attrib': (opts.get_ordered_objects() and change
|
||||||
and 'onclick="submitOrderForm();"' or ''),
|
and 'onclick="submitOrderForm();"' or ''),
|
||||||
'show_delete_link': (not is_popup and context['has_delete_permission']
|
'show_delete_link': (not is_popup and context['has_delete_permission']
|
||||||
@ -40,6 +41,9 @@ def submit_row(context):
|
|||||||
'is_popup': is_popup,
|
'is_popup': is_popup,
|
||||||
'show_save': True
|
'show_save': True
|
||||||
}
|
}
|
||||||
|
if context.get('original') is not None:
|
||||||
|
ctx['original'] = context['original']
|
||||||
|
return ctx
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def cell_count(inline_admin_form):
|
def cell_count(inline_admin_form):
|
||||||
|
@ -48,9 +48,9 @@ def prepare_lookup_value(key, value):
|
|||||||
def quote(s):
|
def quote(s):
|
||||||
"""
|
"""
|
||||||
Ensure that primary key values do not confuse the admin URLs by escaping
|
Ensure that primary key values do not confuse the admin URLs by escaping
|
||||||
any '/', '_' and ':' characters. Similar to urllib.quote, except that the
|
any '/', '_' and ':' and similarly problematic characters.
|
||||||
quoting is slightly different so that it doesn't get automatically
|
Similar to urllib.quote, except that the quoting is slightly different so
|
||||||
unquoted by the Web browser.
|
that it doesn't get automatically unquoted by the Web browser.
|
||||||
"""
|
"""
|
||||||
if not isinstance(s, six.string_types):
|
if not isinstance(s, six.string_types):
|
||||||
return s
|
return s
|
||||||
|
@ -3,6 +3,7 @@ from functools import reduce
|
|||||||
|
|
||||||
from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
|
from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
|
||||||
from django.core.paginator import InvalidPage
|
from django.core.paginator import InvalidPage
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields import FieldDoesNotExist
|
from django.db.models.fields import FieldDoesNotExist
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
@ -376,4 +377,8 @@ class ChangeList(object):
|
|||||||
return qs
|
return qs
|
||||||
|
|
||||||
def url_for_result(self, result):
|
def url_for_result(self, result):
|
||||||
return "%s/" % quote(getattr(result, self.pk_attname))
|
pk = getattr(result, self.pk_attname)
|
||||||
|
return reverse('admin:%s_%s_change' % (self.opts.app_label,
|
||||||
|
self.opts.module_name),
|
||||||
|
args=(quote(pk),),
|
||||||
|
current_app=self.model_admin.admin_site.name)
|
||||||
|
@ -6,6 +6,7 @@ from django.contrib import admin
|
|||||||
from django.contrib.admin.options import IncorrectLookupParameters
|
from django.contrib.admin.options import IncorrectLookupParameters
|
||||||
from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR
|
from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
@ -65,7 +66,8 @@ class ChangeListTests(TestCase):
|
|||||||
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
||||||
context = Context({'cl': cl})
|
context = Context({'cl': cl})
|
||||||
table_output = template.render(context)
|
table_output = template.render(context)
|
||||||
row_html = '<tbody><tr class="row1"><th><a href="%d/">name</a></th><td class="nowrap">(None)</td></tr></tbody>' % new_child.id
|
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
||||||
|
row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">(None)</td></tr></tbody>' % link
|
||||||
self.assertFalse(table_output.find(row_html) == -1,
|
self.assertFalse(table_output.find(row_html) == -1,
|
||||||
'Failed to find expected row element: %s' % table_output)
|
'Failed to find expected row element: %s' % table_output)
|
||||||
|
|
||||||
@ -87,7 +89,8 @@ class ChangeListTests(TestCase):
|
|||||||
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
||||||
context = Context({'cl': cl})
|
context = Context({'cl': cl})
|
||||||
table_output = template.render(context)
|
table_output = template.render(context)
|
||||||
row_html = '<tbody><tr class="row1"><th><a href="%d/">name</a></th><td class="nowrap">Parent object</td></tr></tbody>' % new_child.id
|
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
||||||
|
row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">Parent object</td></tr></tbody>' % link
|
||||||
self.assertFalse(table_output.find(row_html) == -1,
|
self.assertFalse(table_output.find(row_html) == -1,
|
||||||
'Failed to find expected row element: %s' % table_output)
|
'Failed to find expected row element: %s' % table_output)
|
||||||
|
|
||||||
@ -425,7 +428,8 @@ class ChangeListTests(TestCase):
|
|||||||
request = self._mocked_authenticated_request('/child/', superuser)
|
request = self._mocked_authenticated_request('/child/', superuser)
|
||||||
response = m.changelist_view(request)
|
response = m.changelist_view(request)
|
||||||
for i in range(1, 10):
|
for i in range(1, 10):
|
||||||
self.assertContains(response, '<a href="%s/">%s</a>' % (i, i))
|
link = reverse('admin:admin_changelist_child_change', args=(i,))
|
||||||
|
self.assertContains(response, '<a href="%s">%s</a>' % (link, i))
|
||||||
|
|
||||||
list_display = m.get_list_display(request)
|
list_display = m.get_list_display(request)
|
||||||
list_display_links = m.get_list_display_links(request, list_display)
|
list_display_links = m.get_list_display_links(request, list_display)
|
||||||
|
@ -40,12 +40,5 @@
|
|||||||
"fields": {
|
"fields": {
|
||||||
"description": "An action with a name suspected of being a XSS attempt"
|
"description": "An action with a name suspected of being a XSS attempt"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"pk": "The name of an action",
|
|
||||||
"model": "admin_custom_urls.action",
|
|
||||||
"fields": {
|
|
||||||
"description": "A generic action"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from django.contrib.admin.util import quote
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
@ -67,7 +68,7 @@ class AdminCustomUrlsTest(TestCase):
|
|||||||
|
|
||||||
# Ditto, but use reverse() to build the URL
|
# Ditto, but use reverse() to build the URL
|
||||||
url = reverse('admin:%s_action_change' % Action._meta.app_label,
|
url = reverse('admin:%s_action_change' % Action._meta.app_label,
|
||||||
args=('add',))
|
args=(quote('add'),))
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertContains(response, 'Change action')
|
self.assertContains(response, 'Change action')
|
||||||
@ -75,19 +76,8 @@ class AdminCustomUrlsTest(TestCase):
|
|||||||
# Should correctly get the change_view for the model instance with the
|
# Should correctly get the change_view for the model instance with the
|
||||||
# funny-looking PK (the one wth a 'path/to/html/document.html' value)
|
# funny-looking PK (the one wth a 'path/to/html/document.html' value)
|
||||||
url = reverse('admin:%s_action_change' % Action._meta.app_label,
|
url = reverse('admin:%s_action_change' % Action._meta.app_label,
|
||||||
args=("path/to/html/document.html",))
|
args=(quote("path/to/html/document.html"),))
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertContains(response, 'Change action')
|
self.assertContains(response, 'Change action')
|
||||||
self.assertContains(response, 'value="path/to/html/document.html"')
|
self.assertContains(response, 'value="path/to/html/document.html"')
|
||||||
|
|
||||||
def testChangeViewHistoryButton(self):
|
|
||||||
url = reverse('admin:%s_action_change' % Action._meta.app_label,
|
|
||||||
args=('The name of an action',))
|
|
||||||
response = self.client.get(url)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
expected_link = reverse('admin:%s_action_history' %
|
|
||||||
Action._meta.app_label,
|
|
||||||
args=('The name of an action',))
|
|
||||||
self.assertContains(response, '<a href="%s" class="historylink"' %
|
|
||||||
expected_link)
|
|
||||||
|
@ -260,19 +260,21 @@ class AdminViewBasicTest(TestCase):
|
|||||||
p1 = Person.objects.create(name="Chris", gender=1, alive=True)
|
p1 = Person.objects.create(name="Chris", gender=1, alive=True)
|
||||||
p2 = Person.objects.create(name="Chris", gender=2, alive=True)
|
p2 = Person.objects.create(name="Chris", gender=2, alive=True)
|
||||||
p3 = Person.objects.create(name="Bob", gender=1, alive=True)
|
p3 = Person.objects.create(name="Bob", gender=1, alive=True)
|
||||||
link = '<a href="%s/'
|
link1 = reverse('admin:admin_views_person_change', args=(p1.pk,))
|
||||||
|
link2 = reverse('admin:admin_views_person_change', args=(p2.pk,))
|
||||||
|
link3 = reverse('admin:admin_views_person_change', args=(p3.pk,))
|
||||||
|
|
||||||
# Sort by name, gender
|
# Sort by name, gender
|
||||||
# This hard-codes the URL because it'll fail if it runs against the
|
# This hard-codes the URL because it'll fail if it runs against the
|
||||||
# 'admin2' custom admin (which doesn't have the Person model).
|
# 'admin2' custom admin (which doesn't have the Person model).
|
||||||
response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '1.2'})
|
response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '1.2'})
|
||||||
self.assertContentBefore(response, link % p3.id, link % p1.id)
|
self.assertContentBefore(response, link3, link1)
|
||||||
self.assertContentBefore(response, link % p1.id, link % p2.id)
|
self.assertContentBefore(response, link1, link2)
|
||||||
|
|
||||||
# Sort by gender descending, name
|
# Sort by gender descending, name
|
||||||
response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '-2.1'})
|
response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '-2.1'})
|
||||||
self.assertContentBefore(response, link % p2.id, link % p3.id)
|
self.assertContentBefore(response, link2, link3)
|
||||||
self.assertContentBefore(response, link % p3.id, link % p1.id)
|
self.assertContentBefore(response, link3, link1)
|
||||||
|
|
||||||
def testChangeListSortingPreserveQuerySetOrdering(self):
|
def testChangeListSortingPreserveQuerySetOrdering(self):
|
||||||
"""
|
"""
|
||||||
@ -284,37 +286,41 @@ class AdminViewBasicTest(TestCase):
|
|||||||
p1 = Person.objects.create(name="Amy", gender=1, alive=True, age=80)
|
p1 = Person.objects.create(name="Amy", gender=1, alive=True, age=80)
|
||||||
p2 = Person.objects.create(name="Bob", gender=1, alive=True, age=70)
|
p2 = Person.objects.create(name="Bob", gender=1, alive=True, age=70)
|
||||||
p3 = Person.objects.create(name="Chris", gender=2, alive=False, age=60)
|
p3 = Person.objects.create(name="Chris", gender=2, alive=False, age=60)
|
||||||
link = '<a href="%s/'
|
link1 = reverse('admin:admin_views_person_change', args=(p1.pk,))
|
||||||
|
link2 = reverse('admin:admin_views_person_change', args=(p2.pk,))
|
||||||
|
link3 = reverse('admin:admin_views_person_change', args=(p3.pk,))
|
||||||
|
|
||||||
# This hard-codes the URL because it'll fail if it runs against the
|
# This hard-codes the URL because it'll fail if it runs against the
|
||||||
# 'admin2' custom admin (which doesn't have the Person model).
|
# 'admin2' custom admin (which doesn't have the Person model).
|
||||||
response = self.client.get('/test_admin/admin/admin_views/person/', {})
|
response = self.client.get('/test_admin/admin/admin_views/person/', {})
|
||||||
self.assertContentBefore(response, link % p3.id, link % p2.id)
|
self.assertContentBefore(response, link3, link2)
|
||||||
self.assertContentBefore(response, link % p2.id, link % p1.id)
|
self.assertContentBefore(response, link2, link1)
|
||||||
|
|
||||||
def testChangeListSortingModelMeta(self):
|
def testChangeListSortingModelMeta(self):
|
||||||
# Test ordering on Model Meta is respected
|
# Test ordering on Model Meta is respected
|
||||||
|
|
||||||
l1 = Language.objects.create(iso='ur', name='Urdu')
|
l1 = Language.objects.create(iso='ur', name='Urdu')
|
||||||
l2 = Language.objects.create(iso='ar', name='Arabic')
|
l2 = Language.objects.create(iso='ar', name='Arabic')
|
||||||
link = '<a href="%s/'
|
link1 = reverse('admin:admin_views_language_change', args=(quote(l1.pk),))
|
||||||
|
link2 = reverse('admin:admin_views_language_change', args=(quote(l2.pk),))
|
||||||
|
|
||||||
response = self.client.get('/test_admin/admin/admin_views/language/', {})
|
response = self.client.get('/test_admin/admin/admin_views/language/', {})
|
||||||
self.assertContentBefore(response, link % l2.pk, link % l1.pk)
|
self.assertContentBefore(response, link2, link1)
|
||||||
|
|
||||||
# Test we can override with query string
|
# Test we can override with query string
|
||||||
response = self.client.get('/test_admin/admin/admin_views/language/', {'o':'-1'})
|
response = self.client.get('/test_admin/admin/admin_views/language/', {'o':'-1'})
|
||||||
self.assertContentBefore(response, link % l1.pk, link % l2.pk)
|
self.assertContentBefore(response, link1, link2)
|
||||||
|
|
||||||
def testChangeListSortingOverrideModelAdmin(self):
|
def testChangeListSortingOverrideModelAdmin(self):
|
||||||
# Test ordering on Model Admin is respected, and overrides Model Meta
|
# Test ordering on Model Admin is respected, and overrides Model Meta
|
||||||
dt = datetime.datetime.now()
|
dt = datetime.datetime.now()
|
||||||
p1 = Podcast.objects.create(name="A", release_date=dt)
|
p1 = Podcast.objects.create(name="A", release_date=dt)
|
||||||
p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10))
|
p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10))
|
||||||
|
link1 = reverse('admin:admin_views_podcast_change', args=(p1.pk,))
|
||||||
|
link2 = reverse('admin:admin_views_podcast_change', args=(p2.pk,))
|
||||||
|
|
||||||
link = '<a href="%s/'
|
|
||||||
response = self.client.get('/test_admin/admin/admin_views/podcast/', {})
|
response = self.client.get('/test_admin/admin/admin_views/podcast/', {})
|
||||||
self.assertContentBefore(response, link % p1.pk, link % p2.pk)
|
self.assertContentBefore(response, link1, link2)
|
||||||
|
|
||||||
def testMultipleSortSameField(self):
|
def testMultipleSortSameField(self):
|
||||||
# Check that we get the columns we expect if we have two columns
|
# Check that we get the columns we expect if we have two columns
|
||||||
@ -322,14 +328,16 @@ class AdminViewBasicTest(TestCase):
|
|||||||
dt = datetime.datetime.now()
|
dt = datetime.datetime.now()
|
||||||
p1 = Podcast.objects.create(name="A", release_date=dt)
|
p1 = Podcast.objects.create(name="A", release_date=dt)
|
||||||
p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10))
|
p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10))
|
||||||
|
link1 = reverse('admin:admin_views_podcast_change', args=(quote(p1.pk),))
|
||||||
|
link2 = reverse('admin:admin_views_podcast_change', args=(quote(p2.pk),))
|
||||||
|
|
||||||
link = '<a href="%s/'
|
|
||||||
response = self.client.get('/test_admin/admin/admin_views/podcast/', {})
|
response = self.client.get('/test_admin/admin/admin_views/podcast/', {})
|
||||||
self.assertContentBefore(response, link % p1.pk, link % p2.pk)
|
self.assertContentBefore(response, link1, link2)
|
||||||
|
|
||||||
p1 = ComplexSortedPerson.objects.create(name="Bob", age=10)
|
p1 = ComplexSortedPerson.objects.create(name="Bob", age=10)
|
||||||
p2 = ComplexSortedPerson.objects.create(name="Amy", age=20)
|
p2 = ComplexSortedPerson.objects.create(name="Amy", age=20)
|
||||||
link = '<a href="%s/'
|
link1 = reverse('admin:admin_views_complexsortedperson_change', args=(p1.pk,))
|
||||||
|
link2 = reverse('admin:admin_views_complexsortedperson_change', args=(p2.pk,))
|
||||||
|
|
||||||
response = self.client.get('/test_admin/admin/admin_views/complexsortedperson/', {})
|
response = self.client.get('/test_admin/admin/admin_views/complexsortedperson/', {})
|
||||||
# Should have 5 columns (including action checkbox col)
|
# Should have 5 columns (including action checkbox col)
|
||||||
@ -342,7 +350,7 @@ class AdminViewBasicTest(TestCase):
|
|||||||
self.assertContentBefore(response, 'Name', 'Colored name')
|
self.assertContentBefore(response, 'Name', 'Colored name')
|
||||||
|
|
||||||
# Check sorting - should be by name
|
# Check sorting - should be by name
|
||||||
self.assertContentBefore(response, link % p2.id, link % p1.id)
|
self.assertContentBefore(response, link2, link1)
|
||||||
|
|
||||||
def testSortIndicatorsAdminOrder(self):
|
def testSortIndicatorsAdminOrder(self):
|
||||||
"""
|
"""
|
||||||
@ -461,10 +469,12 @@ class AdminViewBasicTest(TestCase):
|
|||||||
for rows corresponding to instances of a model in which a named group
|
for rows corresponding to instances of a model in which a named group
|
||||||
has been used in the choices option of a field.
|
has been used in the choices option of a field.
|
||||||
"""
|
"""
|
||||||
|
link1 = reverse('admin:admin_views_fabric_change', args=(1,), current_app=self.urlbit)
|
||||||
|
link2 = reverse('admin:admin_views_fabric_change', args=(2,), current_app=self.urlbit)
|
||||||
response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit)
|
response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit)
|
||||||
fail_msg = "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group."
|
fail_msg = "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group."
|
||||||
self.assertContains(response, '<a href="1/">Horizontal</a>', msg_prefix=fail_msg, html=True)
|
self.assertContains(response, '<a href="%s">Horizontal</a>' % link1, msg_prefix=fail_msg, html=True)
|
||||||
self.assertContains(response, '<a href="2/">Vertical</a>', msg_prefix=fail_msg, html=True)
|
self.assertContains(response, '<a href="%s">Vertical</a>' % link2, msg_prefix=fail_msg, html=True)
|
||||||
|
|
||||||
def testNamedGroupFieldChoicesFilter(self):
|
def testNamedGroupFieldChoicesFilter(self):
|
||||||
"""
|
"""
|
||||||
@ -1371,9 +1381,12 @@ class AdminViewStringPrimaryKeyTest(TestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_changelist_to_changeform_link(self):
|
def test_changelist_to_changeform_link(self):
|
||||||
"The link from the changelist referring to the changeform of the object should be quoted"
|
"Link to the changeform of the object in changelist should use reverse() and be quoted -- #18072"
|
||||||
response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/')
|
prefix = '/test_admin/admin/admin_views/modelwithstringprimarykey/'
|
||||||
should_contain = """<th><a href="%s/">%s</a></th></tr>""" % (escape(quote(self.pk)), escape(self.pk))
|
response = self.client.get(prefix)
|
||||||
|
# this URL now comes through reverse(), thus iri_to_uri encoding
|
||||||
|
pk_final_url = escape(iri_to_uri(quote(self.pk)))
|
||||||
|
should_contain = """<th><a href="%s%s/">%s</a></th>""" % (prefix, pk_final_url, escape(self.pk))
|
||||||
self.assertContains(response, should_contain)
|
self.assertContains(response, should_contain)
|
||||||
|
|
||||||
def test_recentactions_link(self):
|
def test_recentactions_link(self):
|
||||||
@ -1441,6 +1454,18 @@ class AdminViewStringPrimaryKeyTest(TestCase):
|
|||||||
should_contain = '/%s/" class="viewsitelink">' % model.pk
|
should_contain = '/%s/" class="viewsitelink">' % model.pk
|
||||||
self.assertContains(response, should_contain)
|
self.assertContains(response, should_contain)
|
||||||
|
|
||||||
|
def test_change_view_history_link(self):
|
||||||
|
"""Object history button link should work and contain the pk value quoted."""
|
||||||
|
url = reverse('admin:%s_modelwithstringprimarykey_change' %
|
||||||
|
ModelWithStringPrimaryKey._meta.app_label,
|
||||||
|
args=(quote(self.pk),))
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
expected_link = reverse('admin:%s_modelwithstringprimarykey_history' %
|
||||||
|
ModelWithStringPrimaryKey._meta.app_label,
|
||||||
|
args=(quote(self.pk),))
|
||||||
|
self.assertContains(response, '<a href="%s" class="historylink"' % expected_link)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
class SecureViewTests(TestCase):
|
class SecureViewTests(TestCase):
|
||||||
@ -2023,12 +2048,14 @@ class AdminViewListEditable(TestCase):
|
|||||||
"""
|
"""
|
||||||
story1 = OtherStory.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...')
|
story1 = OtherStory.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...')
|
||||||
story2 = OtherStory.objects.create(title='Crouching Tiger, Hidden Python', content='The Python was sneaking into...')
|
story2 = OtherStory.objects.create(title='Crouching Tiger, Hidden Python', content='The Python was sneaking into...')
|
||||||
|
link1 = reverse('admin:admin_views_otherstory_change', args=(story1.pk,))
|
||||||
|
link2 = reverse('admin:admin_views_otherstory_change', args=(story2.pk,))
|
||||||
response = self.client.get('/test_admin/admin/admin_views/otherstory/')
|
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-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, 'id="id_form-1-id"', 1)
|
||||||
self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True)
|
self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True)
|
||||||
self.assertContains(response, '<th><a href="%d/">%d</a></th>' % (story1.id, story1.id), 1)
|
self.assertContains(response, '<th><a href="%s">%d</a></th>' % (link1, story1.id), 1)
|
||||||
self.assertContains(response, '<th><a href="%d/">%d</a></th>' % (story2.id, story2.id), 1)
|
self.assertContains(response, '<th><a href="%s">%d</a></th>' % (link2, story2.id), 1)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user