diff options
| -rw-r--r-- | django/contrib/admin/options.py | 107 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/change_form.html | 8 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/change_list.html | 6 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/delete_confirmation.html | 3 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/delete_selected_confirmation.html | 3 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/object_history.html | 3 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/submit_line.html | 5 | ||||
| -rw-r--r-- | django/contrib/admin/templatetags/admin_list.py | 2 | ||||
| -rw-r--r-- | django/contrib/admin/templatetags/admin_modify.py | 3 | ||||
| -rw-r--r-- | django/contrib/admin/templatetags/admin_urls.py | 42 | ||||
| -rw-r--r-- | django/contrib/admin/views/main.py | 1 | ||||
| -rw-r--r-- | docs/ref/contrib/admin/index.txt | 8 | ||||
| -rw-r--r-- | docs/releases/1.6.txt | 15 | ||||
| -rw-r--r-- | tests/admin_views/tests.py | 153 |
14 files changed, 317 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: diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 513499c0c1..90ef68837a 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -870,6 +870,14 @@ subclass:: ``prepopulated_fields`` doesn't accept ``DateTimeField``, ``ForeignKey``, nor ``ManyToManyField`` fields. +.. attribute:: ModelAdmin.preserve_filters + + .. versionadded:: 1.6 + + The admin now preserves filters on the list view after creating, editing + or deleting an object. You can restore the previous behavior of clearing + filters by setting this attribute to ``False``. + .. attribute:: ModelAdmin.radio_fields By default, Django's admin uses a select-box interface (<select>) for diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index 8dea37d2da..bd6255eae6 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -325,6 +325,11 @@ Minor features :ref:`see the updated recommendation <raising-validation-error>` for raising a ``ValidationError``. +* :class:`~django.contrib.admin.ModelAdmin` now preserves filters on the list view + after creating, editing or deleting an object. It's possible to restore the previous + behavior of clearing filters by setting the + :attr:`~django.contrib.admin.ModelAdmin.preserve_filters` attribute to ``False``. + Backwards incompatible changes in 1.6 ===================================== @@ -634,6 +639,16 @@ will render something like: If you want to keep the current behavior of rendering ``label_tag`` without the ``label_suffix``, instantiate the form ``label_suffix=''``. +Admin views ``_changelist_filters`` GET parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To achieve preserving and restoring list view filters, admin views now +pass around the `_changelist_filters` GET parameter. It's important that you +account for that change if you have custom admin templates or if your tests +rely on the previous URLs. If you want to revert to the original behavior you +can set the +:attr:`~django.contrib.admin.ModelAdmin.preserve_filters` attribute to ``False``. + Miscellaneous ~~~~~~~~~~~~~ diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 9a7ec7d331..44d56fd04a 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -4157,3 +4157,156 @@ class AdminUserMessageTest(TestCase): self.assertContains(response, '<li class="extra_tag info">Test tags</li>', html=True) + + +@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class AdminKeepChangeListFiltersTests(TestCase): + urls = "admin_views.urls" + fixtures = ['admin-views-users.xml'] + + def setUp(self): + self.client.login(username='super', password='secret') + + def tearDown(self): + self.client.logout() + + def get_changelist_filters_querystring(self): + return urlencode({ + 'is_superuser__exact': 0, + 'is_staff__exact': 0, + }) + + def get_preserved_filters_querystring(self): + return urlencode({ + '_changelist_filters': self.get_changelist_filters_querystring() + }) + + def get_sample_user_id(self): + return 104 + + def get_changelist_url(self): + return '%s?%s' % ( + reverse('admin:auth_user_changelist'), + self.get_changelist_filters_querystring(), + ) + + def get_add_url(self): + return '%s?%s' % ( + reverse('admin:auth_user_add'), + self.get_preserved_filters_querystring(), + ) + + def get_change_url(self, user_id=None): + if user_id is None: + user_id = self.get_sample_user_id() + return "%s?%s" % ( + reverse('admin:auth_user_change', args=(user_id,)), + self.get_preserved_filters_querystring(), + ) + + def get_history_url(self, user_id=None): + if user_id is None: + user_id = self.get_sample_user_id() + return "%s?%s" % ( + reverse('admin:auth_user_history', args=(user_id,)), + self.get_preserved_filters_querystring(), + ) + + def get_delete_url(self, user_id=None): + if user_id is None: + user_id = self.get_sample_user_id() + return "%s?%s" % ( + reverse('admin:auth_user_delete', args=(user_id,)), + self.get_preserved_filters_querystring(), + ) + + def test_changelist_view(self): + response = self.client.get(self.get_changelist_url()) + self.assertEqual(response.status_code, 200) + + # Check the `change_view` link has the correct querystring. + detail_link = """<a href="%s">joepublic</a>""" % self.get_change_url() + self.assertContains(response, detail_link, count=1) + + def test_change_view(self): + # Get the `change_view`. + response = self.client.get(self.get_change_url()) + self.assertEqual(response.status_code, 200) + + # Check the form action. + form_action = """<form enctype="multipart/form-data" action="?%s" method="post" id="user_form">""" % self.get_preserved_filters_querystring() + self.assertContains(response, form_action, count=1) + + # Check the history link. + history_link = """<a href="%s" class="historylink">History</a>""" % self.get_history_url() + self.assertContains(response, history_link, count=1) + + # Check the delete link. + delete_link = """<a href="%s" class="deletelink">Delete</a>""" % (self.get_delete_url()) + self.assertContains(response, delete_link, count=1) + + # Test redirect on "Save". + post_data = { + 'username': 'joepublic', + 'last_login_0': '2007-05-30', + 'last_login_1': '13:20:10', + 'date_joined_0': '2007-05-30', + 'date_joined_1': '13:20:10', + } + + post_data['_save'] = 1 + response = self.client.post(self.get_change_url(), data=post_data) + self.assertRedirects(response, self.get_changelist_url()) + post_data.pop('_save') + + # Test redirect on "Save and continue". + post_data['_continue'] = 1 + response = self.client.post(self.get_change_url(), data=post_data) + self.assertRedirects(response, self.get_change_url()) + post_data.pop('_continue') + + # Test redirect on "Save and add new". + post_data['_addanother'] = 1 + response = self.client.post(self.get_change_url(), data=post_data) + self.assertRedirects(response, self.get_add_url()) + post_data.pop('_addanother') + + def test_add_view(self): + # Get the `add_view`. + response = self.client.get(self.get_add_url()) + self.assertEqual(response.status_code, 200) + + # Check the form action. + form_action = """<form enctype="multipart/form-data" action="?%s" method="post" id="user_form">""" % self.get_preserved_filters_querystring() + self.assertContains(response, form_action, count=1) + + # Test redirect on "Save". + post_data = { + 'username': 'dummy', + 'password1': 'test', + 'password2': 'test', + } + + post_data['_save'] = 1 + response = self.client.post(self.get_add_url(), data=post_data) + self.assertRedirects(response, self.get_change_url(self.get_sample_user_id() + 1)) + post_data.pop('_save') + + # Test redirect on "Save and continue". + post_data['username'] = 'dummy2' + post_data['_continue'] = 1 + response = self.client.post(self.get_add_url(), data=post_data) + self.assertRedirects(response, self.get_change_url(self.get_sample_user_id() + 2)) + post_data.pop('_continue') + + # Test redirect on "Save and add new". + post_data['username'] = 'dummy3' + post_data['_addanother'] = 1 + response = self.client.post(self.get_add_url(), data=post_data) + self.assertRedirects(response, self.get_add_url()) + post_data.pop('_addanother') + + def test_delete_view(self): + # Test redirect on "Delete". + response = self.client.post(self.get_delete_url(), {'post': 'yes'}) + self.assertRedirects(response, self.get_changelist_url()) |
