Ticket #6903: 6903-r11616.diff
File 6903-r11616.diff, 14.6 KB (added by , 15 years ago) |
---|
-
django/contrib/admin/options.py
diff -r 1c267332e8fc django/contrib/admin/options.py
a b 14 14 from django.utils.datastructures import SortedDict 15 15 from django.utils.functional import update_wrapper 16 16 from django.utils.html import escape 17 from django.utils.http import urlquote 17 18 from django.utils.safestring import mark_safe 18 19 from django.utils.functional import curry 19 20 from django.utils.text import capfirst, get_text_list … … 29 30 # returns the <ul> class for a given radio_admin field 30 31 get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '') 31 32 33 # GET parameter for the URL to return to after change/add views. This lets the 34 # admin save the state of the changelist page through the change/add page. 35 RETURN_GET_PARAM = '_return_to' 36 32 37 class IncorrectLookupParameters(Exception): 33 38 pass 34 39 … … 597 602 pk_value = obj._get_pk_val() 598 603 599 604 msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)} 600 # Here, we distinguish between different save types by checking for 605 606 # Below, we distinguish between different save types by checking for 601 607 # the presence of keys in request.POST. 608 609 # The user clicked "save and continue editing". Redirect to admin URL 610 # for this object, possibly in popup mode if needed. 602 611 if request.POST.has_key("_continue"): 603 612 self.message_user(request, msg + ' ' + _("You may edit it again below.")) 604 613 if request.POST.has_key("_popup"): 605 614 post_url_continue += "?_popup=1" 606 615 return HttpResponseRedirect(post_url_continue % pk_value) 607 616 617 # This was a popup and the user clicked the simple "save" button. 618 # Return a little script which populates the calling page with the 619 # saved key. 608 620 if request.POST.has_key("_popup"): 609 621 return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \ 610 622 # escape() calls force_unicode. 611 623 (escape(pk_value), escape(obj))) 624 625 # The user clicked "save and add another", so redirect back to the bare 626 # "add" page, which will be request.path. 612 627 elif request.POST.has_key("_addanother"): 613 628 self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) 614 629 return HttpResponseRedirect(request.path) 630 631 # Just a regular "save" click. IF we've been pased an explicit return 632 # URL in GET, go there. Otherwise, redirect to the plain changelist. 633 # If the user doesn't have changelist permission, just redirect back 634 # to the main admin index. 615 635 else: 616 636 self.message_user(request, msg) 617 618 # Figure out where to redirect. If the user has change permission, 619 # redirect to the change-list page for this object. Otherwise, 620 # redirect to the admin index. 621 if self.has_change_permission(request, None): 637 if RETURN_GET_PARAM in request.GET: 638 post_url = request.GET[RETURN_GET_PARAM] 639 elif self.has_change_permission(request, None): 622 640 post_url = '../' 623 641 else: 624 642 post_url = '../../../' … … 632 655 pk_value = obj._get_pk_val() 633 656 634 657 msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)} 658 659 # Similar redirecting logic to response_add, above. 660 661 # "Save and continue editing" 635 662 if request.POST.has_key("_continue"): 636 663 self.message_user(request, msg + ' ' + _("You may edit it again below.")) 637 664 if request.REQUEST.has_key('_popup'): 638 665 return HttpResponseRedirect(request.path + "?_popup=1") 639 666 else: 640 667 return HttpResponseRedirect(request.path) 668 669 # "Save as a new object" 641 670 elif request.POST.has_key("_saveasnew"): 642 671 msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': obj} 643 672 self.message_user(request, msg) 644 673 return HttpResponseRedirect("../%s/" % pk_value) 674 675 # "Save and add another" 645 676 elif request.POST.has_key("_addanother"): 646 677 self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) 647 678 return HttpResponseRedirect("../add/") 679 680 # "Save" 648 681 else: 649 682 self.message_user(request, msg) 650 return HttpResponseRedirect("../") 651 683 post_url = request.GET.get(RETURN_GET_PARAM, '../') 684 return HttpResponseRedirect(post_url) 685 652 686 def response_action(self, request, queryset): 653 687 """ 654 688 Handle an admin action. This is called if a request is POSTed to the … … 711 747 712 748 ModelForm = self.get_form(request) 713 749 formsets = [] 750 return_to = request.GET.get(RETURN_GET_PARAM, None) 714 751 if request.method == 'POST': 715 752 form = ModelForm(request.POST, request.FILES) 716 753 if form.is_valid(): … … 758 795 prefix = "%s-%s" % (prefix, prefixes[prefix]) 759 796 formset = FormSet(instance=self.model(), prefix=prefix) 760 797 formsets.append(formset) 761 798 799 if return_to: 800 form_url = form_url + '?%s=%s' % (RETURN_GET_PARAM, urlquote(return_to)) 801 762 802 adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)), self.prepopulated_fields) 763 803 media = self.media + adminForm.media 764 804 … … 808 848 809 849 ModelForm = self.get_form(request, obj) 810 850 formsets = [] 851 return_to = request.GET.get(RETURN_GET_PARAM, None) 811 852 if request.method == 'POST': 812 853 form = ModelForm(request.POST, request.FILES, instance=obj) 813 854 if form.is_valid(): … … 846 887 prefix = "%s-%s" % (prefix, prefixes[prefix]) 847 888 formset = FormSet(instance=obj, prefix=prefix) 848 889 formsets.append(formset) 849 890 891 if return_to: 892 form_url = '?%s=%s' % (RETURN_GET_PARAM, urlquote(return_to)) 893 else: 894 form_url = '' 895 850 896 adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj), self.prepopulated_fields) 851 897 media = self.media + adminForm.media 852 898 … … 870 916 'app_label': opts.app_label, 871 917 } 872 918 context.update(extra_context or {}) 873 return self.render_change_form(request, context, change=True, obj=obj)919 return self.render_change_form(request, context, change=True, form_url=form_url, obj=obj) 874 920 change_view = transaction.commit_on_success(change_view) 875 921 876 922 def changelist_view(self, request, extra_context=None): -
django/contrib/admin/views/main.py
diff -r 1c267332e8fc django/contrib/admin/views/main.py
a b 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)) -
new file tests/regressiontests/admin_views/fixtures/admin-views-things.xml
diff -r 1c267332e8fc tests/regressiontests/admin_views/fixtures/admin-views-things.xml
- + 1 <?xml version="1.0" encoding="utf-8"?> 2 <django-objects version="1.0"> 3 <object pk="1" model="admin_views.thing"> 4 <field type="CharField" name="title">Ball</field> 5 <field to="admin_views.color" name="color" rel="ManyToOneRel">1</field> 6 </object> 7 <object pk="2" model="admin_views.thing"> 8 <field type="CharField" name="title">Carrot</field> 9 <field to="admin_views.color" name="color" rel="ManyToOneRel">2</field> 10 </object> 11 </django-objects> 12 -
tests/regressiontests/admin_views/tests.py
diff -r 1c267332e8fc tests/regressiontests/admin_views/tests.py
a b 12 12 from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME 13 13 from django.utils.cache import get_max_age 14 14 from django.utils.html import escape 15 from django.utils.http import urlquote 15 16 16 17 # local test models 17 18 from models import Article, BarAccount, CustomArticle, EmptyModel, \ 18 19 ExternalSubscriber, FooAccount, Gallery, ModelWithStringPrimaryKey, \ 19 20 Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, \ 20 21 Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, \ 21 22 Category 22 23 23 24 try: 24 25 set 25 26 except NameError: 26 27 from sets import Set as set 27 28 28 29 class AdminViewBasicTest(TestCase): 29 fixtures = ['admin-views-users.xml', 'admin-views-colors.xml', 'admin-views-fabrics.xml' ]30 31 # Store the bit of the URL where the admin is registered as a class 30 fixtures = ['admin-views-users.xml', 'admin-views-colors.xml', 'admin-views-fabrics.xml', 'admin-views-things.xml'] 31 32 # Store the bit of the URL where the admin is registered as a class 32 33 # variable. That way we can test a second AdminSite just by subclassing 33 34 # this test case and changing urlbit. 34 35 urlbit = 'admin' … … 204 205 self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit) 205 206 response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'color__id__exact': 'StringNotInteger!'}) 206 207 self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit) 208 209 def testFilterPreservedChangeListLinks(self): 210 """Link to model changeform in changelist should contain filter-preserving GET data if a filter is active""" 211 changelist_url = '/test_admin/%s/admin_views/thing/' % self.urlbit 212 filter_spec = '?color__id__exact=1' 213 should_contain = """<tr class="row1"><th><a href="1/?_return_to=%s%s">Ball</a></th></tr>""" % (changelist_url, urlquote(filter_spec)) 214 # get the changelist page with one active filter 215 response = self.client.get(changelist_url, {'color__id__exact': 1}) 216 # links in the changelist table should preserve the filter 217 self.failUnlessEqual(response.status_code, 200) 218 self.assertContains(response, should_contain) 219 220 def testFilterPreservedEditForm(self): 221 """Model changeform post handler URL should contain filter specified when clicking the changelist link.""" 222 changelist_url = '/test_admin/%s/admin_views/thing/' % self.urlbit 223 filter_spec = '?color__id__exact=1' 224 edit_form_url = '%s1/?_return_to=%s%s' % (changelist_url, changelist_url, urlquote(filter_spec)) 225 should_contain = '<form enctype="multipart/form-data" action="?_return_to=%s%s" method="post" id="thing_form">' % (changelist_url, urlquote(filter_spec)) 226 response = self.client.get(edit_form_url) 227 self.failUnlessEqual(response.status_code, 200) 228 self.assertContains(response, should_contain) 229 230 def testFilterPreservedAfterEditSave(self): 231 """Redirection target after 'Save' is clicked in model changeform should be the changelist""" 232 changelist_url = '/test_admin/%s/admin_views/thing' % self.urlbit 233 filter_spec = '?color__id__exact=1' 234 action = '%s/1/?_return_to=%s/%s' % (changelist_url, changelist_url, urlquote(filter_spec)) 235 post_data = { 236 # Model fields 237 "title": u"Car", 238 "color": u"1", 239 } 240 response = self.client.post(action, post_data) 241 # It should redirect to the changelist 242 self.assertRedirects(response, '%s/%s' % (changelist_url, filter_spec)) 243 244 def testFilterIgnoredEditAddAnother(self): 245 """Redirection target after 'Save and add another' is clicked in model changeform should be an add form""" 246 base_url = '/test_admin/%s/admin_views/thing' % self.urlbit 247 filter_spec = '?color__id__exact=1' 248 post_to = '%s/1/?_return_to=%s/%s' % (base_url, base_url, urlquote(filter_spec)) 249 post_data = { 250 # Model fields 251 "title": u"Car", 252 "color": u"1", 253 # Form button used 254 "_addanother": u"" 255 } 256 response = self.client.post(post_to, post_data) 257 # It should redirect to an add form 258 #self.failUnlessEqual(response.status_code, 302) 259 self.assertRedirects(response, '%s/%s' % (base_url, 'add/')) 207 260 208 261 def testLogoutAndPasswordChangeURLs(self): 209 262 response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit) … … 618 771 619 772 def test_changelist_to_changeform_link(self): 620 773 "The link from the changelist referring to the changeform of the object should be quoted" 621 response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/') 622 should_contain = """<th><a href="%s/">%s</a></th></tr>""" % (quote(self.pk), escape(self.pk)) 774 changelist_url = '/test_admin/admin/admin_views/modelwithstringprimarykey/' 775 response = self.client.get(changelist_url) 776 should_contain = """<th><a href="%s/?_return_to=%s">%s</a></th></tr>""" % (quote(self.pk), changelist_url, escape(self.pk)) 623 777 self.assertContains(response, should_contain) 624 778 625 779 def test_recentactions_link(self):