diff options
| author | Aymeric Augustin <aymeric.augustin@m4x.org> | 2013-06-18 13:07:28 -0700 |
|---|---|---|
| committer | Aymeric Augustin <aymeric.augustin@m4x.org> | 2013-06-18 13:07:28 -0700 |
| commit | 9da9b3eb0454fa3dea228b16c6e35b01db5eee69 (patch) | |
| tree | f894376d33a3576436bc333a1f07aeb0ae7d9a67 /django | |
| parent | a01b1ee6881cc4ce6c8bee771bb5b463bc16dd77 (diff) | |
| parent | c86a9b63984f6692d478f6f70e3c78de4ec41814 (diff) | |
Merge pull request #1281 from loic/ticket6903
Fixed #6903 - Preserved admin changelist filters.
Diffstat (limited to 'django')
11 files changed, 141 insertions, 42 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index be9671a76e..942385e1c6 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -13,6 +13,7 @@ from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_o model_format_dict, NestedObjects, lookup_needs_distinct) from django.contrib.admin import validation from django.contrib.admin.templatetags.admin_static import static +from django.contrib.admin.templatetags.admin_urls import add_preserved_filters from django.contrib import messages from django.views.decorators.csrf import csrf_protect from django.core.exceptions import PermissionDenied, ValidationError, FieldError @@ -33,6 +34,7 @@ from django.utils.html import escape, escapejs from django.utils.safestring import mark_safe from django.utils import six from django.utils.deprecation import RenameMethodsBase +from django.utils.http import urlencode from django.utils.text import capfirst, get_text_list from django.utils.translation import ugettext as _ from django.utils.translation import ungettext @@ -393,6 +395,7 @@ class ModelAdmin(BaseModelAdmin): save_as = False save_on_top = False paginator = Paginator + preserve_filters = True inlines = [] # Custom templates (designed to be over-ridden in subclasses) @@ -755,6 +758,27 @@ class ModelAdmin(BaseModelAdmin): """ return self.list_filter + def get_preserved_filters(self, request): + """ + Returns the preserved filters querystring. + """ + + # FIXME: We can remove that getattr as soon as #20619 is fixed. + match = getattr(request, 'resolver_match', None) + + if self.preserve_filters and match: + opts = self.model._meta + current_url = '%s:%s' % (match.namespace, match.url_name) + changelist_url = 'admin:%s_%s_changelist' % (opts.app_label, opts.model_name) + if current_url == changelist_url: + preserved_filters = request.GET.urlencode() + else: + preserved_filters = request.GET.get('_changelist_filters') + + if preserved_filters: + return urlencode({'_changelist_filters': preserved_filters}) + return '' + def construct_change_message(self, request, form, formsets): """ Construct a change message from a changed object. @@ -846,6 +870,8 @@ class ModelAdmin(BaseModelAdmin): def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): opts = self.model._meta app_label = opts.app_label + preserved_filters = self.get_preserved_filters(request) + form_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, form_url) context.update({ 'add': add, 'change': change, @@ -877,11 +903,19 @@ class ModelAdmin(BaseModelAdmin): """ opts = obj._meta pk_value = obj._get_pk_val() + preserved_filters = self.get_preserved_filters(request) msg_dict = {'name': force_text(opts.verbose_name), 'obj': force_text(obj)} # Here, we distinguish between different save types by checking for # the presence of keys in request.POST. - if "_continue" in request.POST: + if "_popup" in request.POST: + return HttpResponse( + '<!DOCTYPE html><html><head><title></title></head><body>' + '<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script></body></html>' % \ + # escape() calls force_text. + (escape(pk_value), escapejs(obj))) + + elif "_continue" in request.POST: msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % msg_dict self.message_user(request, msg, messages.SUCCESS) if post_url_continue is None: @@ -889,20 +923,16 @@ class ModelAdmin(BaseModelAdmin): (opts.app_label, opts.model_name), args=(pk_value,), current_app=self.admin_site.name) - if "_popup" in request.POST: - post_url_continue += "?_popup=1" + post_url_continue = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, post_url_continue) return HttpResponseRedirect(post_url_continue) - if "_popup" in request.POST: - return HttpResponse( - '<!DOCTYPE html><html><head><title></title></head><body>' - '<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script></body></html>' % \ - # escape() calls force_text. - (escape(pk_value), escapejs(obj))) elif "_addanother" in request.POST: msg = _('The %(name)s "%(obj)s" was added successfully. You may add another %(name)s below.') % msg_dict self.message_user(request, msg, messages.SUCCESS) - return HttpResponseRedirect(request.path) + redirect_url = request.path + redirect_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, redirect_url) + return HttpResponseRedirect(redirect_url) + else: msg = _('The %(name)s "%(obj)s" was added successfully.') % msg_dict self.message_user(request, msg, messages.SUCCESS) @@ -913,30 +943,36 @@ class ModelAdmin(BaseModelAdmin): Determines the HttpResponse for the change_view stage. """ opts = self.model._meta - pk_value = obj._get_pk_val() + preserved_filters = self.get_preserved_filters(request) msg_dict = {'name': force_text(opts.verbose_name), 'obj': force_text(obj)} if "_continue" in request.POST: msg = _('The %(name)s "%(obj)s" was changed successfully. You may edit it again below.') % msg_dict self.message_user(request, msg, messages.SUCCESS) - if "_popup" in request.REQUEST: - return HttpResponseRedirect(request.path + "?_popup=1") - else: - return HttpResponseRedirect(request.path) + redirect_url = request.path + redirect_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, redirect_url) + return HttpResponseRedirect(redirect_url) + elif "_saveasnew" in request.POST: msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % msg_dict self.message_user(request, msg, messages.SUCCESS) - return HttpResponseRedirect(reverse('admin:%s_%s_change' % - (opts.app_label, opts.model_name), - args=(pk_value,), - current_app=self.admin_site.name)) + redirect_url = reverse('admin:%s_%s_change' % + (opts.app_label, opts.model_name), + args=(pk_value,), + current_app=self.admin_site.name) + redirect_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, redirect_url) + return HttpResponseRedirect(redirect_url) + elif "_addanother" in request.POST: msg = _('The %(name)s "%(obj)s" was changed successfully. You may add another %(name)s below.') % msg_dict self.message_user(request, msg, messages.SUCCESS) - return HttpResponseRedirect(reverse('admin:%s_%s_add' % - (opts.app_label, opts.model_name), - current_app=self.admin_site.name)) + redirect_url = reverse('admin:%s_%s_add' % + (opts.app_label, opts.model_name), + current_app=self.admin_site.name) + redirect_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, redirect_url) + return HttpResponseRedirect(redirect_url) + else: msg = _('The %(name)s "%(obj)s" was changed successfully.') % msg_dict self.message_user(request, msg, messages.SUCCESS) @@ -952,6 +988,8 @@ class ModelAdmin(BaseModelAdmin): post_url = reverse('admin:%s_%s_changelist' % (opts.app_label, opts.model_name), current_app=self.admin_site.name) + preserved_filters = self.get_preserved_filters(request) + post_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, post_url) else: post_url = reverse('admin:index', current_app=self.admin_site.name) @@ -963,10 +1001,13 @@ class ModelAdmin(BaseModelAdmin): when editing an existing object. """ opts = self.model._meta + if self.has_change_permission(request, None): post_url = reverse('admin:%s_%s_changelist' % (opts.app_label, opts.model_name), current_app=self.admin_site.name) + preserved_filters = self.get_preserved_filters(request) + post_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, post_url) else: post_url = reverse('admin:index', current_app=self.admin_site.name) @@ -1122,6 +1163,7 @@ class ModelAdmin(BaseModelAdmin): 'inline_admin_formsets': inline_admin_formsets, 'errors': helpers.AdminErrorList(form, formsets), 'app_label': opts.app_label, + 'preserved_filters': self.get_preserved_filters(request), } context.update(extra_context or {}) return self.render_change_form(request, context, form_url=form_url, add=True) @@ -1214,6 +1256,7 @@ class ModelAdmin(BaseModelAdmin): 'inline_admin_formsets': inline_admin_formsets, 'errors': helpers.AdminErrorList(form, formsets), 'app_label': opts.app_label, + 'preserved_filters': self.get_preserved_filters(request), } context.update(extra_context or {}) return self.render_change_form(request, context, change=True, obj=obj, form_url=form_url) @@ -1357,11 +1400,13 @@ class ModelAdmin(BaseModelAdmin): 'cl': cl, 'media': media, 'has_add_permission': self.has_add_permission(request), + 'opts': cl.opts, 'app_label': app_label, 'action_form': action_form, 'actions_on_top': self.actions_on_top, 'actions_on_bottom': self.actions_on_bottom, 'actions_selection_counter': self.actions_selection_counter, + 'preserved_filters': self.get_preserved_filters(request), } context.update(extra_context or {}) @@ -1406,12 +1451,16 @@ class ModelAdmin(BaseModelAdmin): 'obj': force_text(obj_display)}, messages.SUCCESS) - if not self.has_change_permission(request, None): - return HttpResponseRedirect(reverse('admin:index', - current_app=self.admin_site.name)) - return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % - (opts.app_label, opts.model_name), - current_app=self.admin_site.name)) + if self.has_change_permission(request, None): + post_url = reverse('admin:%s_%s_changelist' % + (opts.app_label, opts.model_name), + current_app=self.admin_site.name) + preserved_filters = self.get_preserved_filters(request) + post_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, post_url) + else: + post_url = reverse('admin:index', + current_app=self.admin_site.name) + return HttpResponseRedirect(post_url) object_name = force_text(opts.verbose_name) @@ -1429,6 +1478,7 @@ class ModelAdmin(BaseModelAdmin): "protected": protected, "opts": opts, "app_label": app_label, + 'preserved_filters': self.get_preserved_filters(request), } context.update(extra_context or {}) @@ -1463,6 +1513,7 @@ class ModelAdmin(BaseModelAdmin): 'object': obj, 'app_label': app_label, 'opts': opts, + 'preserved_filters': self.get_preserved_filters(request), } context.update(extra_context or {}) return TemplateResponse(request, self.object_history_template or [ diff --git a/django/contrib/admin/templates/admin/change_form.html b/django/contrib/admin/templates/admin/change_form.html index 4accf80c46..d4935ed781 100644 --- a/django/contrib/admin/templates/admin/change_form.html +++ b/django/contrib/admin/templates/admin/change_form.html @@ -1,6 +1,5 @@ {% extends "admin/base_site.html" %} -{% load i18n admin_static admin_modify %} -{% load admin_urls %} +{% load i18n admin_urls admin_static admin_modify %} {% block extrahead %}{{ block.super }} <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script> @@ -29,7 +28,10 @@ {% if change %}{% if not is_popup %} <ul class="object-tools"> {% block object-tools-items %} - <li><a href="{% url opts|admin_urlname:'history' original.pk|admin_urlquote %}" class="historylink">{% trans "History" %}</a></li> + <li> + {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} + <a href="{% add_preserved_filters history_url %}" class="historylink">{% trans "History" %}</a> + </li> {% if has_absolute_url %}<li><a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%} {% endblock %} </ul> diff --git a/django/contrib/admin/templates/admin/change_list.html b/django/contrib/admin/templates/admin/change_list.html index 200b4cd74d..15583d1a8c 100644 --- a/django/contrib/admin/templates/admin/change_list.html +++ b/django/contrib/admin/templates/admin/change_list.html @@ -1,6 +1,5 @@ {% extends "admin/base_site.html" %} -{% load i18n admin_static admin_list %} -{% load admin_urls %} +{% load i18n admin_urls admin_static admin_list %} {% block extrastyle %} {{ block.super }} @@ -54,7 +53,8 @@ <ul class="object-tools"> {% block object-tools-items %} <li> - <a href="{% url cl.opts|admin_urlname:'add' %}{% if is_popup %}?_popup=1{% endif %}" class="addlink"> + {% url cl.opts|admin_urlname:'add' as add_url %} + <a href="{% add_preserved_filters add_url is_popup %}" class="addlink"> {% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %} </a> </li> diff --git a/django/contrib/admin/templates/admin/delete_confirmation.html b/django/contrib/admin/templates/admin/delete_confirmation.html index c1a711534d..3ed60807b7 100644 --- a/django/contrib/admin/templates/admin/delete_confirmation.html +++ b/django/contrib/admin/templates/admin/delete_confirmation.html @@ -1,6 +1,5 @@ {% extends "admin/base_site.html" %} -{% load i18n %} -{% load admin_urls %} +{% load i18n admin_urls %} {% block breadcrumbs %} <div class="breadcrumbs"> diff --git a/django/contrib/admin/templates/admin/delete_selected_confirmation.html b/django/contrib/admin/templates/admin/delete_selected_confirmation.html index 608052d6f5..36527dcbf5 100644 --- a/django/contrib/admin/templates/admin/delete_selected_confirmation.html +++ b/django/contrib/admin/templates/admin/delete_selected_confirmation.html @@ -1,6 +1,5 @@ {% extends "admin/base_site.html" %} -{% load i18n l10n %} -{% load admin_urls %} +{% load i18n l10n admin_urls %} {% block breadcrumbs %} <div class="breadcrumbs"> diff --git a/django/contrib/admin/templates/admin/object_history.html b/django/contrib/admin/templates/admin/object_history.html index 870c4648a6..c750ff14b3 100644 --- a/django/contrib/admin/templates/admin/object_history.html +++ b/django/contrib/admin/templates/admin/object_history.html @@ -1,6 +1,5 @@ {% extends "admin/base_site.html" %} -{% load i18n %} -{% load admin_urls %} +{% load i18n admin_urls %} {% block breadcrumbs %} <div class="breadcrumbs"> diff --git a/django/contrib/admin/templates/admin/submit_line.html b/django/contrib/admin/templates/admin/submit_line.html index 38a97a1c6a..52baed3ffa 100644 --- a/django/contrib/admin/templates/admin/submit_line.html +++ b/django/contrib/admin/templates/admin/submit_line.html @@ -1,7 +1,10 @@ {% load i18n admin_urls %} <div class="submit-row"> {% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" />{% endif %} -{% if show_delete_link %}<p class="deletelink-box"><a href="{% url opts|admin_urlname:'delete' original.pk|admin_urlquote %}" class="deletelink">{% trans "Delete" %}</a></p>{% endif %} +{% if show_delete_link %} + {% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %} + <p class="deletelink-box"><a href="{% add_preserved_filters delete_url %}" class="deletelink">{% trans "Delete" %}</a></p> +{% endif %} {% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" />{%endif%} {% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %} {% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" />{% endif %} diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 965352e0f5..e81b13cda4 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import datetime +from django.contrib.admin.templatetags.admin_urls import add_preserved_filters from django.contrib.admin.util import (lookup_field, display_for_field, display_for_value, label_for_field) from django.contrib.admin.views.main import (ALL_VAR, EMPTY_CHANGELIST_VALUE, @@ -217,6 +218,7 @@ def items_for_result(cl, result, form): table_tag = {True:'th', False:'td'}[first] first = False url = cl.url_for_result(result) + url = add_preserved_filters({'preserved_filters': cl.preserved_filters, 'opts': cl.opts}, url) # Convert the pk to something that can be used in Javascript. # Problem cases are long ints (23L) and non-ASCII strings. if cl.to_field: diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py index cecc6ed6c4..98ac1a657e 100644 --- a/django/contrib/admin/templatetags/admin_modify.py +++ b/django/contrib/admin/templatetags/admin_modify.py @@ -37,7 +37,8 @@ def submit_row(context): not is_popup and (not save_as or context['add']), 'show_save_and_continue': not is_popup and context['has_change_permission'], 'is_popup': is_popup, - 'show_save': True + 'show_save': True, + 'preserved_filters': context.get('preserved_filters'), } if context.get('original') is not None: ctx['original'] = context['original'] diff --git a/django/contrib/admin/templatetags/admin_urls.py b/django/contrib/admin/templatetags/admin_urls.py index bca95d92ae..bb1b16883d 100644 --- a/django/contrib/admin/templatetags/admin_urls.py +++ b/django/contrib/admin/templatetags/admin_urls.py @@ -1,8 +1,17 @@ +from django.utils.http import urlencode + +try: + from urllib.parse import parse_qsl, urlparse, urlunparse +except ImportError: + from urlparse import parse_qsl, urlparse, urlunparse + from django import template from django.contrib.admin.util import quote +from django.core.urlresolvers import resolve, Resolver404 register = template.Library() + @register.filter def admin_urlname(value, arg): return 'admin:%s_%s_%s' % (value.app_label, value.model_name, arg) @@ -11,3 +20,36 @@ def admin_urlname(value, arg): @register.filter def admin_urlquote(value): return quote(value) + + +@register.simple_tag(takes_context=True) +def add_preserved_filters(context, url, popup=False): + opts = context.get('opts') + preserved_filters = context.get('preserved_filters') + + parsed_url = list(urlparse(url)) + parsed_qs = dict(parse_qsl(parsed_url[4])) + merged_qs = dict() + + if opts and preserved_filters: + preserved_filters = dict(parse_qsl(preserved_filters)) + + try: + match = resolve(url) + except Resolver404: + pass + else: + current_url = '%s:%s' % (match.namespace, match.url_name) + changelist_url = 'admin:%s_%s_changelist' % (opts.app_label, opts.model_name) + if changelist_url == current_url and '_changelist_filters' in preserved_filters: + preserved_filters = dict(parse_qsl(preserved_filters['_changelist_filters'])) + + merged_qs.update(preserved_filters) + + if popup: + merged_qs['_popup'] = 1 + + merged_qs.update(parsed_qs) + + parsed_url[4] = urlencode(merged_qs) + return urlunparse(parsed_url) diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 8ea7e10fc0..f676706a89 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -59,6 +59,7 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)): self.list_per_page = list_per_page self.list_max_show_all = list_max_show_all self.model_admin = model_admin + self.preserved_filters = model_admin.get_preserved_filters(request) # Get search parameters from the query string. try: |
