Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge the patch, makes all tests pass and document the remaining test.

  • Loading branch information...
commit af3285ca12604ddde1b7f2af65fed1f8634e340c 1 parent 0ba1272
Batiste Bieler authored
71 django/contrib/admin/options.py
View
@@ -13,8 +13,9 @@
from django.db.models.related import RelatedObject
from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
from django.db.models.sql.constants import LOOKUP_SEP, QUERY_TERMS
-from django.http import Http404, HttpResponse, HttpResponseRedirect
+from django.db import models, transaction
from django.shortcuts import get_object_or_404, render_to_response
+from django.http import Http404, HttpResponse, HttpResponseRedirect, QueryDict
from django.utils.decorators import method_decorator
from django.utils.datastructures import SortedDict
from django.utils.functional import update_wrapper
@@ -707,6 +708,30 @@ def render_change_form(self, request, context, add=False, change=False, form_url
"admin/change_form.html"
], context, context_instance=context_instance)
+ def url_with_querystring(self, request, url=None):
+ """
+ Returns a URL with the query string preserved from the current request.
+ """
+ if url is None:
+ url = request.path
+ querystring = request.GET.copy()
+
+ # If the new URL has an existing query string, set (or override
+ # existing) values with the new ones explicitly provided.
+ if '?' in url:
+ url, new_qs = url.split('?', 1)
+ for key, value in QueryDict(new_qs):
+ querystring.setlist(key, value)
+
+ # Preserve the admin's "_popup" variable which may have originate in
+ # the POST QueryDict.
+ if '_popup' in request.POST or '_popup' in request.REQUEST:
+ querystring['_popup'] = 1
+
+ if querystring:
+ url = '%s?%s' % (url, querystring.urlencode())
+ return url
+
def response_add(self, request, obj, post_url_continue='../%s/'):
"""
Determines the HttpResponse for the add_view stage.
@@ -714,33 +739,41 @@ def response_add(self, request, obj, post_url_continue='../%s/'):
opts = obj._meta
pk_value = obj._get_pk_val()
+ # Handle proxy models automatically created by .only() or .defer()
+ verbose_name = opts.verbose_name
+ if obj._deferred:
+ opts_ = opts.proxy_for_model._meta
+ verbose_name = opts_.verbose_name
+
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)}
# Here, we distinguish between different save types by checking for
# the presence of keys in request.POST.
+
if "_continue" in request.POST:
self.message_user(request, msg + ' ' + _("You may edit it again below."))
- if "_popup" in request.POST:
- post_url_continue += "?_popup=1"
- return HttpResponseRedirect(post_url_continue % pk_value)
-
- if "_popup" in request.POST:
+ url = self.url_with_querystring(request,
+ post_url_continue % pk_value)
+ elif "_addanother" in request.POST:
+ self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
+ url = self.url_with_querystring(request)
+ elif "_saveasnew" in request.POST:
+ msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(verbose_name), 'obj': obj}
+ self.message_user(request, msg)
+ return HttpResponseRedirect(self.url_with_querystring(request, "../%s/" % pk_value))
+ elif "_popup" in request.POST:
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
# escape() calls force_unicode.
(escape(pk_value), escapejs(obj)))
- elif "_addanother" in request.POST:
- self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
- return HttpResponseRedirect(request.path)
else:
self.message_user(request, msg)
-
# Figure out where to redirect. If the user has change permission,
# redirect to the change-list page for this object. Otherwise,
# redirect to the admin index.
if self.has_change_permission(request, None):
- post_url = '../'
+ url = '../'
else:
- post_url = '../../../'
- return HttpResponseRedirect(post_url)
+ url = '../../../'
+ return HttpResponseRedirect(url)
def response_change(self, request, obj):
"""
@@ -759,17 +792,10 @@ def response_change(self, request, obj):
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(verbose_name), 'obj': force_unicode(obj)}
if "_continue" in request.POST:
self.message_user(request, msg + ' ' + _("You may edit it again below."))
- if "_popup" in request.REQUEST:
- return HttpResponseRedirect(request.path + "?_popup=1")
- else:
- return HttpResponseRedirect(request.path)
- elif "_saveasnew" in request.POST:
- msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(verbose_name), 'obj': obj}
- self.message_user(request, msg)
- return HttpResponseRedirect("../%s/" % pk_value)
+ return HttpResponseRedirect(self.url_with_querystring(request, request.path))
elif "_addanother" in request.POST:
self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(verbose_name)))
- return HttpResponseRedirect("../add/")
+ return HttpResponseRedirect(self.url_with_querystring(request, "../add/"))
else:
self.message_user(request, msg)
# Figure out where to redirect. If the user has change permission,
@@ -780,6 +806,7 @@ def response_change(self, request, obj):
else:
return HttpResponseRedirect('../../../')
+
def response_action(self, request, queryset):
"""
Handle an admin action. This is called if a request is POSTed to the
104 tests/regressiontests/admin_views/tests.py
View
@@ -2110,6 +2110,110 @@ def test_add_model_modeladmin_only_qs(self):
# Message should contain non-ugly model name. Instance representation is set by model's __unicode__()
self.assertContains(response, '<li class="info">The cover letter &quot;John Doe II&quot; was changed successfully.</li>')
+
+class AdminPreserveQSTest(TestCase):
+ """
+ Neither adding nor changing a model will preserve the request query string
+ after a redirection if the standard "save" button is used within the admin.
+ """
+ action = None
+ preserves_qs = False
+ fixtures = ['admin-views-users.xml']
+
+ def setUp(self):
+ self.client.login(username='super', password='secret')
+ self.data = {'title': "I Could Go Anywhere", 'content': "Versatile"}
+ article = Article.objects.create(date=datetime.datetime.now(),
+ **self.data)
+ self.data.update({'date_0': u"2008-03-18", 'date_1': u"11:54:58"})
+ if self.action:
+ self.data[self.action] = True
+ self.pk = article.id
+
+ def check_redirect(self, response):
+ self.assertEqual(response.status_code, 302)
+ location = urlparse.urlparse(response['Location'])
+ return urlparse.parse_qs(location.query)
+
+ def test_response_add(self):
+ url = '/test_admin/admin/admin_views/article/add/'
+
+ response = self.client.post('%s?test=1' % url, self.data)
+ parsed_query_string = self.check_redirect(response)
+ if self.preserves_qs:
+ self.assertTrue('test' in parsed_query_string)
+ else:
+ self.assertFalse('test' in parsed_query_string)
+
+ # Test the popup redirection.
+ self.data['_popup'] = 1
+ response = self.client.post(url, self.data)
+ if self.preserves_qs:
+ parsed_query_string = self.check_redirect(response)
+ self.assertTrue('_popup' in parsed_query_string)
+ else:
+ self.assertContains(response,
+ '<script type="text/javascript">opener.dismissAddAnotherPopup')
+
+ # Test the popup redirection togethre with 2 other GET parameters.
+ response = self.client.post('%s?test=1&extra=1' % url, self.data)
+ if self.preserves_qs:
+ parsed_query_string = self.check_redirect(response)
+ self.assertTrue('test' in parsed_query_string)
+ self.assertTrue('extra' in parsed_query_string)
+ self.assertTrue('_popup' in parsed_query_string)
+ else:
+ self.assertContains(response,
+ '<script type="text/javascript">opener.dismissAddAnotherPopup')
+
+ def test_response_change(self):
+ url = '/test_admin/admin/admin_views/article/%d/' % self.pk
+
+ response = self.client.post('%s?test=1' % url, self.data)
+ parsed_query_string = self.check_redirect(response)
+ if self.preserves_qs:
+ self.assertTrue('test' in parsed_query_string)
+ else:
+ self.assertFalse('test' in parsed_query_string)
+
+ response = self.client.post(url, self.data)
+ parsed_query_string = self.check_redirect(response)
+ self.assertFalse('test' in parsed_query_string)
+
+
+class AdminPreserveQSOnContinueTest(AdminPreserveQSTest):
+ """
+ Adding or changing a model should preserve the request query string after
+ the redirection if the "save and continue" button is used within the admin
+ (Ticket #12241).
+ """
+ action = '_continue'
+ preserves_qs = True
+
+
+class AdminPreserveQSOnAddAnotherTest(AdminPreserveQSTest):
+ """
+ Adding or changing a model should preserve the request query string after
+ the redirection if the "add another" button is used within the admin
+ (Ticket #12241).
+ """
+ action = '_addanother'
+ preserves_qs = True
+
+
+class AdminPreserveQSOnSaveAsNewTest(AdminPreserveQSTest):
+ """
+ Adding or changing a model should preserve the request query string after
+ the redirection if the "save as new" button is used within the admin
+ (Ticket #12241).
+
+ Only the change view has the "save as new" button, but then
+ the admin use the response_add for the answer.
+ """
+ action = '_saveasnew'
+ preserves_qs = True
+
+
class AdminInlineFileUploadTest(TestCase):
fixtures = ['admin-views-users.xml', 'admin-views-actions.xml']
urlbit = 'admin'
Please sign in to comment.
Something went wrong with that request. Please try again.