Skip to content
Browse files

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

  • Loading branch information...
1 parent 0ba1272 commit af3285ca12604ddde1b7f2af65fed1f8634e340c Batiste Bieler committed Mar 3, 2011
Showing with 153 additions and 22 deletions.
  1. +49 −22 django/contrib/admin/options.py
  2. +104 −0 tests/regressiontests/admin_views/tests.py
View
71 django/contrib/admin/options.py
@@ -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,40 +708,72 @@ 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.
"""
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
View
104 tests/regressiontests/admin_views/tests.py
@@ -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'

0 comments on commit af3285c

Please sign in to comment.
Something went wrong with that request. Please try again.