Fixed #19505 -- A more flexible implementation for customizable admin redirect urls.
Work by Julien Phalip.
Refs #8001, #18310, #19505. See also 0b908b92a2
.
This commit is contained in:
parent
4a71b84266
commit
35d1cd0b28
|
@ -9,7 +9,7 @@ from django.forms.models import (modelform_factory, modelformset_factory,
|
||||||
inlineformset_factory, BaseInlineFormSet)
|
inlineformset_factory, BaseInlineFormSet)
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.admin import widgets, helpers
|
from django.contrib.admin import widgets, helpers
|
||||||
from django.contrib.admin.util import quote, unquote, flatten_fieldsets, get_deleted_objects, model_format_dict
|
from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_format_dict
|
||||||
from django.contrib.admin.templatetags.admin_static import static
|
from django.contrib.admin.templatetags.admin_static import static
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.views.decorators.csrf import csrf_protect
|
from django.views.decorators.csrf import csrf_protect
|
||||||
|
@ -38,6 +38,7 @@ HORIZONTAL, VERTICAL = 1, 2
|
||||||
# returns the <ul> class for a given radio_admin field
|
# returns the <ul> class for a given radio_admin field
|
||||||
get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
|
get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
|
||||||
|
|
||||||
|
|
||||||
class IncorrectLookupParameters(Exception):
|
class IncorrectLookupParameters(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ FORMFIELD_FOR_DBFIELD_DEFAULTS = {
|
||||||
|
|
||||||
csrf_protect_m = method_decorator(csrf_protect)
|
csrf_protect_m = method_decorator(csrf_protect)
|
||||||
|
|
||||||
|
|
||||||
class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
|
class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
|
||||||
"""Functionality common to both ModelAdmin and InlineAdmin."""
|
"""Functionality common to both ModelAdmin and InlineAdmin."""
|
||||||
|
|
||||||
|
@ -787,49 +789,37 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
"admin/change_form.html"
|
"admin/change_form.html"
|
||||||
], context, current_app=self.admin_site.name)
|
], context, current_app=self.admin_site.name)
|
||||||
|
|
||||||
def response_add(self, request, obj, post_url_continue='../%s/',
|
def response_add(self, request, obj, post_url_continue=None):
|
||||||
continue_editing_url=None, add_another_url=None,
|
|
||||||
hasperm_url=None, noperm_url=None):
|
|
||||||
"""
|
"""
|
||||||
Determines the HttpResponse for the add_view stage.
|
Determines the HttpResponse for the add_view stage.
|
||||||
|
|
||||||
:param request: HttpRequest instance.
|
|
||||||
:param obj: Object just added.
|
|
||||||
:param post_url_continue: Deprecated/undocumented.
|
|
||||||
:param continue_editing_url: URL where user will be redirected after
|
|
||||||
pressing 'Save and continue editing'.
|
|
||||||
:param add_another_url: URL where user will be redirected after
|
|
||||||
pressing 'Save and add another'.
|
|
||||||
:param hasperm_url: URL to redirect after a successful object creation
|
|
||||||
when the user has change permissions.
|
|
||||||
:param noperm_url: URL to redirect after a successful object creation
|
|
||||||
when the user has no change permissions.
|
|
||||||
"""
|
"""
|
||||||
if post_url_continue != '../%s/':
|
|
||||||
warnings.warn("The undocumented 'post_url_continue' argument to "
|
|
||||||
"ModelAdmin.response_add() is deprecated, use the new "
|
|
||||||
"*_url arguments instead.", DeprecationWarning,
|
|
||||||
stacklevel=2)
|
|
||||||
opts = obj._meta
|
opts = obj._meta
|
||||||
pk_value = obj.pk
|
pk_value = obj._get_pk_val()
|
||||||
app_label = opts.app_label
|
|
||||||
model_name = opts.module_name
|
|
||||||
site_name = self.admin_site.name
|
|
||||||
|
|
||||||
msg_dict = {'name': force_text(opts.verbose_name), 'obj': force_text(obj)}
|
msg_dict = {'name': force_text(opts.verbose_name), 'obj': force_text(obj)}
|
||||||
|
|
||||||
# Here, we distinguish between different save types by checking for
|
# Here, we distinguish between different save types by checking for
|
||||||
# the presence of keys in request.POST.
|
# the presence of keys in request.POST.
|
||||||
if "_continue" in request.POST:
|
if "_continue" in request.POST:
|
||||||
msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % msg_dict
|
msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % msg_dict
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
if continue_editing_url is None:
|
if post_url_continue is None:
|
||||||
continue_editing_url = 'admin:%s_%s_change' % (app_label, model_name)
|
post_url_continue = reverse('admin:%s_%s_change' %
|
||||||
url = reverse(continue_editing_url, args=(quote(pk_value),),
|
(opts.app_label, opts.module_name),
|
||||||
current_app=site_name)
|
args=(pk_value,),
|
||||||
|
current_app=self.admin_site.name)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
post_url_continue = post_url_continue % pk_value
|
||||||
|
warnings.warn(
|
||||||
|
"The use of string formats for post_url_continue "
|
||||||
|
"in ModelAdmin.response_add() is deprecated. Provide "
|
||||||
|
"a pre-formatted url instead.",
|
||||||
|
DeprecationWarning, stacklevel=2)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
if "_popup" in request.POST:
|
if "_popup" in request.POST:
|
||||||
url += "?_popup=1"
|
post_url_continue += "?_popup=1"
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(post_url_continue)
|
||||||
|
|
||||||
if "_popup" in request.POST:
|
if "_popup" in request.POST:
|
||||||
return HttpResponse(
|
return HttpResponse(
|
||||||
|
@ -840,102 +830,61 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
elif "_addanother" in request.POST:
|
elif "_addanother" in request.POST:
|
||||||
msg = _('The %(name)s "%(obj)s" was added successfully. You may add another %(name)s below.') % msg_dict
|
msg = _('The %(name)s "%(obj)s" was added successfully. You may add another %(name)s below.') % msg_dict
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
if add_another_url is None:
|
return HttpResponseRedirect(request.path)
|
||||||
add_another_url = 'admin:%s_%s_add' % (app_label, model_name)
|
|
||||||
url = reverse(add_another_url, current_app=site_name)
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
else:
|
else:
|
||||||
msg = _('The %(name)s "%(obj)s" was added successfully.') % msg_dict
|
msg = _('The %(name)s "%(obj)s" was added successfully.') % msg_dict
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
|
return self.response_post_save(request, obj)
|
||||||
|
|
||||||
# Figure out where to redirect. If the user has change permission,
|
def response_change(self, request, obj):
|
||||||
# redirect to the change-list page for this object. Otherwise,
|
|
||||||
# redirect to the admin index.
|
|
||||||
if self.has_change_permission(request, None):
|
|
||||||
if hasperm_url is None:
|
|
||||||
hasperm_url = 'admin:%s_%s_changelist' % (app_label, model_name)
|
|
||||||
url = reverse(hasperm_url, current_app=site_name)
|
|
||||||
else:
|
|
||||||
if noperm_url is None:
|
|
||||||
noperm_url = 'admin:index'
|
|
||||||
url = reverse(noperm_url, current_app=site_name)
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
def response_change(self, request, obj, continue_editing_url=None,
|
|
||||||
save_as_new_url=None, add_another_url=None,
|
|
||||||
hasperm_url=None, noperm_url=None):
|
|
||||||
"""
|
"""
|
||||||
Determines the HttpResponse for the change_view stage.
|
Determines the HttpResponse for the change_view stage.
|
||||||
|
|
||||||
:param request: HttpRequest instance.
|
|
||||||
:param obj: Object just modified.
|
|
||||||
:param continue_editing_url: URL where user will be redirected after
|
|
||||||
pressing 'Save and continue editing'.
|
|
||||||
:param save_as_new_url: URL where user will be redirected after pressing
|
|
||||||
'Save as new' (when applicable).
|
|
||||||
:param add_another_url: URL where user will be redirected after pressing
|
|
||||||
'Save and add another'.
|
|
||||||
:param hasperm_url: URL to redirect after a successful object edition when
|
|
||||||
the user has change permissions.
|
|
||||||
:param noperm_url: URL to redirect after a successful object edition when
|
|
||||||
the user has no change permissions.
|
|
||||||
"""
|
"""
|
||||||
opts = obj._meta
|
opts = self.model._meta
|
||||||
|
|
||||||
app_label = opts.app_label
|
pk_value = obj._get_pk_val()
|
||||||
model_name = opts.module_name
|
|
||||||
site_name = self.admin_site.name
|
|
||||||
verbose_name = opts.verbose_name
|
|
||||||
# Handle proxy models automatically created by .only() or .defer().
|
|
||||||
# Refs #14529
|
|
||||||
if obj._deferred:
|
|
||||||
opts_ = opts.proxy_for_model._meta
|
|
||||||
verbose_name = opts_.verbose_name
|
|
||||||
model_name = opts_.module_name
|
|
||||||
|
|
||||||
msg_dict = {'name': force_text(verbose_name), 'obj': force_text(obj)}
|
|
||||||
|
|
||||||
|
msg_dict = {'name': force_text(opts.verbose_name), 'obj': force_text(obj)}
|
||||||
if "_continue" in request.POST:
|
if "_continue" in request.POST:
|
||||||
msg = _('The %(name)s "%(obj)s" was changed successfully. You may edit it again below.') % msg_dict
|
msg = _('The %(name)s "%(obj)s" was changed successfully. You may edit it again below.') % msg_dict
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
if continue_editing_url is None:
|
if "_popup" in request.REQUEST:
|
||||||
continue_editing_url = 'admin:%s_%s_change' % (app_label, model_name)
|
return HttpResponseRedirect(request.path + "?_popup=1")
|
||||||
url = reverse(continue_editing_url, args=(quote(obj.pk),),
|
else:
|
||||||
current_app=site_name)
|
return HttpResponseRedirect(request.path)
|
||||||
if "_popup" in request.POST:
|
|
||||||
url += "?_popup=1"
|
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
elif "_saveasnew" in request.POST:
|
elif "_saveasnew" in request.POST:
|
||||||
msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % msg_dict
|
msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % msg_dict
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
if save_as_new_url is None:
|
return HttpResponseRedirect(reverse('admin:%s_%s_change' %
|
||||||
save_as_new_url = 'admin:%s_%s_change' % (app_label, model_name)
|
(opts.app_label, opts.module_name),
|
||||||
url = reverse(save_as_new_url, args=(quote(obj.pk),),
|
args=(pk_value,),
|
||||||
current_app=site_name)
|
current_app=self.admin_site.name))
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
elif "_addanother" in request.POST:
|
elif "_addanother" in request.POST:
|
||||||
msg = _('The %(name)s "%(obj)s" was changed successfully. You may add another %(name)s below.') % msg_dict
|
msg = _('The %(name)s "%(obj)s" was changed successfully. You may add another %(name)s below.') % msg_dict
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
if add_another_url is None:
|
return HttpResponseRedirect(reverse('admin:%s_%s_add' %
|
||||||
add_another_url = 'admin:%s_%s_add' % (app_label, model_name)
|
(opts.app_label, opts.module_name),
|
||||||
url = reverse(add_another_url, current_app=site_name)
|
current_app=self.admin_site.name))
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
else:
|
else:
|
||||||
msg = _('The %(name)s "%(obj)s" was changed successfully.') % msg_dict
|
msg = _('The %(name)s "%(obj)s" was changed successfully.') % msg_dict
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
# Figure out where to redirect. If the user has change permission,
|
return self.response_post_save(request, obj)
|
||||||
# redirect to the change-list page for this object. Otherwise,
|
|
||||||
# redirect to the admin index.
|
def response_post_save(self, request, obj):
|
||||||
|
"""
|
||||||
|
Figure out where to redirect after the 'Save' button has been pressed.
|
||||||
|
If the user has change permission, redirect to the change-list page for
|
||||||
|
this object. Otherwise, redirect to the admin index.
|
||||||
|
"""
|
||||||
|
opts = self.model._meta
|
||||||
if self.has_change_permission(request, None):
|
if self.has_change_permission(request, None):
|
||||||
if hasperm_url is None:
|
post_url = reverse('admin:%s_%s_changelist' %
|
||||||
hasperm_url = 'admin:%s_%s_changelist' % (app_label,
|
(opts.app_label, opts.module_name),
|
||||||
model_name)
|
current_app=self.admin_site.name)
|
||||||
url = reverse(hasperm_url, current_app=site_name)
|
|
||||||
else:
|
else:
|
||||||
if noperm_url is None:
|
post_url = reverse('admin:index',
|
||||||
noperm_url = 'admin:index'
|
current_app=self.admin_site.name)
|
||||||
url = reverse(noperm_url, current_app=site_name)
|
return HttpResponseRedirect(post_url)
|
||||||
return HttpResponseRedirect(url)
|
|
||||||
|
|
||||||
def response_action(self, request, queryset):
|
def response_action(self, request, queryset):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -153,7 +153,7 @@ class UserAdmin(admin.ModelAdmin):
|
||||||
'admin/auth/user/change_password.html',
|
'admin/auth/user/change_password.html',
|
||||||
context, current_app=self.admin_site.name)
|
context, current_app=self.admin_site.name)
|
||||||
|
|
||||||
def response_add(self, request, obj, **kwargs):
|
def response_add(self, request, obj, post_url_continue=None):
|
||||||
"""
|
"""
|
||||||
Determines the HttpResponse for the add_view stage. It mostly defers to
|
Determines the HttpResponse for the add_view stage. It mostly defers to
|
||||||
its superclass implementation but is customized because the User model
|
its superclass implementation but is customized because the User model
|
||||||
|
@ -166,7 +166,8 @@ class UserAdmin(admin.ModelAdmin):
|
||||||
# * We are adding a user in a popup
|
# * We are adding a user in a popup
|
||||||
if '_addanother' not in request.POST and '_popup' not in request.POST:
|
if '_addanother' not in request.POST and '_popup' not in request.POST:
|
||||||
request.POST['_continue'] = 1
|
request.POST['_continue'] = 1
|
||||||
return super(UserAdmin, self).response_add(request, obj, **kwargs)
|
return super(UserAdmin, self).response_add(request, obj,
|
||||||
|
post_url_continue)
|
||||||
|
|
||||||
admin.site.register(Group, GroupAdmin)
|
admin.site.register(Group, GroupAdmin)
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
|
|
|
@ -268,6 +268,12 @@ these changes.
|
||||||
* ``django.contrib.markup`` will be removed following an accelerated
|
* ``django.contrib.markup`` will be removed following an accelerated
|
||||||
deprecation.
|
deprecation.
|
||||||
|
|
||||||
|
* The value for the ``post_url_continue`` parameter in
|
||||||
|
``ModelAdmin.response_add()`` will have to be either ``None`` (to redirect
|
||||||
|
to the newly created object's edit page) or a pre-formatted url. String
|
||||||
|
formats, such as the previous default ``'../%s/'``, will not be accepted any
|
||||||
|
more.
|
||||||
|
|
||||||
1.7
|
1.7
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,41 +51,38 @@ class ActionAdmin(admin.ModelAdmin):
|
||||||
) + self.remove_url(view_name)
|
) + self.remove_url(view_name)
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Action, ActionAdmin)
|
|
||||||
|
|
||||||
|
|
||||||
class Person(models.Model):
|
class Person(models.Model):
|
||||||
nick = models.CharField(max_length=20)
|
|
||||||
|
|
||||||
|
|
||||||
class PersonAdmin(admin.ModelAdmin):
|
|
||||||
"""A custom ModelAdmin that customizes the deprecated post_url_continue
|
|
||||||
argument to response_add()"""
|
|
||||||
def response_add(self, request, obj, post_url_continue='../%s/continue/',
|
|
||||||
continue_url=None, add_url=None, hasperm_url=None,
|
|
||||||
noperm_url=None):
|
|
||||||
return super(PersonAdmin, self).response_add(request, obj,
|
|
||||||
post_url_continue,
|
|
||||||
continue_url, add_url,
|
|
||||||
hasperm_url, noperm_url)
|
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Person, PersonAdmin)
|
|
||||||
|
|
||||||
|
|
||||||
class City(models.Model):
|
|
||||||
name = models.CharField(max_length=20)
|
name = models.CharField(max_length=20)
|
||||||
|
|
||||||
|
class PersonAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
class CityAdmin(admin.ModelAdmin):
|
def response_post_save(self, request, obj):
|
||||||
"""A custom ModelAdmin that redirects to the changelist when the user
|
return HttpResponseRedirect(
|
||||||
presses the 'Save and add another' button when adding a model instance."""
|
reverse('admin:admin_custom_urls_person_history', args=[obj.pk]))
|
||||||
def response_add(self, request, obj,
|
|
||||||
add_another_url='admin:admin_custom_urls_city_changelist',
|
|
||||||
**kwargs):
|
|
||||||
return super(CityAdmin, self).response_add(request, obj,
|
|
||||||
add_another_url=add_another_url,
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(City, CityAdmin)
|
class Car(models.Model):
|
||||||
|
name = models.CharField(max_length=20)
|
||||||
|
|
||||||
|
class CarAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
|
def response_add(self, request, obj, post_url_continue=None):
|
||||||
|
return super(CarAdmin, self).response_add(
|
||||||
|
request, obj, post_url_continue=reverse('admin:admin_custom_urls_car_history', args=[obj.pk]))
|
||||||
|
|
||||||
|
|
||||||
|
class CarDeprecated(models.Model):
|
||||||
|
""" This class must be removed in Django 1.6 """
|
||||||
|
name = models.CharField(max_length=20)
|
||||||
|
|
||||||
|
class CarDeprecatedAdmin(admin.ModelAdmin):
|
||||||
|
""" This class must be removed in Django 1.6 """
|
||||||
|
def response_add(self, request, obj, post_url_continue=None):
|
||||||
|
return super(CarDeprecatedAdmin, self).response_add(
|
||||||
|
request, obj, post_url_continue='../%s/history/')
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(Action, ActionAdmin)
|
||||||
|
admin.site.register(Person, PersonAdmin)
|
||||||
|
admin.site.register(Car, CarAdmin)
|
||||||
|
admin.site.register(CarDeprecated, CarDeprecatedAdmin)
|
|
@ -1,5 +1,4 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from django.contrib.admin.util import quote
|
from django.contrib.admin.util import quote
|
||||||
|
@ -8,7 +7,7 @@ from django.template.response import TemplateResponse
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
from .models import Action, Person, City
|
from .models import Action, Person, Car, CarDeprecated
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
|
@ -86,8 +85,8 @@ class AdminCustomUrlsTest(TestCase):
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
class CustomUrlsWorkflowTests(TestCase):
|
class CustomRedirects(TestCase):
|
||||||
fixtures = ['users.json']
|
fixtures = ['users.json', 'actions.json']
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client.login(username='super', password='secret')
|
self.client.login(username='super', password='secret')
|
||||||
|
@ -95,33 +94,49 @@ class CustomUrlsWorkflowTests(TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
||||||
def test_old_argument_deprecation(self):
|
def test_post_save_redirect(self):
|
||||||
"""Test reporting of post_url_continue deprecation."""
|
"""
|
||||||
post_data = {
|
Ensures that ModelAdmin.response_post_save() controls the redirection
|
||||||
'nick': 'johndoe',
|
after the 'Save' button has been pressed.
|
||||||
}
|
Refs 8001, 18310, 19505.
|
||||||
cnt = Person.objects.count()
|
"""
|
||||||
|
post_data = { 'name': 'John Doe', }
|
||||||
|
self.assertEqual(Person.objects.count(), 0)
|
||||||
|
response = self.client.post(
|
||||||
|
reverse('admin:admin_custom_urls_person_add'), post_data)
|
||||||
|
persons = Person.objects.all()
|
||||||
|
self.assertEqual(len(persons), 1)
|
||||||
|
self.assertRedirects(
|
||||||
|
response, reverse('admin:admin_custom_urls_person_history', args=[persons[0].pk]))
|
||||||
|
|
||||||
|
def test_post_url_continue(self):
|
||||||
|
"""
|
||||||
|
Ensures that the ModelAdmin.response_add()'s parameter `post_url_continue`
|
||||||
|
controls the redirection after an object has been created.
|
||||||
|
"""
|
||||||
|
post_data = { 'name': 'SuperFast', '_continue': '1' }
|
||||||
|
self.assertEqual(Car.objects.count(), 0)
|
||||||
|
response = self.client.post(
|
||||||
|
reverse('admin:admin_custom_urls_car_add'), post_data)
|
||||||
|
cars = Car.objects.all()
|
||||||
|
self.assertEqual(len(cars), 1)
|
||||||
|
self.assertRedirects(
|
||||||
|
response, reverse('admin:admin_custom_urls_car_history', args=[cars[0].pk]))
|
||||||
|
|
||||||
|
def test_post_url_continue_string_formats(self):
|
||||||
|
"""
|
||||||
|
Ensures that string formats are accepted for post_url_continue. This
|
||||||
|
is a deprecated functionality that will be removed in Django 1.6 along
|
||||||
|
with this test.
|
||||||
|
"""
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with warnings.catch_warnings(record=True) as w:
|
||||||
warnings.simplefilter("always")
|
post_data = { 'name': 'SuperFast', '_continue': '1' }
|
||||||
response = self.client.post(reverse('admin:admin_custom_urls_person_add'), post_data)
|
self.assertEqual(Car.objects.count(), 0)
|
||||||
self.assertEqual(response.status_code, 302)
|
response = self.client.post(
|
||||||
self.assertEqual(Person.objects.count(), cnt + 1)
|
reverse('admin:admin_custom_urls_cardeprecated_add'), post_data)
|
||||||
# We should get a DeprecationWarning
|
cars = CarDeprecated.objects.all()
|
||||||
|
self.assertEqual(len(cars), 1)
|
||||||
|
self.assertRedirects(
|
||||||
|
response, reverse('admin:admin_custom_urls_cardeprecated_history', args=[cars[0].pk]))
|
||||||
self.assertEqual(len(w), 1)
|
self.assertEqual(len(w), 1)
|
||||||
self.assertTrue(isinstance(w[0].message, DeprecationWarning))
|
self.assertTrue(isinstance(w[0].message, DeprecationWarning))
|
||||||
|
|
||||||
def test_custom_add_another_redirect(self):
|
|
||||||
"""Test customizability of post-object-creation redirect URL."""
|
|
||||||
post_data = {
|
|
||||||
'name': 'Rome',
|
|
||||||
'_addanother': '1',
|
|
||||||
}
|
|
||||||
cnt = City.objects.count()
|
|
||||||
with warnings.catch_warnings(record=True) as w:
|
|
||||||
# POST to the view whose post-object-creation redir URL argument we
|
|
||||||
# are customizing (object creation)
|
|
||||||
response = self.client.post(reverse('admin:admin_custom_urls_city_add'), post_data)
|
|
||||||
self.assertEqual(City.objects.count(), cnt + 1)
|
|
||||||
# Check that it redirected to the URL we set
|
|
||||||
self.assertRedirects(response, reverse('admin:admin_custom_urls_city_changelist'))
|
|
||||||
self.assertEqual(len(w), 0) # We should get no DeprecationWarning
|
|
||||||
|
|
Loading…
Reference in New Issue