Ticket #6903: 6903.r12351.current.diff

File 6903.r12351.current.diff, 13.3 KB (added by ramusus, 14 years ago)

Without new changes, just for working with 12351 revision in development trunk

  • django/django/contrib/admin/options.py

     
    1616from django.utils.datastructures import SortedDict
    1717from django.utils.functional import update_wrapper
    1818from django.utils.html import escape
     19from django.utils.http import urlquote
    1920from django.utils.safestring import mark_safe
    2021from django.utils.functional import curry
    2122from django.utils.text import capfirst, get_text_list
     
    3132# returns the <ul> class for a given radio_admin field
    3233get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
    3334
     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.
     37RETURN_GET_PARAM = '_return_to'
     38
    3439class IncorrectLookupParameters(Exception):
    3540    pass
    3641
     
    638643        pk_value = obj._get_pk_val()
    639644
    640645        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
    642648        # 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.
    643652        if request.POST.has_key("_continue"):
    644653            self.message_user(request, msg + ' ' + _("You may edit it again below."))
    645654            if request.POST.has_key("_popup"):
    646655                post_url_continue += "?_popup=1"
    647656            return HttpResponseRedirect(post_url_continue % pk_value)
    648657
     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.
    649661        if request.POST.has_key("_popup"):
    650662            return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
    651663                # escape() calls force_unicode.
    652664                (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.
    653668        elif request.POST.has_key("_addanother"):
    654669            self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
    655670            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.
    656676        else:
    657677            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):
    663681                post_url = '../'
    664682            else:
    665683                post_url = '../../../'
     
    673691        pk_value = obj._get_pk_val()
    674692
    675693        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"
    676698        if request.POST.has_key("_continue"):
    677699            self.message_user(request, msg + ' ' + _("You may edit it again below."))
    678700            if request.REQUEST.has_key('_popup'):
    679701                return HttpResponseRedirect(request.path + "?_popup=1")
    680702            else:
    681703                return HttpResponseRedirect(request.path)
     704
     705        # "Save as a new object"
    682706        elif request.POST.has_key("_saveasnew"):
    683707            msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': obj}
    684708            self.message_user(request, msg)
    685709            return HttpResponseRedirect("../%s/" % pk_value)
     710
     711        # "Save and add another"
    686712        elif request.POST.has_key("_addanother"):
    687713            self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
    688714            return HttpResponseRedirect("../add/")
     715
     716        # "Save"
    689717        else:
    690718            self.message_user(request, msg)
    691             return HttpResponseRedirect("../")
     719            post_url = request.GET.get(RETURN_GET_PARAM, '../')
     720            return HttpResponseRedirect(post_url)
    692721
    693722    def response_action(self, request, queryset):
    694723        """
     
    766795
    767796        ModelForm = self.get_form(request)
    768797        formsets = []
     798        return_to = request.GET.get(RETURN_GET_PARAM, None)
    769799        if request.method == 'POST':
    770800            form = ModelForm(request.POST, request.FILES)
    771801            if form.is_valid():
     
    816846                                  queryset=inline.queryset(request))
    817847                formsets.append(formset)
    818848
     849        if return_to:
     850            form_url = form_url + '?%s=%s' % (RETURN_GET_PARAM, urlquote(return_to))
     851
    819852        adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)),
    820853            self.prepopulated_fields, self.get_readonly_fields(request),
    821854            model_admin=self)
     
    864897
    865898        ModelForm = self.get_form(request, obj)
    866899        formsets = []
     900        return_to = request.GET.get(RETURN_GET_PARAM, None)
    867901        if request.method == 'POST':
    868902            form = ModelForm(request.POST, request.FILES, instance=obj)
    869903            if form.is_valid():
     
    907941                                  queryset=inline.queryset(request))
    908942                formsets.append(formset)
    909943
     944        if return_to:
     945            form_url = '?%s=%s' % (RETURN_GET_PARAM, urlquote(return_to))
     946        else:
     947            form_url = ''
     948
    910949        adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj),
    911950            self.prepopulated_fields, self.get_readonly_fields(request, obj),
    912951            model_admin=self)
     
    934973            'app_label': opts.app_label,
    935974        }
    936975        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)
    938977
    939978    @csrf_protect
    940979    def changelist_view(self, request, extra_context=None):
  • django/django/contrib/admin/views/main.py

     
    11from django.contrib.admin.filterspecs import FilterSpec
    2 from django.contrib.admin.options import IncorrectLookupParameters
     2from django.contrib.admin.options import IncorrectLookupParameters, RETURN_GET_PARAM
    33from django.contrib.admin.util import quote
    44from django.core.paginator import Paginator, InvalidPage
    55from django.db import models
    66from django.db.models.query import QuerySet
    77from django.utils.encoding import force_unicode, smart_str
    88from django.utils.translation import ugettext
    9 from django.utils.http import urlencode
     9from django.utils.http import urlencode, urlquote
    1010import operator
    1111
    1212try:
     
    7070        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))
    7171        self.filter_specs, self.has_filters = self.get_filters(request)
    7272        self.pk_attname = self.lookup_opts.pk.attname
     73        self.this_clist_url = request.get_full_path()
    7374
    7475    def get_filters(self, request):
    7576        filter_specs = []
     
    240241        return qs
    241242
    242243    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

     
    1414from django.utils import formats
    1515from django.utils.cache import get_max_age
    1616from django.utils.html import escape
     17from django.utils.http import urlquote
    1718from django.utils.translation import get_date_formats
    1819
    1920# local test models
     
    2526
    2627
    2728class 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']
    2930
    3031    # Store the bit of the URL where the admin is registered as a class
    3132    # variable. That way we can test a second AdminSite just by subclassing
     
    213214        response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'color__id__exact': 'StringNotInteger!'})
    214215        self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit)
    215216
     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
    216269    def testLogoutAndPasswordChangeURLs(self):
    217270        response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit)
    218271        self.failIf('<a href="/test_admin/%s/logout/">' % self.urlbit not in response.content)
     
    663716
    664717    def test_changelist_to_changeform_link(self):
    665718        "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))
    668722        self.assertContains(response, should_contain)
    669723
    670724    def test_recentactions_link(self):
Back to Top