Ticket #6903: 6903.r12351.current.diff
File 6903.r12351.current.diff, 13.3 KB (added by , 15 years ago) |
---|
-
django/django/contrib/admin/options.py
16 16 from django.utils.datastructures import SortedDict 17 17 from django.utils.functional import update_wrapper 18 18 from django.utils.html import escape 19 from django.utils.http import urlquote 19 20 from django.utils.safestring import mark_safe 20 21 from django.utils.functional import curry 21 22 from django.utils.text import capfirst, get_text_list … … 31 32 # returns the <ul> class for a given radio_admin field 32 33 get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '') 33 34 35 # GET parameter for the URL to return to after change/add views. This lets the 36 # admin save the state of the changelist page through the change/add page. 37 RETURN_GET_PARAM = '_return_to' 38 34 39 class IncorrectLookupParameters(Exception): 35 40 pass 36 41 … … 638 643 pk_value = obj._get_pk_val() 639 644 640 645 msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)} 641 # Here, we distinguish between different save types by checking for 646 647 # Below, we distinguish between different save types by checking for 642 648 # the presence of keys in request.POST. 649 650 # The user clicked "save and continue editing". Redirect to admin URL 651 # for this object, possibly in popup mode if needed. 643 652 if request.POST.has_key("_continue"): 644 653 self.message_user(request, msg + ' ' + _("You may edit it again below.")) 645 654 if request.POST.has_key("_popup"): 646 655 post_url_continue += "?_popup=1" 647 656 return HttpResponseRedirect(post_url_continue % pk_value) 648 657 658 # This was a popup and the user clicked the simple "save" button. 659 # Return a little script which populates the calling page with the 660 # saved key. 649 661 if request.POST.has_key("_popup"): 650 662 return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \ 651 663 # escape() calls force_unicode. 652 664 (escape(pk_value), escape(obj))) 665 666 # The user clicked "save and add another", so redirect back to the bare 667 # "add" page, which will be request.path. 653 668 elif request.POST.has_key("_addanother"): 654 669 self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) 655 670 return HttpResponseRedirect(request.path) 671 672 # Just a regular "save" click. IF we've been pased an explicit return 673 # URL in GET, go there. Otherwise, redirect to the plain changelist. 674 # If the user doesn't have changelist permission, just redirect back 675 # to the main admin index. 656 676 else: 657 677 self.message_user(request, msg) 658 659 # Figure out where to redirect. If the user has change permission, 660 # redirect to the change-list page for this object. Otherwise, 661 # redirect to the admin index. 662 if self.has_change_permission(request, None): 678 if RETURN_GET_PARAM in request.GET: 679 post_url = request.GET[RETURN_GET_PARAM] 680 elif self.has_change_permission(request, None): 663 681 post_url = '../' 664 682 else: 665 683 post_url = '../../../' … … 673 691 pk_value = obj._get_pk_val() 674 692 675 693 msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)} 694 695 # Similar redirecting logic to response_add, above. 696 697 # "Save and continue editing" 676 698 if request.POST.has_key("_continue"): 677 699 self.message_user(request, msg + ' ' + _("You may edit it again below.")) 678 700 if request.REQUEST.has_key('_popup'): 679 701 return HttpResponseRedirect(request.path + "?_popup=1") 680 702 else: 681 703 return HttpResponseRedirect(request.path) 704 705 # "Save as a new object" 682 706 elif request.POST.has_key("_saveasnew"): 683 707 msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': obj} 684 708 self.message_user(request, msg) 685 709 return HttpResponseRedirect("../%s/" % pk_value) 710 711 # "Save and add another" 686 712 elif request.POST.has_key("_addanother"): 687 713 self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) 688 714 return HttpResponseRedirect("../add/") 715 716 # "Save" 689 717 else: 690 718 self.message_user(request, msg) 691 return HttpResponseRedirect("../") 719 post_url = request.GET.get(RETURN_GET_PARAM, '../') 720 return HttpResponseRedirect(post_url) 692 721 693 722 def response_action(self, request, queryset): 694 723 """ … … 766 795 767 796 ModelForm = self.get_form(request) 768 797 formsets = [] 798 return_to = request.GET.get(RETURN_GET_PARAM, None) 769 799 if request.method == 'POST': 770 800 form = ModelForm(request.POST, request.FILES) 771 801 if form.is_valid(): … … 816 846 queryset=inline.queryset(request)) 817 847 formsets.append(formset) 818 848 849 if return_to: 850 form_url = form_url + '?%s=%s' % (RETURN_GET_PARAM, urlquote(return_to)) 851 819 852 adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)), 820 853 self.prepopulated_fields, self.get_readonly_fields(request), 821 854 model_admin=self) … … 864 897 865 898 ModelForm = self.get_form(request, obj) 866 899 formsets = [] 900 return_to = request.GET.get(RETURN_GET_PARAM, None) 867 901 if request.method == 'POST': 868 902 form = ModelForm(request.POST, request.FILES, instance=obj) 869 903 if form.is_valid(): … … 907 941 queryset=inline.queryset(request)) 908 942 formsets.append(formset) 909 943 944 if return_to: 945 form_url = '?%s=%s' % (RETURN_GET_PARAM, urlquote(return_to)) 946 else: 947 form_url = '' 948 910 949 adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj), 911 950 self.prepopulated_fields, self.get_readonly_fields(request, obj), 912 951 model_admin=self) … … 934 973 'app_label': opts.app_label, 935 974 } 936 975 context.update(extra_context or {}) 937 return self.render_change_form(request, context, change=True, obj=obj)976 return self.render_change_form(request, context, change=True, form_url=form_url, obj=obj) 938 977 939 978 @csrf_protect 940 979 def changelist_view(self, request, extra_context=None): -
django/django/contrib/admin/views/main.py
1 1 from django.contrib.admin.filterspecs import FilterSpec 2 from django.contrib.admin.options import IncorrectLookupParameters 2 from django.contrib.admin.options import IncorrectLookupParameters, RETURN_GET_PARAM 3 3 from django.contrib.admin.util import quote 4 4 from django.core.paginator import Paginator, InvalidPage 5 5 from django.db import models 6 6 from django.db.models.query import QuerySet 7 7 from django.utils.encoding import force_unicode, smart_str 8 8 from django.utils.translation import ugettext 9 from django.utils.http import urlencode 9 from django.utils.http import urlencode, urlquote 10 10 import operator 11 11 12 12 try: … … 70 70 self.title = (self.is_popup and ugettext('Select %s') % force_unicode(self.opts.verbose_name) or ugettext('Select %s to change') % force_unicode(self.opts.verbose_name)) 71 71 self.filter_specs, self.has_filters = self.get_filters(request) 72 72 self.pk_attname = self.lookup_opts.pk.attname 73 self.this_clist_url = request.get_full_path() 73 74 74 75 def get_filters(self, request): 75 76 filter_specs = [] … … 240 241 return qs 241 242 242 243 def url_for_result(self, result): 243 return "%s/ " % quote(getattr(result, self.pk_attname))244 return "%s/?%s=%s" % (quote(getattr(result, self.pk_attname)), RETURN_GET_PARAM, urlquote(self.this_clist_url)) -
django/tests/regressiontests/admin_views/tests.py
14 14 from django.utils import formats 15 15 from django.utils.cache import get_max_age 16 16 from django.utils.html import escape 17 from django.utils.http import urlquote 17 18 from django.utils.translation import get_date_formats 18 19 19 20 # local test models … … 25 26 26 27 27 28 class AdminViewBasicTest(TestCase): 28 fixtures = ['admin-views-users.xml', 'admin-views-colors.xml', 'admin-views-fabrics.xml' ]29 fixtures = ['admin-views-users.xml', 'admin-views-colors.xml', 'admin-views-fabrics.xml', 'admin-views-things.xml'] 29 30 30 31 # Store the bit of the URL where the admin is registered as a class 31 32 # variable. That way we can test a second AdminSite just by subclassing … … 213 214 response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'color__id__exact': 'StringNotInteger!'}) 214 215 self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit) 215 216 217 def testFilterPreservedChangeListLinks(self): 218 """Link to model changeform in changelist should contain filter-preserving GET data if a filter is active""" 219 changelist_url = '/test_admin/%s/admin_views/thing/' % self.urlbit 220 filter_spec = '?color__id__exact=1' 221 should_contain = """<tr class="row1"><th><a href="1/?_return_to=%s%s">Ball</a></th></tr>""" % (changelist_url, urlquote(filter_spec)) 222 # get the changelist page with one active filter 223 response = self.client.get(changelist_url, {'color__id__exact': 1}) 224 # links in the changelist table should preserve the filter 225 self.failUnlessEqual(response.status_code, 200) 226 self.assertContains(response, should_contain) 227 228 def testFilterPreservedEditForm(self): 229 """Model changeform post handler URL should contain filter specified when clicking the changelist link.""" 230 changelist_url = '/test_admin/%s/admin_views/thing/' % self.urlbit 231 filter_spec = '?color__id__exact=1' 232 edit_form_url = '%s1/?_return_to=%s%s' % (changelist_url, changelist_url, urlquote(filter_spec)) 233 should_contain = '<form enctype="multipart/form-data" action="?_return_to=%s%s" method="post" id="thing_form">' % (changelist_url, urlquote(filter_spec)) 234 response = self.client.get(edit_form_url) 235 self.failUnlessEqual(response.status_code, 200) 236 self.assertContains(response, should_contain) 237 238 def testFilterPreservedAfterEditSave(self): 239 """Redirection target after 'Save' is clicked in model changeform should be the changelist""" 240 changelist_url = '/test_admin/%s/admin_views/thing' % self.urlbit 241 filter_spec = '?color__id__exact=1' 242 action = '%s/1/?_return_to=%s/%s' % (changelist_url, changelist_url, urlquote(filter_spec)) 243 post_data = { 244 # Model fields 245 "title": u"Car", 246 "color": u"1", 247 } 248 response = self.client.post(action, post_data) 249 # It should redirect to the changelist 250 self.assertRedirects(response, '%s/%s' % (changelist_url, filter_spec)) 251 252 def testFilterIgnoredEditAddAnother(self): 253 """Redirection target after 'Save and add another' is clicked in model changeform should be an add form""" 254 base_url = '/test_admin/%s/admin_views/thing' % self.urlbit 255 filter_spec = '?color__id__exact=1' 256 post_to = '%s/1/?_return_to=%s/%s' % (base_url, base_url, urlquote(filter_spec)) 257 post_data = { 258 # Model fields 259 "title": u"Car", 260 "color": u"1", 261 # Form button used 262 "_addanother": u"" 263 } 264 response = self.client.post(post_to, post_data) 265 # It should redirect to an add form 266 #self.failUnlessEqual(response.status_code, 302) 267 self.assertRedirects(response, '%s/%s' % (base_url, 'add/')) 268 216 269 def testLogoutAndPasswordChangeURLs(self): 217 270 response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit) 218 271 self.failIf('<a href="/test_admin/%s/logout/">' % self.urlbit not in response.content) … … 663 716 664 717 def test_changelist_to_changeform_link(self): 665 718 "The link from the changelist referring to the changeform of the object should be quoted" 666 response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/') 667 should_contain = """<th><a href="%s/">%s</a></th></tr>""" % (quote(self.pk), escape(self.pk)) 719 changelist_url = '/test_admin/admin/admin_views/modelwithstringprimarykey/' 720 response = self.client.get(changelist_url) 721 should_contain = """<th><a href="%s/?_return_to=%s">%s</a></th></tr>""" % (quote(self.pk), changelist_url, escape(self.pk)) 668 722 self.assertContains(response, should_contain) 669 723 670 724 def test_recentactions_link(self):