Fixed #15779 -- Allowed 'add' primary key in admin edition
Thanks Marwan Alsabbagh for the report, and Simon Charette and Tim Graham for the reviews.
This commit is contained in:
parent
47ee7b48ad
commit
1791a7e75a
|
@ -46,6 +46,7 @@ from django.utils.safestring import mark_safe
|
||||||
from django.utils.text import capfirst, get_text_list
|
from django.utils.text import capfirst, get_text_list
|
||||||
from django.utils.translation import string_concat, ugettext as _, ungettext
|
from django.utils.translation import string_concat, ugettext as _, ungettext
|
||||||
from django.views.decorators.csrf import csrf_protect
|
from django.views.decorators.csrf import csrf_protect
|
||||||
|
from django.views.generic import RedirectView
|
||||||
|
|
||||||
IS_POPUP_VAR = '_popup'
|
IS_POPUP_VAR = '_popup'
|
||||||
TO_FIELD_VAR = '_to_field'
|
TO_FIELD_VAR = '_to_field'
|
||||||
|
@ -554,7 +555,11 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
url(r'^add/$', wrap(self.add_view), name='%s_%s_add' % info),
|
url(r'^add/$', wrap(self.add_view), name='%s_%s_add' % info),
|
||||||
url(r'^(.+)/history/$', wrap(self.history_view), name='%s_%s_history' % info),
|
url(r'^(.+)/history/$', wrap(self.history_view), name='%s_%s_history' % info),
|
||||||
url(r'^(.+)/delete/$', wrap(self.delete_view), name='%s_%s_delete' % info),
|
url(r'^(.+)/delete/$', wrap(self.delete_view), name='%s_%s_delete' % info),
|
||||||
url(r'^(.+)/$', wrap(self.change_view), name='%s_%s_change' % info),
|
url(r'^(.+)/change/$', wrap(self.change_view), name='%s_%s_change' % info),
|
||||||
|
# For backwards compatibility (was the change url before 1.9)
|
||||||
|
url(r'^(.+)/$', wrap(RedirectView.as_view(
|
||||||
|
pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info)
|
||||||
|
))),
|
||||||
]
|
]
|
||||||
return urlpatterns
|
return urlpatterns
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,14 @@ Minor features
|
||||||
|
|
||||||
* Admin views now have ``model_admin`` or ``admin_site`` attributes.
|
* Admin views now have ``model_admin`` or ``admin_site`` attributes.
|
||||||
|
|
||||||
|
* The URL of the admin change view has been changed (was at
|
||||||
|
``/admin/<app>/<model>/<pk>/`` by default and is now at
|
||||||
|
``/admin/<app>/<model>/<pk>/change/``). This should not affect your
|
||||||
|
application unless you have hardcoded admin URLs. In that case, replace those
|
||||||
|
links by :ref:`reversing admin URLs <admin-reverse-urls>` instead. Note that
|
||||||
|
the old URL still redirects to the new one for backwards compatibility, but
|
||||||
|
it may be removed in a future version.
|
||||||
|
|
||||||
:mod:`django.contrib.auth`
|
:mod:`django.contrib.auth`
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -61,11 +61,6 @@ class AdminCustomUrlsTest(TestCase):
|
||||||
"""
|
"""
|
||||||
# Should get the change_view for model instance with PK 'add', not show
|
# Should get the change_view for model instance with PK 'add', not show
|
||||||
# the add_view
|
# the add_view
|
||||||
response = self.client.get('/admin/admin_custom_urls/action/add/')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertContains(response, 'Change action')
|
|
||||||
|
|
||||||
# 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=(quote('add'),))
|
args=(quote('add'),))
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
|
|
|
@ -693,7 +693,7 @@ class UnchangeableObjectAdmin(admin.ModelAdmin):
|
||||||
def get_urls(self):
|
def get_urls(self):
|
||||||
# Disable change_view, but leave other urls untouched
|
# Disable change_view, but leave other urls untouched
|
||||||
urlpatterns = super(UnchangeableObjectAdmin, self).get_urls()
|
urlpatterns = super(UnchangeableObjectAdmin, self).get_urls()
|
||||||
return [p for p in urlpatterns if not p.name.endswith("_change")]
|
return [p for p in urlpatterns if p.name and not p.name.endswith("_change")]
|
||||||
|
|
||||||
|
|
||||||
def callable_on_unknown(obj):
|
def callable_on_unknown(obj):
|
||||||
|
|
|
@ -137,6 +137,15 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
|
||||||
response = self.client.get(reverse('admin:admin_views_section_change', args=('abc',)))
|
response = self.client.get(reverse('admin:admin_views_section_change', args=('abc',)))
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
|
def test_basic_edit_GET_old_url_redirect(self):
|
||||||
|
"""
|
||||||
|
The change URL changed in Django 1.9, but the old one still redirects.
|
||||||
|
"""
|
||||||
|
response = self.client.get(
|
||||||
|
reverse('admin:admin_views_section_change', args=(1,)).replace('change/', '')
|
||||||
|
)
|
||||||
|
self.assertRedirects(response, reverse('admin:admin_views_section_change', args=(1,)))
|
||||||
|
|
||||||
def test_basic_inheritance_GET_string_PK(self):
|
def test_basic_inheritance_GET_string_PK(self):
|
||||||
"""
|
"""
|
||||||
Ensure GET on the change_view works on inherited models (returns an
|
Ensure GET on the change_view works on inherited models (returns an
|
||||||
|
@ -2022,8 +2031,8 @@ class AdminViewStringPrimaryKeyTest(TestCase):
|
||||||
self.assertContains(response, should_contain)
|
self.assertContains(response, should_contain)
|
||||||
|
|
||||||
def test_url_conflicts_with_add(self):
|
def test_url_conflicts_with_add(self):
|
||||||
"A model with a primary key that ends with add should be visible"
|
"A model with a primary key that ends with add or is `add` should be visible"
|
||||||
add_model = ModelWithStringPrimaryKey(pk="i have something to add")
|
add_model = ModelWithStringPrimaryKey.objects.create(pk="i have something to add")
|
||||||
add_model.save()
|
add_model.save()
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(add_model.pk),))
|
reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(add_model.pk),))
|
||||||
|
@ -2031,6 +2040,11 @@ class AdminViewStringPrimaryKeyTest(TestCase):
|
||||||
should_contain = """<h1>Change model with string primary key</h1>"""
|
should_contain = """<h1>Change model with string primary key</h1>"""
|
||||||
self.assertContains(response, should_contain)
|
self.assertContains(response, should_contain)
|
||||||
|
|
||||||
|
add_model2 = ModelWithStringPrimaryKey.objects.create(pk="add")
|
||||||
|
add_url = reverse('admin:admin_views_modelwithstringprimarykey_add')
|
||||||
|
change_url = reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(add_model2.pk),))
|
||||||
|
self.assertNotEqual(add_url, change_url)
|
||||||
|
|
||||||
def test_url_conflicts_with_delete(self):
|
def test_url_conflicts_with_delete(self):
|
||||||
"A model with a primary key that ends with delete should be visible"
|
"A model with a primary key that ends with delete should be visible"
|
||||||
delete_model = ModelWithStringPrimaryKey(pk="delete")
|
delete_model = ModelWithStringPrimaryKey(pk="delete")
|
||||||
|
|
Loading…
Reference in New Issue