Ticket #15294: 15294.15.diff

File 15294.15.diff, 130.0 KB (added by Florian Apolloner, 11 years ago)

all tests working, hopefully even for ramiro -- https://github.com/apollo13/django/compare/master...t15294

  • django/contrib/admin/options.py

    diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
    index c9f2562..73e62e3 100644
    a b from django.contrib import messages 
    1111from django.views.decorators.csrf import csrf_protect
    1212from django.core.exceptions import PermissionDenied, ValidationError
    1313from django.core.paginator import Paginator
     14from django.core.urlresolvers import reverse
    1415from django.db import models, transaction, router
    1516from django.db.models.related import RelatedObject
    1617from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
    class ModelAdmin(BaseModelAdmin): 
    776777            # redirect to the change-list page for this object. Otherwise,
    777778            # redirect to the admin index.
    778779            if self.has_change_permission(request, None):
    779                 post_url = '../'
     780                post_url = reverse('admin:%s_%s_changelist' %
     781                                   (opts.app_label, opts.module_name),
     782                                   current_app=self.admin_site.name)
    780783            else:
    781                 post_url = '../../../'
     784                post_url = reverse('admin:index',
     785                                   current_app=self.admin_site.name)
    782786            return HttpResponseRedirect(post_url)
    783787
    784788    def response_change(self, request, obj):
    class ModelAdmin(BaseModelAdmin): 
    787791        """
    788792        opts = obj._meta
    789793
    790         # Handle proxy models automatically created by .only() or .defer()
     794        # Handle proxy models automatically created by .only() or .defer().
     795        # Refs #14529
    791796        verbose_name = opts.verbose_name
     797        module_name = opts.module_name
    792798        if obj._deferred:
    793799            opts_ = opts.proxy_for_model._meta
    794800            verbose_name = opts_.verbose_name
     801            module_name = opts_.module_name
    795802
    796803        pk_value = obj._get_pk_val()
    797804
    class ModelAdmin(BaseModelAdmin): 
    805812        elif "_saveasnew" in request.POST:
    806813            msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(verbose_name), 'obj': obj}
    807814            self.message_user(request, msg)
    808             return HttpResponseRedirect("../%s/" % pk_value)
     815            return HttpResponseRedirect(reverse('admin:%s_%s_change' %
     816                                        (opts.app_label, module_name),
     817                                        args=(pk_value,),
     818                                        current_app=self.admin_site.name))
    809819        elif "_addanother" in request.POST:
    810820            self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(verbose_name)))
    811             return HttpResponseRedirect("../add/")
     821            return HttpResponseRedirect(reverse('admin:%s_%s_add' %
     822                                        (opts.app_label, module_name),
     823                                        current_app=self.admin_site.name))
    812824        else:
    813825            self.message_user(request, msg)
    814826            # Figure out where to redirect. If the user has change permission,
    815827            # redirect to the change-list page for this object. Otherwise,
    816828            # redirect to the admin index.
    817829            if self.has_change_permission(request, None):
    818                 return HttpResponseRedirect('../')
     830                post_url = reverse('admin:%s_%s_changelist' %
     831                                   (opts.app_label, module_name),
     832                                   current_app=self.admin_site.name)
    819833            else:
    820                 return HttpResponseRedirect('../../../')
     834                post_url = reverse('admin:index',
     835                                   current_app=self.admin_site.name)
     836            return HttpResponseRedirect(post_url)
    821837
    822838    def response_action(self, request, queryset):
    823839        """
    class ModelAdmin(BaseModelAdmin): 
    9901006            raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)})
    9911007
    9921008        if request.method == 'POST' and "_saveasnew" in request.POST:
    993             return self.add_view(request, form_url='../add/')
     1009            return self.add_view(request, form_url=reverse('admin:%s_%s_add' %
     1010                                    (opts.app_label, opts.module_name),
     1011                                    current_app=self.admin_site.name))
    9941012
    9951013        ModelForm = self.get_form(request, obj)
    9961014        formsets = []
    class ModelAdmin(BaseModelAdmin): 
    12461264            self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)})
    12471265
    12481266            if not self.has_change_permission(request, None):
    1249                 return HttpResponseRedirect("../../../../")
    1250             return HttpResponseRedirect("../../")
     1267                return HttpResponseRedirect(reverse('admin:index',
     1268                                                    current_app=self.admin_site.name))
     1269            return HttpResponseRedirect(reverse('admin:%s_%s_changelist' %
     1270                                        (opts.app_label, opts.module_name),
     1271                                        current_app=self.admin_site.name))
    12511272
    12521273        object_name = force_unicode(opts.verbose_name)
    12531274
    class ModelAdmin(BaseModelAdmin): 
    12921313            'module_name': capfirst(force_unicode(opts.verbose_name_plural)),
    12931314            'object': obj,
    12941315            'app_label': app_label,
     1316            'opts': opts,
    12951317        }
    12961318        context.update(extra_context or {})
    12971319        return TemplateResponse(request, self.object_history_template or [
  • django/contrib/admin/sites.py

    diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
    index 49c1e78..3ba9314 100644
    a b class AdminSite(object): 
    339339                # Check whether user has any perm for this module.
    340340                # If so, add the module to the model_list.
    341341                if True in perms.values():
     342                    info = (app_label, model._meta.module_name)
    342343                    model_dict = {
    343344                        'name': capfirst(model._meta.verbose_name_plural),
    344                         'admin_url': mark_safe('%s/%s/' % (app_label, model.__name__.lower())),
     345                        'admin_url': reverse('admin:%s_%s_changelist' % info, current_app=self.name),
     346                        'add_url': reverse('admin:%s_%s_add' % info, current_app=self.name),
    345347                        'perms': perms,
    346348                    }
    347349                    if app_label in app_dict:
    class AdminSite(object): 
    349351                    else:
    350352                        app_dict[app_label] = {
    351353                            'name': app_label.title(),
    352                             'app_url': app_label + '/',
     354                            'app_url': reverse('admin:app_list', kwargs={'app_label': app_label}, current_app=self.name),
    353355                            'has_module_perms': has_module_perms,
    354356                            'models': [model_dict],
    355357                        }
    class AdminSite(object): 
    383385                    # Check whether user has any perm for this module.
    384386                    # If so, add the module to the model_list.
    385387                    if True in perms.values():
     388                        info = (app_label, model._meta.module_name)
    386389                        model_dict = {
    387390                            'name': capfirst(model._meta.verbose_name_plural),
    388                             'admin_url': '%s/' % model.__name__.lower(),
     391                            'admin_url': reverse('admin:%s_%s_changelist' % info, current_app=self.name),
     392                            'add_url': reverse('admin:%s_%s_add' % info, current_app=self.name),
    389393                            'perms': perms,
    390394                        }
    391395                        if app_dict:
  • django/contrib/admin/templates/admin/500.html

    diff --git a/django/contrib/admin/templates/admin/500.html b/django/contrib/admin/templates/admin/500.html
    index b30e431..eeb9e8d 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
    34
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="/">{% trans "Home" %}</a> &rsaquo; {% trans "Server error" %}</div>{% endblock %}
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; {% trans 'Server error' %}
     9</div>
     10{% endblock %}
    511
    612{% block title %}{% trans 'Server error (500)' %}{% endblock %}
    713
  • django/contrib/admin/templates/admin/app_index.html

    diff --git a/django/contrib/admin/templates/admin/app_index.html b/django/contrib/admin/templates/admin/app_index.html
    index 120433d..4616b16 100644
    a b  
    1 {% extends "admin/index.html" %}
    2 {% load i18n %}
     1{% extends "admin/index.html" %}
     2{% load i18n %}
     3{% load url from future %}
    34
    45{% if not is_popup %}
    5 
    66{% block breadcrumbs %}
    7 <div class="breadcrumbs"><a href="../">
    8 {% trans "Home" %}</a> &rsaquo;
     7<div class="breadcrumbs">
     8<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     9&rsaquo;
    910{% for app in app_list %}
    1011{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}
    11 {% endfor %}</div>{% endblock %}
    12 
    13 {% endif %}
     12{% endfor %}
     13</div>
     14{% endblock %}
     15{% endif %}
    1416
    15 {% block sidebar %}{% endblock %}
    16  No newline at end of file
     17{% block sidebar %}{% endblock %}
  • django/contrib/admin/templates/admin/auth/user/change_password.html

    diff --git a/django/contrib/admin/templates/admin/auth/user/change_password.html b/django/contrib/admin/templates/admin/auth/user/change_password.html
    index b18b0aa..32dbcd9 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n admin_static admin_modify %}
    33{% load url from future %}
     4{% load admin_urls %}
     5
    46{% block extrahead %}{{ block.super }}
    57{% url 'admin:jsi18n' as jsi18nurl %}
    68<script type="text/javascript" src="{{ jsi18nurl|default:"../../../../jsi18n/" }}"></script>
    79{% endblock %}
    810{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
    911{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
    10 {% block breadcrumbs %}{% if not is_popup %}
    11 <div class="breadcrumbs">
    12      <a href="../../../../">{% trans "Home" %}</a> &rsaquo;
    13      <a href="../../../">{{ opts.app_label|capfirst|escape }}</a> &rsaquo;
    14      <a href="../../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
    15      <a href="../">{{ original|truncatewords:"18" }}</a> &rsaquo;
    16     {% trans 'Change password' %}
     12{% if not is_popup %}
     13{% block breadcrumbs %}
     14<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     15&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_label|capfirst|escape }}</a>
     16&rsaquo; <a href="{% url opts|admin_url:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
     17&rsaquo; <a href="{% url opts|admin_url:'changelist' %}{{ original.pk }}">{{ original|truncatewords:"18" }}</a>
     18&rsaquo; {% trans 'Change password' %}
    1719</div>
    18 {% endif %}{% endblock %}
     20{% endblock %}
     21{% endif %}
    1922{% block content %}<div id="content-main">
    2023<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %}
    2124<div>
  • django/contrib/admin/templates/admin/base.html

    diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html
    index 4b3c429..3b50adc 100644
    a b  
    3232                {% if docsroot %}
    3333                    <a href="{{ docsroot }}">{% trans 'Documentation' %}</a> /
    3434                {% endif %}
    35                 <a href="{% url 'admin:password_change' %}">
    36                 {% trans 'Change password' %}</a> /
    37                 <a href="{% url 'admin:logout' %}">
    38                 {% trans 'Log out' %}</a>
     35                <a href="{% url 'admin:password_change' %}">{% trans 'Change password' %}</a> /
     36                <a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>
    3937            {% endblock %}
    4038        </div>
    4139        {% endif %}
    4240        {% block nav-global %}{% endblock %}
    4341    </div>
    4442    <!-- END Header -->
    45     {% block breadcrumbs %}<div class="breadcrumbs"><a href="/">{% trans 'Home' %}</a>{% if title %} &rsaquo; {{ title }}{% endif %}</div>{% endblock %}
     43    {% block breadcrumbs %}
     44    <div class="breadcrumbs">
     45    <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     46    {% if title %} &rsaquo; {{ title }}{% endif %}
     47    </div>
     48    {% endblock %}
    4649    {% endif %}
    4750
    4851    {% block messages %}
  • django/contrib/admin/templates/admin/change_form.html

    diff --git a/django/contrib/admin/templates/admin/change_form.html b/django/contrib/admin/templates/admin/change_form.html
    index 56661e9..71230e4 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n admin_static admin_modify %}
    33{% load url from future %}
     4{% load admin_urls %}
    45
    56{% block extrahead %}{{ block.super }}
    67{% url 'admin:jsi18n' as jsi18nurl %}
     
    1415
    1516{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
    1617
    17 {% block breadcrumbs %}{% if not is_popup %}
    18 <div class="breadcrumbs">
    19      <a href="../../../">{% trans "Home" %}</a> &rsaquo;
    20      <a href="../../">{{ app_label|capfirst|escape }}</a> &rsaquo;
    21      {% if has_change_permission %}<a href="../">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %} &rsaquo;
    22      {% if add %}{% trans "Add" %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
     18{% if not is_popup %}
     19{% block breadcrumbs %}
     20<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     21&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst|escape }}</a>
     22&rsaquo; {% if has_change_permission %}<a href="{% url opts|admin_url:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
     23&rsaquo; {% if add %}{% trans 'Add' %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
    2324</div>
    24 {% endif %}{% endblock %}
     25{% endblock %}
     26{% endif %}
    2527
    2628{% block content %}<div id="content-main">
    2729{% block object-tools %}
  • django/contrib/admin/templates/admin/change_list.html

    diff --git a/django/contrib/admin/templates/admin/change_list.html b/django/contrib/admin/templates/admin/change_list.html
    index 24c6d8c..6a92496 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n admin_static admin_list %}
    33{% load url from future %}
     4{% load admin_urls %}
     5
    46{% block extrastyle %}
    57  {{ block.super }}
    68  <link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}" />
     
    3638{% block bodyclass %}change-list{% endblock %}
    3739
    3840{% if not is_popup %}
    39   {% block breadcrumbs %}
    40     <div class="breadcrumbs">
    41       <a href="../../">
    42         {% trans "Home" %}
    43       </a>
    44        &rsaquo;
    45        <a href="../">
    46          {{ app_label|capfirst }}
    47       </a>
    48       &rsaquo;
    49       {{ cl.opts.verbose_name_plural|capfirst }}
    50     </div>
    51   {% endblock %}
     41{% block breadcrumbs %}
     42<div class="breadcrumbs">
     43<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     44&rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ app_label|capfirst|escape }}</a>
     45&rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}
     46</div>
     47{% endblock %}
    5248{% endif %}
    5349
    5450{% block coltype %}flex{% endblock %}
     
    6056        <ul class="object-tools">
    6157          {% block object-tools-items %}
    6258            <li>
    63               <a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">
     59              <a href="{% url cl.opts|admin_url:'add' %}{% if is_popup %}?_popup=1{% endif %}" class="addlink">
    6460                {% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}
    6561              </a>
    6662            </li>
  • django/contrib/admin/templates/admin/delete_confirmation.html

    diff --git a/django/contrib/admin/templates/admin/delete_confirmation.html b/django/contrib/admin/templates/admin/delete_confirmation.html
    index 0e6d47e..a4acb95 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
     4{% load admin_urls %}
    35
    46{% block breadcrumbs %}
    57<div class="breadcrumbs">
    6      <a href="../../../../">{% trans "Home" %}</a> &rsaquo;
    7      <a href="../../../">{{ app_label|capfirst }}</a> &rsaquo;
    8      <a href="../../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
    9      <a href="../">{{ object|truncatewords:"18" }}</a> &rsaquo;
    10     {% trans 'Delete' %}
     8<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     9&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst }}</a>
     10&rsaquo; <a href="{% url opts|admin_url:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a>
     11&rsaquo; <a href="{% url opts|admin_url:'changelist' %}{{ object.pk }}">{{ object|truncatewords:"18" }}</a>
     12&rsaquo; {% trans 'Delete' %}
    1113</div>
    1214{% endblock %}
    1315
  • django/contrib/admin/templates/admin/delete_selected_confirmation.html

    diff --git a/django/contrib/admin/templates/admin/delete_selected_confirmation.html b/django/contrib/admin/templates/admin/delete_selected_confirmation.html
    index 127519b..04a2ae7 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n l10n %}
     3{% load url from future %}
     4{% load admin_urls %}
    35
    46{% block breadcrumbs %}
    57<div class="breadcrumbs">
    6      <a href="../../">{% trans "Home" %}</a> &rsaquo;
    7      <a href="../">{{ app_label|capfirst }}</a> &rsaquo;
    8      <a href="./">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
    9     {% trans 'Delete multiple objects' %}
     8<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     9&rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
     10&rsaquo; <a href="{% url opts|admin_url:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
     11&rsaquo; {% trans 'Delete multiple objects' %}
    1012</div>
    1113{% endblock %}
    1214
  • django/contrib/admin/templates/admin/index.html

    diff --git a/django/contrib/admin/templates/admin/index.html b/django/contrib/admin/templates/admin/index.html
    index 7164220..86c795b 100644
    a b  
    2626            {% endif %}
    2727
    2828            {% if model.perms.add %}
    29                 <td><a href="{{ model.admin_url }}add/" class="addlink">{% trans 'Add' %}</a></td>
     29                <td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
    3030            {% else %}
    3131                <td>&nbsp;</td>
    3232            {% endif %}
  • django/contrib/admin/templates/admin/invalid_setup.html

    diff --git a/django/contrib/admin/templates/admin/invalid_setup.html b/django/contrib/admin/templates/admin/invalid_setup.html
    index f09b316..c2df4df 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
    34
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {{ title }}</div>{% endblock %}
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; {{ title }}
     9</div>
     10{% endblock %}
    511
    612{% block content %}
    713<p>{% trans "Something's wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user." %}</p>
  • django/contrib/admin/templates/admin/object_history.html

    diff --git a/django/contrib/admin/templates/admin/object_history.html b/django/contrib/admin/templates/admin/object_history.html
    index 5ae7847..e39471b 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
     4{% load admin_urls %}
    35
    46{% block breadcrumbs %}
    57<div class="breadcrumbs">
    6     <a href="../../../../">{% trans 'Home' %}</a> &rsaquo;
    7     <a href="../../../">{{ app_label|capfirst }}</a> &rsaquo;
    8     <a href="../../">{{ module_name }}</a> &rsaquo;
    9     <a href="../">{{ object|truncatewords:"18" }}</a> &rsaquo;
    10     {% trans 'History' %}
     8<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     9&rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
     10&rsaquo; <a href="{% url opts|admin_url:'changelist' %}">{{ module_name }}</a>
     11&rsaquo; <a href="{% url opts|admin_url:'changelist' %}{{ object.pk }}">{{ object|truncatewords:"18" }}</a>
     12&rsaquo; {% trans 'History' %}
    1113</div>
    1214{% endblock %}
    1315
  • django/contrib/admin/templates/registration/logged_out.html

    diff --git a/django/contrib/admin/templates/registration/logged_out.html b/django/contrib/admin/templates/registration/logged_out.html
    index d339ef0..e95d864 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
    34
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a></div>{% endblock %}
     5{% block breadcrumbs %}<div class="breadcrumbs"><a href="{% url 'admin:index' %}">{% trans 'Home' %}</a></div>{% endblock %}
    56
    67{% block content %}
    78
    89<p>{% trans "Thanks for spending some quality time with the Web site today." %}</p>
    910
    10 <p><a href="../">{% trans 'Log in again' %}</a></p>
     11<p><a href="{% url 'admin:index' %}">{% trans 'Log in again' %}</a></p>
    1112
    1213{% endblock %}
  • django/contrib/admin/templates/registration/password_change_done.html

    diff --git a/django/contrib/admin/templates/registration/password_change_done.html b/django/contrib/admin/templates/registration/password_change_done.html
    index 0c0690d..863fc96 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
    33{% load url from future %}
    4 {% block userlinks %}{% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %}{% trans 'Change password' %} / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %}
    5 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %}
     4{% block userlinks %}{% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %}{% trans 'Change password' %} / <a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>{% endblock %}
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; {% trans 'Password change' %}
     9</div>
     10{% endblock %}
    611
    712{% block title %}{% trans 'Password change successful' %}{% endblock %}
    813
  • django/contrib/admin/templates/registration/password_change_form.html

    diff --git a/django/contrib/admin/templates/registration/password_change_form.html b/django/contrib/admin/templates/registration/password_change_form.html
    index f76692d..0e4ea53 100644
    a b  
    22{% load i18n static %}
    33{% load url from future %}
    44{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
    5 {% block userlinks %}{% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %} {% trans 'Change password' %} / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %}
    6 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %}
     5{% block userlinks %}{% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %} {% trans 'Change password' %} / <a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>{% endblock %}
     6{% block breadcrumbs %}
     7<div class="breadcrumbs">
     8<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     9&rsaquo; {% trans 'Password change' %}
     10</div>
     11{% endblock %}
    712
    813{% block title %}{% trans 'Password change' %}{% endblock %}
    914
  • django/contrib/admin/templates/registration/password_reset_complete.html

    diff --git a/django/contrib/admin/templates/registration/password_reset_complete.html b/django/contrib/admin/templates/registration/password_reset_complete.html
    index fceb167..7c07707 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
    34
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset' %}</div>{% endblock %}
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; {% trans 'Password reset' %}
     9</div>
     10{% endblock %}
    511
    612{% block title %}{% trans 'Password reset complete' %}{% endblock %}
    713
  • django/contrib/admin/templates/registration/password_reset_confirm.html

    diff --git a/django/contrib/admin/templates/registration/password_reset_confirm.html b/django/contrib/admin/templates/registration/password_reset_confirm.html
    index df9cf1b..d5299eb 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
    34
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset confirmation' %}</div>{% endblock %}
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; {% trans 'Password reset confirmation' %}
     9</div>
     10{% endblock %}
    511
    612{% block title %}{% trans 'Password reset' %}{% endblock %}
    713
  • django/contrib/admin/templates/registration/password_reset_done.html

    diff --git a/django/contrib/admin/templates/registration/password_reset_done.html b/django/contrib/admin/templates/registration/password_reset_done.html
    index e223bdb..fd73af8 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
    34
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset' %}</div>{% endblock %}
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; {% trans 'Password reset' %}
     9</div>
     10{% endblock %}
    511
    612{% block title %}{% trans 'Password reset successful' %}{% endblock %}
    713
  • django/contrib/admin/templates/registration/password_reset_form.html

    diff --git a/django/contrib/admin/templates/registration/password_reset_form.html b/django/contrib/admin/templates/registration/password_reset_form.html
    index d3a1284..2b591fe 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
    34
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset' %}</div>{% endblock %}
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; {% trans 'Password reset' %}
     9</div>
     10{% endblock %}
    511
    612{% block title %}{% trans "Password reset" %}{% endblock %}
    713
  • new file django/contrib/admin/templatetags/admin_urls.py

    diff --git a/django/contrib/admin/templatetags/admin_urls.py b/django/contrib/admin/templatetags/admin_urls.py
    new file mode 100644
    index 0000000..15e4649
    - +  
     1from django.core.urlresolvers import reverse, NoReverseMatch
     2from django import template
     3
     4register = template.Library()
     5
     6@register.filter
     7def admin_url(value, arg):
     8    return 'admin:%s_%s_%s' % (value.app_label, value.module_name, arg)
  • django/contrib/admindocs/templates/admin_doc/bookmarklets.html

    diff --git a/django/contrib/admindocs/templates/admin_doc/bookmarklets.html b/django/contrib/admindocs/templates/admin_doc/bookmarklets.html
    index 6447529..baa717c 100644
    a b  
    11{% extends "admin/base_site.html" %}
    2 
    3 {% block breadcrumbs %}{% load i18n %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; <a href="../">{% trans "Documentation" %}</a> &rsaquo; {% trans "Bookmarklets" %}</div>{% endblock %}
     2{% load i18n %}
     3{% load url from future %}
     4
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
     9&rsaquo; {% trans 'Bookmarklets' %}
     10</div>
     11{% endblock %}
    412{% block title %}{% trans "Documentation bookmarklets" %}{% endblock %}
    513
    614{% block content %}
  • django/contrib/admindocs/templates/admin_doc/index.html

    diff --git a/django/contrib/admindocs/templates/admin_doc/index.html b/django/contrib/admindocs/templates/admin_doc/index.html
    index a8b21c3..652fbfd 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
    3 {% block breadcrumbs %}<div class="breadcrumbs"><a href="{{ root_path }}">Home</a> &rsaquo; Documentation</div>{% endblock %}
     3{% load url from future %}
     4
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; {% trans 'Documentation' %}</a>
     9</div>
     10{% endblock %}
    411{% block title %}Documentation{% endblock %}
    512
    613{% block content %}
  • django/contrib/admindocs/templates/admin_doc/missing_docutils.html

    diff --git a/django/contrib/admindocs/templates/admin_doc/missing_docutils.html b/django/contrib/admindocs/templates/admin_doc/missing_docutils.html
    index 97c9d47..34c18ff 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
    3 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Documentation</div>{% endblock %}
     3{% load url from future %}
     4
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; {% trans 'Documentation' %}</a>
     9</div>
    410{% block title %}Please install docutils{% endblock %}
    511
    612{% block content %}
  • django/contrib/admindocs/templates/admin_doc/model_detail.html

    diff --git a/django/contrib/admindocs/templates/admin_doc/model_detail.html b/django/contrib/admindocs/templates/admin_doc/model_detail.html
    index 828df18..82cb405 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
     4
    35{% block extrahead %}
    46{{ block.super }}
    57<style type="text/css">
     
    810</style>
    911{% endblock %}
    1012
    11 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Models</a> &rsaquo; {{ name }}</div>{% endblock %}
     13{% block breadcrumbs %}
     14<div class="breadcrumbs">
     15<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     16&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
     17&rsaquo; <a href="{% url 'django-admindocs-models-index' %}">{% trans 'Models' %}</a>
     18&rsaquo; {{ name }}
     19</div>
     20{% endblock %}
    1221
    1322{% block title %}Model: {{ name }}{% endblock %}
    1423
     
    4150</table>
    4251</div>
    4352
    44 <p class="small"><a href="../">&lsaquo; Back to Models Documentation</a></p>
     53<p class="small"><a href="{% url 'django-admindocs-models-index' %}">&lsaquo; Back to Models Documentation</a></p>
    4554</div>
    4655{% endblock %}
  • django/contrib/admindocs/templates/admin_doc/model_index.html

    diff --git a/django/contrib/admindocs/templates/admin_doc/model_index.html b/django/contrib/admindocs/templates/admin_doc/model_index.html
    index 47c94c0..4eb3cf6 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
     4
    35{% block coltype %}colSM{% endblock %}
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Models</div>{% endblock %}
     6
     7{% block breadcrumbs %}
     8<div class="breadcrumbs">
     9<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     10&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
     11&rsaquo; {% trans 'Models' %}
     12</div>
     13{% endblock %}
    514
    615{% block title %}Models{% endblock %}
    716
     
    1928<table class="xfull">
    2029{% for model in group.list %}
    2130<tr>
    22 <th><a href="{{ model.app_label }}.{{ model.object_name.lower }}/">{{ model.object_name }}</a></th>
     31<th><a href="{% url 'django-admindocs-models-detail' app_label=model.app_label model_name=model.object_name.lower %}">{{ model.object_name }}</a></th>
    2332</tr>
    2433{% endfor %}
    2534</table>
  • django/contrib/admindocs/templates/admin_doc/template_detail.html

    diff --git a/django/contrib/admindocs/templates/admin_doc/template_detail.html b/django/contrib/admindocs/templates/admin_doc/template_detail.html
    index c04dedc..307dd62 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
    3 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; Templates &rsaquo; {{ name }}</div>{% endblock %}
     3{% load url from future %}
     4
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
     9&rsaquo; {% trans 'Templates' %}
     10&rsaquo; {{ name }}
     11</div>
     12{% endblock %}
    413
    514{% block title %}Template: {{ name }}{% endblock %}
    615
     
    1726    </ol>
    1827{% endfor %}
    1928
    20 <p class="small"><a href="../../">&lsaquo; Back to Documentation</a></p>
     29<p class="small"><a href="{% url 'django-admindocs-docroot' %}">&lsaquo; Back to Documentation</a></p>
    2130{% endblock %}
  • django/contrib/admindocs/templates/admin_doc/template_filter_index.html

    diff --git a/django/contrib/admindocs/templates/admin_doc/template_filter_index.html b/django/contrib/admindocs/templates/admin_doc/template_filter_index.html
    index 46ccf0f..1809bc9 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
     4
    35{% block coltype %}colSM{% endblock %}
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; filters</div>{% endblock %}
     6{% block breadcrumbs %}
     7<div class="breadcrumbs">
     8<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     9&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
     10&rsaquo; {% trans 'Filters' %}
     11</div>
     12{% endblock %}
    513{% block title %}Template filters{% endblock %}
    614
    715{% block content %}
  • django/contrib/admindocs/templates/admin_doc/template_tag_index.html

    diff --git a/django/contrib/admindocs/templates/admin_doc/template_tag_index.html b/django/contrib/admindocs/templates/admin_doc/template_tag_index.html
    index 676c025..18e5d95 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
     4
    35{% block coltype %}colSM{% endblock %}
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Tags</div>{% endblock %}
     6{% block breadcrumbs %}
     7<div class="breadcrumbs">
     8<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     9&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
     10&rsaquo; {% trans 'Tags' %}
     11</div>
     12{% endblock %}
    513{% block title %}Template tags{% endblock %}
    614
    715{% block content %}
  • django/contrib/admindocs/templates/admin_doc/view_detail.html

    diff --git a/django/contrib/admindocs/templates/admin_doc/view_detail.html b/django/contrib/admindocs/templates/admin_doc/view_detail.html
    index c6d080c..41c8121 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
    3 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Views</a> &rsaquo; {{ name }}</div>{% endblock %}
     3{% load url from future %}
     4
     5{% block breadcrumbs %}
     6<div class="breadcrumbs">
     7<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     8&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
     9&rsaquo; <a href="{% url 'django-admindocs-views-index' %}">{% trans 'Views' %}</a>
     10&rsaquo; {{ name }}
     11</div>
     12{% endblock %}
    413{% block title %}View: {{ name }}{% endblock %}
    514
    615{% block content %}
     
    2130<p>{{ meta.Templates }}</p>
    2231{% endif %}
    2332
    24 <p class="small"><a href="../">&lsaquo; Back to Views Documentation</a></p>
     33<p class="small"><a href="{% url 'django-admindocs-views-index' %}">&lsaquo; Back to Views Documentation</a></p>
    2534{% endblock %}
  • django/contrib/admindocs/templates/admin_doc/view_index.html

    diff --git a/django/contrib/admindocs/templates/admin_doc/view_index.html b/django/contrib/admindocs/templates/admin_doc/view_index.html
    index 6b10fa6..e508c84 100644
    a b  
    11{% extends "admin/base_site.html" %}
    22{% load i18n %}
     3{% load url from future %}
     4
    35{% block coltype %}colSM{% endblock %}
    4 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Views</div>{% endblock %}
     6{% block breadcrumbs %}
     7<div class="breadcrumbs">
     8<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
     9&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
     10&rsaquo; {% trans 'Views' %}
     11</div>
     12{% endblock %}
    513{% block title %}Views{% endblock %}
    614
    715{% block content %}
     
    2937
    3038{% for view in site_views.list|dictsort:"url" %}
    3139{% ifchanged %}
    32 <h3><a href="{{ view.module }}.{{ view.name }}/">{{ view.url }}</a></h3>
    33 <p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p>
     40<h3><a href="{% url 'django-admindocs-views-detail' view=view.full_name %}">{{ view.url }}</a></h3>
     41<p class="small quiet">View function: {{ view.full_name }}</p>
    3442<p>{{ view.title }}</p>
    3543<hr />
    3644{% endifchanged %}
  • django/contrib/admindocs/views.py

    diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py
    index 28319be..9725865 100644
    a b def view_index(request): 
    136136            site_obj = GenericSite()
    137137        for (func, regex) in view_functions:
    138138            views.append({
    139                 'name': getattr(func, '__name__', func.__class__.__name__),
    140                 'module': func.__module__,
     139                'full_name': '%s.%s' % (func.__module__, getattr(func, '__name__', func.__class__.__name__)),
    141140                'site_id': settings_mod.SITE_ID,
    142141                'site': site_obj,
    143142                'url': simplify_regex(regex),
  • new file tests/regressiontests/admin_changelist/admin.py

    diff --git a/tests/regressiontests/admin_changelist/admin.py b/tests/regressiontests/admin_changelist/admin.py
    new file mode 100644
    index 0000000..9490cd5
    - +  
     1from django.core.paginator import Paginator
     2from django.contrib import admin
     3
     4from models import (Child, Parent, Genre, Band, Musician, Group, Quartet,
     5    Membership, ChordsMusician, ChordsBand, Invitation)
     6
     7site = admin.AdminSite(name="admin")
     8
     9class CustomPaginator(Paginator):
     10    def __init__(self, queryset, page_size, orphans=0, allow_empty_first_page=True):
     11        super(CustomPaginator, self).__init__(queryset, 5, orphans=2,
     12            allow_empty_first_page=allow_empty_first_page)
     13
     14
     15class ParentAdmin(admin.ModelAdmin):
     16    list_filter = ['child__name']
     17    search_fields = ['child__name']
     18
     19
     20class ChildAdmin(admin.ModelAdmin):
     21    list_display = ['name', 'parent']
     22    list_per_page = 10
     23
     24    def queryset(self, request):
     25        return super(ChildAdmin, self).queryset(request).select_related("parent__name")
     26
     27
     28class CustomPaginationAdmin(ChildAdmin):
     29    paginator = CustomPaginator
     30
     31
     32class FilteredChildAdmin(admin.ModelAdmin):
     33    list_display = ['name', 'parent']
     34    list_per_page = 10
     35
     36    def queryset(self, request):
     37        return super(FilteredChildAdmin, self).queryset(request).filter(
     38            name__contains='filtered')
     39
     40
     41class BandAdmin(admin.ModelAdmin):
     42    list_filter = ['genres']
     43
     44
     45class GroupAdmin(admin.ModelAdmin):
     46    list_filter = ['members']
     47
     48
     49class QuartetAdmin(admin.ModelAdmin):
     50    list_filter = ['members']
     51
     52
     53class ChordsBandAdmin(admin.ModelAdmin):
     54    list_filter = ['members']
     55
     56
     57class DynamicListDisplayChildAdmin(admin.ModelAdmin):
     58    list_display = ('name', 'parent')
     59
     60    def get_list_display(self, request):
     61        my_list_display = list(self.list_display)
     62        if request.user.username == 'noparents':
     63            my_list_display.remove('parent')
     64
     65        return my_list_display
     66
     67site.register(Child, DynamicListDisplayChildAdmin)
  • tests/regressiontests/admin_changelist/tests.py

    diff --git a/tests/regressiontests/admin_changelist/tests.py b/tests/regressiontests/admin_changelist/tests.py
    index 09ec190..83ce6e3 100644
    a b from __future__ import with_statement 
    33from django.contrib import admin
    44from django.contrib.admin.options import IncorrectLookupParameters
    55from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR
    6 from django.core.paginator import Paginator
    76from django.template import Context, Template
    87from django.test import TestCase
    98from django.test.client import RequestFactory
    from django.contrib.auth.models import User 
    1211from models import (Child, Parent, Genre, Band, Musician, Group, Quartet,
    1312    Membership, ChordsMusician, ChordsBand, Invitation)
    1413
     14from admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin,
     15    GroupAdmin, ParentAdmin, DynamicListDisplayChildAdmin, CustomPaginationAdmin,
     16    FilteredChildAdmin, CustomPaginator, site as custom_site)
     17
    1518
    1619class ChangeListTests(TestCase):
     20    urls = "regressiontests.admin_changelist.urls"
     21
    1722    def setUp(self):
    1823        self.factory = RequestFactory()
    1924
    class ChangeListTests(TestCase): 
    129134            new_child = Child.objects.create(name='name %s' % i, parent=new_parent)
    130135
    131136        request = self.factory.get('/child/')
    132         m = ChildAdmin(Child, admin.site)
    133         m.list_display = ['id', 'name', 'parent']
    134         m.list_display_links = ['id']
    135         m.list_editable = ['name']
    136         m.paginator = CustomPaginator
     137        m = CustomPaginationAdmin(Child, admin.site)
    137138
    138139        cl = ChangeList(request, Child, m.list_display, m.list_display_links,
    139140                m.list_filter, m.date_hierarchy, m.search_fields,
    class ChangeListTests(TestCase): 
    330331            request.user = user
    331332            return request
    332333
     334        m = custom_site._registry[Child]
    333335        # Test with user 'noparents'
    334         m = DynamicListDisplayChildAdmin(Child, admin.site)
    335336        request = _mocked_authenticated_request(user_noparents)
    336337        response = m.changelist_view(request)
    337338        # XXX - Calling render here to avoid ContentNotRenderedError to be
    class ChangeListTests(TestCase): 
    340341        self.assertNotContains(response, 'Parent object')
    341342
    342343        # Test with user 'parents'
    343         m = DynamicListDisplayChildAdmin(Child, admin.site)
    344344        request = _mocked_authenticated_request(user_parents)
    345345        response = m.changelist_view(request)
    346346        # XXX - #15826
    347347        response.render()
    348348        self.assertContains(response, 'Parent object')
    349349
     350        custom_site.unregister(Child)
    350351        # Test default implementation
    351         m = ChildAdmin(Child, admin.site)
     352        custom_site.register(Child, ChildAdmin)
     353        m = custom_site._registry[Child]
    352354        request = _mocked_authenticated_request(user_noparents)
    353355        response = m.changelist_view(request)
    354356        # XXX - #15826
    class ChangeListTests(TestCase): 
    383385        cl.get_results(request)
    384386        self.assertEqual(len(cl.result_list), 10)
    385387
    386 
    387 class ParentAdmin(admin.ModelAdmin):
    388     list_filter = ['child__name']
    389     search_fields = ['child__name']
    390 
    391 
    392 class ChildAdmin(admin.ModelAdmin):
    393     list_display = ['name', 'parent']
    394     list_per_page = 10
    395 
    396     def queryset(self, request):
    397         return super(ChildAdmin, self).queryset(request).select_related("parent__name")
    398 
    399 
    400 class FilteredChildAdmin(admin.ModelAdmin):
    401     list_display = ['name', 'parent']
    402     list_per_page = 10
    403 
    404     def queryset(self, request):
    405         return super(FilteredChildAdmin, self).queryset(request).filter(
    406             name__contains='filtered')
    407 
    408 
    409 class CustomPaginator(Paginator):
    410     def __init__(self, queryset, page_size, orphans=0, allow_empty_first_page=True):
    411         super(CustomPaginator, self).__init__(queryset, 5, orphans=2,
    412             allow_empty_first_page=allow_empty_first_page)
    413 
    414 
    415 class BandAdmin(admin.ModelAdmin):
    416     list_filter = ['genres']
    417 
    418 
    419 class GroupAdmin(admin.ModelAdmin):
    420     list_filter = ['members']
    421 
    422 
    423 class QuartetAdmin(admin.ModelAdmin):
    424     list_filter = ['members']
    425 
    426 
    427 class ChordsBandAdmin(admin.ModelAdmin):
    428     list_filter = ['members']
    429 
    430 
    431 class DynamicListDisplayChildAdmin(admin.ModelAdmin):
    432     list_display = ('name', 'parent')
    433 
    434     def get_list_display(self, request):
    435         my_list_display = list(self.list_display)
    436         if request.user.username == 'noparents':
    437             my_list_display.remove('parent')
    438 
    439         return my_list_display
  • new file tests/regressiontests/admin_changelist/urls.py

    diff --git a/tests/regressiontests/admin_changelist/urls.py b/tests/regressiontests/admin_changelist/urls.py
    new file mode 100644
    index 0000000..1cbb496
    - +  
     1from django.conf.urls.defaults import *
     2
     3import admin
     4
     5urlpatterns = patterns('',
     6    (r'^admin/', include(admin.site.urls)),
     7)
  • new file tests/regressiontests/admin_custom_urls/__init__.py

    diff --git a/tests/regressiontests/admin_custom_urls/__init__.py b/tests/regressiontests/admin_custom_urls/__init__.py
    new file mode 100644
    index 0000000..792d600
    - +  
     1#
  • new file tests/regressiontests/admin_custom_urls/fixtures/actions.json

    diff --git a/tests/regressiontests/admin_custom_urls/fixtures/actions.json b/tests/regressiontests/admin_custom_urls/fixtures/actions.json
    new file mode 100644
    index 0000000..d803393
    - +  
     1[
     2  {
     3    "pk": "delete",
     4    "model": "admin_custom_urls.action",
     5    "fields": {
     6      "description": "Remove things."
     7    }
     8  },
     9  {
     10    "pk": "rename",
     11    "model": "admin_custom_urls.action",
     12    "fields": {
     13      "description": "Gives things other names."
     14    }
     15  },
     16  {
     17    "pk": "add",
     18    "model": "admin_custom_urls.action",
     19    "fields": {
     20      "description": "Add things."
     21    }
     22  },
     23  {
     24    "pk": "path/to/file/",
     25    "model": "admin_custom_urls.action",
     26    "fields": {
     27      "description": "An action with '/' in its name."
     28    }
     29  },
     30  {
     31    "pk": "path/to/html/document.html",
     32    "model": "admin_custom_urls.action",
     33    "fields": {
     34      "description": "An action with a name similar to a HTML doc path."
     35    }
     36  },
     37  {
     38    "pk": "javascript:alert('Hello world');\">Click here</a>",
     39    "model": "admin_custom_urls.action",
     40    "fields": {
     41      "description": "An action with a name suspected of being a XSS attempt"
     42    }
     43  }
     44]
     45 No newline at end of file
  • new file tests/regressiontests/admin_custom_urls/fixtures/users.json

    diff --git a/tests/regressiontests/admin_custom_urls/fixtures/users.json b/tests/regressiontests/admin_custom_urls/fixtures/users.json
    new file mode 100644
    index 0000000..72d86d7
    - +  
     1[
     2  {
     3    "pk": 100,
     4    "model": "auth.user",
     5    "fields": {
     6      "username": "super",
     7      "first_name": "Super",
     8      "last_name": "User",
     9      "is_active": true,
     10      "is_superuser": true,
     11      "is_staff": true,
     12      "last_login": "2007-05-30 13:20:10",
     13      "groups": [],
     14      "user_permissions": [],
     15      "password": "sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158",
     16      "email": "super@example.com",
     17      "date_joined": "2007-05-30 13:20:10"
     18    }
     19  }
     20]
  • new file tests/regressiontests/admin_custom_urls/models.py

    diff --git a/tests/regressiontests/admin_custom_urls/models.py b/tests/regressiontests/admin_custom_urls/models.py
    new file mode 100644
    index 0000000..f8c83a9
    - +  
     1from functools import update_wrapper
     2
     3from django.contrib import admin
     4from django.db import models
     5
     6
     7class Action(models.Model):
     8    name = models.CharField(max_length=50, primary_key=True)
     9    description = models.CharField(max_length=70)
     10
     11    def __unicode__(self):
     12        return self.name
     13
     14
     15class ActionAdmin(admin.ModelAdmin):
     16    """
     17    A ModelAdmin for the Action model that changes the URL of the add_view
     18    to '<app name>/<model name>/!add/'
     19    The Action model has a CharField PK.
     20    """
     21
     22    list_display = ('name', 'description')
     23
     24    def remove_url(self, name):
     25        """
     26        Remove all entries named 'name' from the ModelAdmin instance URL
     27        patterns list
     28        """
     29        return filter(lambda e: e.name != name, super(ActionAdmin, self).get_urls())
     30
     31    def get_urls(self):
     32        # Add the URL of our custom 'add_view' view to the front of the URLs
     33        # list.  Remove the existing one(s) first
     34        from django.conf.urls.defaults import patterns, url
     35
     36        def wrap(view):
     37            def wrapper(*args, **kwargs):
     38                return self.admin_site.admin_view(view)(*args, **kwargs)
     39            return update_wrapper(wrapper, view)
     40
     41        info = self.model._meta.app_label, self.model._meta.module_name
     42
     43        view_name = '%s_%s_add' % info
     44
     45        return patterns('',
     46            url(r'^!add/$', wrap(self.add_view), name=view_name),
     47        ) + self.remove_url(view_name)
     48
     49
     50admin.site.register(Action, ActionAdmin)
  • new file tests/regressiontests/admin_custom_urls/tests.py

    diff --git a/tests/regressiontests/admin_custom_urls/tests.py b/tests/regressiontests/admin_custom_urls/tests.py
    new file mode 100644
    index 0000000..cfc6b85
    - +  
     1from django.core.urlresolvers import reverse
     2from django.template.response import TemplateResponse
     3from django.test import TestCase
     4
     5from models import Action
     6
     7
     8class AdminCustomUrlsTest(TestCase):
     9    fixtures = ['users.json', 'actions.json']
     10
     11    def setUp(self):
     12        self.client.login(username='super', password='secret')
     13
     14    def tearDown(self):
     15        self.client.logout()
     16
     17    def testBasicAddGet(self):
     18        """
     19        A smoke test to ensure GET on the add_view works.
     20        """
     21        response = self.client.get('/custom_urls/admin/admin_custom_urls/action/!add/')
     22        self.assertIsInstance(response, TemplateResponse)
     23        self.assertEqual(response.status_code, 200)
     24
     25    def testAddWithGETArgs(self):
     26        response = self.client.get('/custom_urls/admin/admin_custom_urls/action/!add/', {'name': 'My Action'})
     27        self.assertEqual(response.status_code, 200)
     28        self.assertTrue(
     29            'value="My Action"' in response.content,
     30            "Couldn't find an input with the right value in the response."
     31        )
     32
     33    def testBasicAddPost(self):
     34        """
     35        A smoke test to ensure POST on add_view works.
     36        """
     37        post_data = {
     38            '_popup': u'1',
     39            "name": u'Action added through a popup',
     40            "description": u"Description of added action",
     41        }
     42        response = self.client.post('/custom_urls/admin/admin_custom_urls/action/!add/', post_data)
     43        self.assertEqual(response.status_code, 200)
     44        self.assertContains(response, 'dismissAddAnotherPopup')
     45        self.assertContains(response, 'Action added through a popup')
     46
     47    def testAdminUrlsNoClash(self):
     48        """
     49        Test that some admin URLs work correctly. The model has a CharField
     50        PK and the add_view URL has been customized.
     51        """
     52        # Should get the change_view for model instance with PK 'add', not show
     53        # the add_view
     54        response = self.client.get('/custom_urls/admin/admin_custom_urls/action/add/')
     55        self.assertEqual(response.status_code, 200)
     56        self.assertContains(response, 'Change action')
     57
     58        # Ditto, but use reverse() to build the URL
     59        path = reverse('admin:%s_action_change' % Action._meta.app_label,
     60                args=('add',))
     61        response = self.client.get(path)
     62        self.assertEqual(response.status_code, 200)
     63        self.assertContains(response, 'Change action')
     64
     65        # Should correctly get the change_view for the model instance with the
     66        # funny-looking PK
     67        path = reverse('admin:%s_action_change' % Action._meta.app_label,
     68                args=("path/to/html/document.html",))
     69        response = self.client.get(path)
     70        self.assertEqual(response.status_code, 200)
     71        self.assertContains(response, 'Change action')
     72        self.assertContains(response, 'value="path/to/html/document.html"')
  • new file tests/regressiontests/admin_custom_urls/urls.py

    diff --git a/tests/regressiontests/admin_custom_urls/urls.py b/tests/regressiontests/admin_custom_urls/urls.py
    new file mode 100644
    index 0000000..6c2761a
    - +  
     1from django.conf.urls.defaults import *
     2from django.contrib import admin
     3
     4urlpatterns = patterns('',
     5    (r'^admin/', include(admin.site.urls)),
     6)
     7
  • new file tests/regressiontests/admin_inlines/admin.py

    diff --git a/tests/regressiontests/admin_inlines/admin.py b/tests/regressiontests/admin_inlines/admin.py
    new file mode 100644
    index 0000000..098d7b5
    - +  
     1from django.contrib import admin
     2from django import forms
     3
     4from models import *
     5
     6site = admin.AdminSite(name="admin")
     7
     8
     9class BookInline(admin.TabularInline):
     10    model = Author.books.through
     11
     12
     13class AuthorAdmin(admin.ModelAdmin):
     14    inlines = [BookInline]
     15
     16
     17class InnerInline(admin.StackedInline):
     18    model = Inner
     19    can_delete = False
     20    readonly_fields = ('readonly',) # For bug #13174 tests.
     21
     22
     23class HolderAdmin(admin.ModelAdmin):
     24
     25    class Media:
     26        js = ('my_awesome_admin_scripts.js',)
     27
     28
     29class InnerInline2(admin.StackedInline):
     30    model = Inner2
     31
     32    class Media:
     33        js = ('my_awesome_inline_scripts.js',)
     34
     35
     36class InnerInline3(admin.StackedInline):
     37    model = Inner3
     38
     39    class Media:
     40        js = ('my_awesome_inline_scripts.js',)
     41
     42
     43class TitleForm(forms.ModelForm):
     44
     45    def clean(self):
     46        cleaned_data = self.cleaned_data
     47        title1 = cleaned_data.get("title1")
     48        title2 = cleaned_data.get("title2")
     49        if title1 != title2:
     50            raise forms.ValidationError("The two titles must be the same")
     51        return cleaned_data
     52
     53
     54class TitleInline(admin.TabularInline):
     55    model = Title
     56    form = TitleForm
     57    extra = 1
     58
     59
     60class Inner4StackedInline(admin.StackedInline):
     61    model = Inner4Stacked
     62
     63
     64class Inner4TabularInline(admin.TabularInline):
     65    model = Inner4Tabular
     66
     67
     68class Holder4Admin(admin.ModelAdmin):
     69    inlines = [Inner4StackedInline, Inner4TabularInline]
     70
     71
     72class InlineWeakness(admin.TabularInline):
     73    model = ShoppingWeakness
     74    extra = 1
     75
     76
     77class QuestionInline(admin.TabularInline):
     78    model = Question
     79    readonly_fields=['call_me']
     80
     81    def call_me(self, obj):
     82        return 'Callable in QuestionInline'
     83
     84
     85class PollAdmin(admin.ModelAdmin):
     86    inlines = [QuestionInline]
     87
     88    def call_me(self, obj):
     89        return 'Callable in PollAdmin'
     90
     91
     92class ChapterInline(admin.TabularInline):
     93    model = Chapter
     94    readonly_fields=['call_me']
     95
     96    def call_me(self, obj):
     97        return 'Callable in ChapterInline'
     98
     99
     100class NovelAdmin(admin.ModelAdmin):
     101    inlines = [ChapterInline]
     102
     103
     104site.register(TitleCollection, inlines=[TitleInline])
     105# Test bug #12561 and #12778
     106# only ModelAdmin media
     107site.register(Holder, HolderAdmin, inlines=[InnerInline])
     108# ModelAdmin and Inline media
     109site.register(Holder2, HolderAdmin, inlines=[InnerInline2])
     110# only Inline media
     111site.register(Holder3, inlines=[InnerInline3])
     112
     113site.register(Poll, PollAdmin)
     114site.register(Novel, NovelAdmin)
     115site.register(Fashionista, inlines=[InlineWeakness])
     116site.register(Holder4, Holder4Admin)
     117site.register(Author, AuthorAdmin)
     118
  • tests/regressiontests/admin_inlines/models.py

    diff --git a/tests/regressiontests/admin_inlines/models.py b/tests/regressiontests/admin_inlines/models.py
    index 8d9ca7c..f4e58f2 100644
    a b Testing of admin inline formsets. 
    33
    44"""
    55from django.db import models
    6 from django.contrib import admin
    76from django.contrib.contenttypes.models import ContentType
    87from django.contrib.contenttypes import generic
    9 from django import forms
     8
    109
    1110class Parent(models.Model):
    1211    name = models.CharField(max_length=50)
    class Parent(models.Model): 
    1413    def __unicode__(self):
    1514        return self.name
    1615
     16
    1717class Teacher(models.Model):
    1818    name = models.CharField(max_length=50)
    1919
    2020    def __unicode__(self):
    2121        return self.name
    2222
     23
    2324class Child(models.Model):
    2425    name = models.CharField(max_length=50)
    2526    teacher = models.ForeignKey(Teacher)
    class Child(models.Model): 
    3132    def __unicode__(self):
    3233        return u'I am %s, a child of %s' % (self.name, self.parent)
    3334
     35
    3436class Book(models.Model):
    3537    name = models.CharField(max_length=50)
    3638
     39
    3740class Author(models.Model):
    3841    name = models.CharField(max_length=50)
    3942    books = models.ManyToManyField(Book)
    4043
    41 class BookInline(admin.TabularInline):
    42     model = Author.books.through
    43 
    44 class AuthorAdmin(admin.ModelAdmin):
    45     inlines = [BookInline]
    46 
    47 admin.site.register(Author, AuthorAdmin)
    4844
    4945class Holder(models.Model):
    5046    dummy = models.IntegerField()
    class Inner(models.Model): 
    5652    readonly = models.CharField("Inner readonly label", max_length=1)
    5753
    5854
    59 class InnerInline(admin.StackedInline):
    60     model = Inner
    61     can_delete = False
    62     readonly_fields = ('readonly',) # For bug #13174 tests.
    63 
    64 
    6555class Holder2(models.Model):
    6656    dummy = models.IntegerField()
    6757
    class Inner2(models.Model): 
    7060    dummy = models.IntegerField()
    7161    holder = models.ForeignKey(Holder2)
    7262
    73 class HolderAdmin(admin.ModelAdmin):
    74 
    75     class Media:
    76         js = ('my_awesome_admin_scripts.js',)
    77 
    78 class InnerInline2(admin.StackedInline):
    79     model = Inner2
    80 
    81     class Media:
    82         js = ('my_awesome_inline_scripts.js',)
    83 
    8463class Holder3(models.Model):
    8564    dummy = models.IntegerField()
    8665
    class Inner3(models.Model): 
    8968    dummy = models.IntegerField()
    9069    holder = models.ForeignKey(Holder3)
    9170
    92 class InnerInline3(admin.StackedInline):
    93     model = Inner3
    94 
    95     class Media:
    96         js = ('my_awesome_inline_scripts.js',)
    97 
    98 # Test bug #12561 and #12778
    99 # only ModelAdmin media
    100 admin.site.register(Holder, HolderAdmin, inlines=[InnerInline])
    101 # ModelAdmin and Inline media
    102 admin.site.register(Holder2, HolderAdmin, inlines=[InnerInline2])
    103 # only Inline media
    104 admin.site.register(Holder3, inlines=[InnerInline3])
    105 
    106 
    10771# Models for ticket #8190
    10872
    10973class Holder4(models.Model):
    class Inner4Tabular(models.Model): 
    11781    dummy = models.IntegerField(help_text="Awesome tabular help text is awesome.")
    11882    holder = models.ForeignKey(Holder4)
    11983
    120 class Inner4StackedInline(admin.StackedInline):
    121     model = Inner4Stacked
    122 
    123 class Inner4TabularInline(admin.TabularInline):
    124     model = Inner4Tabular
    125 
    126 class Holder4Admin(admin.ModelAdmin):
    127     inlines = [Inner4StackedInline, Inner4TabularInline]
    128 
    129 admin.site.register(Holder4, Holder4Admin)
    130 
    13184
    13285# Models for #12749
    13386
    class ShoppingWeakness(models.Model): 
    14598    fashionista = models.ForeignKey(Fashionista)
    14699    item = models.ForeignKey(OutfitItem)
    147100
    148 class InlineWeakness(admin.TabularInline):
    149     model = ShoppingWeakness
    150     extra = 1
    151 
    152 admin.site.register(Fashionista, inlines=[InlineWeakness])
    153 
    154101# Models for #13510
    155102
    156103class TitleCollection(models.Model):
    class Title(models.Model): 
    161108    title1 = models.CharField(max_length=100)
    162109    title2 = models.CharField(max_length=100)
    163110
    164 class TitleForm(forms.ModelForm):
    165 
    166     def clean(self):
    167         cleaned_data = self.cleaned_data
    168         title1 = cleaned_data.get("title1")
    169         title2 = cleaned_data.get("title2")
    170         if title1 != title2:
    171             raise forms.ValidationError("The two titles must be the same")
    172         return cleaned_data
    173 
    174 class TitleInline(admin.TabularInline):
    175     model = Title
    176     form = TitleForm
    177     extra = 1
    178 
    179 admin.site.register(TitleCollection, inlines=[TitleInline])
    180 
    181111# Models for #15424
    182112
    183113class Poll(models.Model):
    class Poll(models.Model): 
    186116class Question(models.Model):
    187117    poll = models.ForeignKey(Poll)
    188118
    189 class QuestionInline(admin.TabularInline):
    190     model = Question
    191     readonly_fields=['call_me']
    192 
    193     def call_me(self, obj):
    194         return 'Callable in QuestionInline'
    195 
    196 class PollAdmin(admin.ModelAdmin):
    197     inlines = [QuestionInline]
    198 
    199     def call_me(self, obj):
    200         return 'Callable in PollAdmin'
    201 
    202119class Novel(models.Model):
    203120    name = models.CharField(max_length=40)
    204121
    205122class Chapter(models.Model):
    206123    novel = models.ForeignKey(Novel)
    207124
    208 class ChapterInline(admin.TabularInline):
    209     model = Chapter
    210     readonly_fields=['call_me']
    211 
    212     def call_me(self, obj):
    213         return 'Callable in ChapterInline'
    214 
    215 class NovelAdmin(admin.ModelAdmin):
    216     inlines = [ChapterInline]
    217 
    218 admin.site.register(Poll, PollAdmin)
    219 admin.site.register(Novel, NovelAdmin)
  • tests/regressiontests/admin_inlines/tests.py

    diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py
    index 45ecb70..efa6cf6 100644
    a b from django.contrib.contenttypes.models import ContentType 
    33from django.test import TestCase
    44
    55# local test models
    6 from models import (Holder, Inner, InnerInline, Holder2, Inner2, Holder3,
     6from models import (Holder, Inner, Holder2, Inner2, Holder3,
    77    Inner3, Person, OutfitItem, Fashionista, Teacher, Parent, Child)
     8from admin import InnerInline
    89
    910
    1011class TestInline(TestCase):
     12    urls = "regressiontests.admin_inlines.urls"
    1113    fixtures = ['admin-views-users.xml']
    1214
    1315    def setUp(self):
    1416        holder = Holder(dummy=13)
    1517        holder.save()
    1618        Inner(dummy=42, holder=holder).save()
    17         self.change_url = '/test_admin/admin/admin_inlines/holder/%i/' % holder.id
     19        self.change_url = '/admin/admin_inlines/holder/%i/' % holder.id
    1820
    1921        result = self.client.login(username='super', password='secret')
    2022        self.assertEqual(result, True)
    class TestInline(TestCase): 
    3638        """Bug #13174."""
    3739        holder = Holder.objects.create(dummy=42)
    3840        inner = Inner.objects.create(holder=holder, dummy=42, readonly='')
    39         response = self.client.get('/test_admin/admin/admin_inlines/holder/%i/'
     41        response = self.client.get('/admin/admin_inlines/holder/%i/'
    4042                                   % holder.id)
    4143        self.assertContains(response, '<label>Inner readonly label:</label>')
    4244
    4345    def test_many_to_many_inlines(self):
    4446        "Autogenerated many-to-many inlines are displayed correctly (#13407)"
    45         response = self.client.get('/test_admin/admin/admin_inlines/author/add/')
     47        response = self.client.get('/admin/admin_inlines/author/add/')
    4648        # The heading for the m2m inline block uses the right text
    4749        self.assertContains(response, '<h2>Author-book relationships</h2>')
    4850        # The "add another" label is correct
    class TestInline(TestCase): 
    6365            'max_weight': 0,
    6466            'shoppingweakness_set-0-item': item.id,
    6567        }
    66         response = self.client.post('/test_admin/admin/admin_inlines/fashionista/add/', data)
     68        response = self.client.post('/admin/admin_inlines/fashionista/add/', data)
    6769        self.assertEqual(response.status_code, 302)
    6870        self.assertEqual(len(Fashionista.objects.filter(person__firstname='Imelda')), 1)
    6971
    class TestInline(TestCase): 
    8082            'title_set-0-title1': 'a title',
    8183            'title_set-0-title2': 'a different title',
    8284        }
    83         response = self.client.post('/test_admin/admin/admin_inlines/titlecollection/add/', data)
     85        response = self.client.post('/admin/admin_inlines/titlecollection/add/', data)
    8486        # Here colspan is "4": two fields (title1 and title2), one hidden field and the delete checkbock.
    8587        self.assertContains(response, '<tr><td colspan="4"><ul class="errorlist"><li>The two titles must be the same</li></ul></td></tr>')
    8688
    class TestInline(TestCase): 
    8890        """Admin inline `readonly_field` shouldn't invoke parent ModelAdmin callable"""
    8991        # Identically named callable isn't present in the parent ModelAdmin,
    9092        # rendering of the add view shouldn't explode
    91         response = self.client.get('/test_admin/admin/admin_inlines/novel/add/')
     93        response = self.client.get('/admin/admin_inlines/novel/add/')
    9294        self.assertEqual(response.status_code, 200)
    9395        # View should have the child inlines section
    9496        self.assertContains(response, '<div class="inline-group" id="chapter_set-group">')
    9597
    9698    def test_callable_lookup(self):
    9799        """Admin inline should invoke local callable when its name is listed in readonly_fields"""
    98         response = self.client.get('/test_admin/admin/admin_inlines/poll/add/')
     100        response = self.client.get('/admin/admin_inlines/poll/add/')
    99101        self.assertEqual(response.status_code, 200)
    100102        # Add parent object view should have the child inlines section
    101103        self.assertContains(response, '<div class="inline-group" id="question_set-group">')
    class TestInline(TestCase): 
    109111        using both the stacked and tabular layouts.
    110112        Ref #8190.
    111113        """
    112         response = self.client.get('/test_admin/admin/admin_inlines/holder4/add/')
     114        response = self.client.get('/admin/admin_inlines/holder4/add/')
    113115        self.assertContains(response, '<p class="help">Awesome stacked help text is awesome.</p>', 4)
    114116        self.assertContains(response, '<img src="/static/admin/img/icon-unknown.gif" class="help help-tooltip" width="10" height="10" alt="(Awesome tabular help text is awesome.)" title="Awesome tabular help text is awesome." />', 1)
    115117
    116118class TestInlineMedia(TestCase):
     119    urls = "regressiontests.admin_inlines.urls"
    117120    fixtures = ['admin-views-users.xml']
    118121
    119122    def setUp(self):
    class TestInlineMedia(TestCase): 
    128131        holder = Holder(dummy=13)
    129132        holder.save()
    130133        Inner(dummy=42, holder=holder).save()
    131         change_url = '/test_admin/admin/admin_inlines/holder/%i/' % holder.id
     134        change_url = '/admin/admin_inlines/holder/%i/' % holder.id
    132135        response = self.client.get(change_url)
    133136        self.assertContains(response, 'my_awesome_admin_scripts.js')
    134137
    class TestInlineMedia(TestCase): 
    136139        holder = Holder3(dummy=13)
    137140        holder.save()
    138141        Inner3(dummy=42, holder=holder).save()
    139         change_url = '/test_admin/admin/admin_inlines/holder3/%i/' % holder.id
     142        change_url = '/admin/admin_inlines/holder3/%i/' % holder.id
    140143        response = self.client.get(change_url)
    141144        self.assertContains(response, 'my_awesome_inline_scripts.js')
    142145
    class TestInlineMedia(TestCase): 
    144147        holder = Holder2(dummy=13)
    145148        holder.save()
    146149        Inner2(dummy=42, holder=holder).save()
    147         change_url = '/test_admin/admin/admin_inlines/holder2/%i/' % holder.id
     150        change_url = '/admin/admin_inlines/holder2/%i/' % holder.id
    148151        response = self.client.get(change_url)
    149152        self.assertContains(response, 'my_awesome_admin_scripts.js')
    150153        self.assertContains(response, 'my_awesome_inline_scripts.js')
    151154
    152155class TestInlineAdminForm(TestCase):
     156    urls = "regressiontests.admin_inlines.urls"
    153157
    154158    def test_immutable_content_type(self):
    155159        """Regression for #9362
  • new file tests/regressiontests/admin_inlines/urls.py

    diff --git a/tests/regressiontests/admin_inlines/urls.py b/tests/regressiontests/admin_inlines/urls.py
    new file mode 100644
    index 0000000..1cbb496
    - +  
     1from django.conf.urls.defaults import *
     2
     3import admin
     4
     5urlpatterns = patterns('',
     6    (r'^admin/', include(admin.site.urls)),
     7)
  • new file tests/regressiontests/admin_views/admin.py

    diff --git a/tests/regressiontests/admin_views/admin.py b/tests/regressiontests/admin_views/admin.py
    new file mode 100644
    index 0000000..11b0b9e
    - +  
     1# -*- coding: utf-8 -*-
     2import datetime
     3import tempfile
     4import os
     5
     6from django.contrib import admin
     7from django.contrib.admin.views.main import ChangeList
     8from django.forms.models import BaseModelFormSet
     9from django.core.mail import EmailMessage
     10
     11from models import *
     12
     13
     14def callable_year(dt_value):
     15    return dt_value.year
     16callable_year.admin_order_field = 'date'
     17
     18
     19class ArticleInline(admin.TabularInline):
     20    model = Article
     21
     22
     23class ChapterInline(admin.TabularInline):
     24    model = Chapter
     25
     26
     27class ChapterXtra1Admin(admin.ModelAdmin):
     28    list_filter = ('chap',
     29                   'chap__title',
     30                   'chap__book',
     31                   'chap__book__name',
     32                   'chap__book__promo',
     33                   'chap__book__promo__name',)
     34
     35
     36class ArticleAdmin(admin.ModelAdmin):
     37    list_display = ('content', 'date', callable_year, 'model_year', 'modeladmin_year')
     38    list_filter = ('date', 'section')
     39
     40    def changelist_view(self, request):
     41        "Test that extra_context works"
     42        return super(ArticleAdmin, self).changelist_view(
     43            request, extra_context={
     44                'extra_var': 'Hello!'
     45            }
     46        )
     47
     48    def modeladmin_year(self, obj):
     49        return obj.date.year
     50    modeladmin_year.admin_order_field = 'date'
     51    modeladmin_year.short_description = None
     52
     53    def delete_model(self, request, obj):
     54        EmailMessage(
     55            'Greetings from a deleted object',
     56            'I hereby inform you that some user deleted me',
     57            'from@example.com',
     58            ['to@example.com']
     59        ).send()
     60        return super(ArticleAdmin, self).delete_model(request, obj)
     61
     62    def save_model(self, request, obj, form, change=True):
     63        EmailMessage(
     64            'Greetings from a created object',
     65            'I hereby inform you that some user created me',
     66            'from@example.com',
     67            ['to@example.com']
     68        ).send()
     69        return super(ArticleAdmin, self).save_model(request, obj, form, change)
     70
     71
     72class RowLevelChangePermissionModelAdmin(admin.ModelAdmin):
     73    def has_change_permission(self, request, obj=None):
     74        """ Only allow changing objects with even id number """
     75        return request.user.is_staff and (obj is not None) and (obj.id % 2 == 0)
     76
     77
     78class CustomArticleAdmin(admin.ModelAdmin):
     79    """
     80    Tests various hooks for using custom templates and contexts.
     81    """
     82    change_list_template = 'custom_admin/change_list.html'
     83    change_form_template = 'custom_admin/change_form.html'
     84    add_form_template = 'custom_admin/add_form.html'
     85    object_history_template = 'custom_admin/object_history.html'
     86    delete_confirmation_template = 'custom_admin/delete_confirmation.html'
     87    delete_selected_confirmation_template = 'custom_admin/delete_selected_confirmation.html'
     88
     89    def changelist_view(self, request):
     90        "Test that extra_context works"
     91        return super(CustomArticleAdmin, self).changelist_view(
     92            request, extra_context={
     93                'extra_var': 'Hello!'
     94            }
     95        )
     96
     97
     98class ThingAdmin(admin.ModelAdmin):
     99    list_filter = ('color__warm', 'color__value')
     100
     101
     102class InquisitionAdmin(admin.ModelAdmin):
     103    list_display = ('leader', 'country', 'expected')
     104
     105
     106class SketchAdmin(admin.ModelAdmin):
     107    raw_id_fields = ('inquisition',)
     108
     109
     110class FabricAdmin(admin.ModelAdmin):
     111    list_display = ('surface',)
     112    list_filter = ('surface',)
     113
     114
     115class BasePersonModelFormSet(BaseModelFormSet):
     116    def clean(self):
     117        for person_dict in self.cleaned_data:
     118            person = person_dict.get('id')
     119            alive = person_dict.get('alive')
     120            if person and alive and person.name == "Grace Hopper":
     121                raise forms.ValidationError("Grace is not a Zombie")
     122
     123
     124class PersonAdmin(admin.ModelAdmin):
     125    list_display = ('name', 'gender', 'alive')
     126    list_editable = ('gender', 'alive')
     127    list_filter = ('gender',)
     128    search_fields = ('^name',)
     129    save_as = True
     130
     131    def get_changelist_formset(self, request, **kwargs):
     132        return super(PersonAdmin, self).get_changelist_formset(request,
     133            formset=BasePersonModelFormSet, **kwargs)
     134
     135    def queryset(self, request):
     136        # Order by a field that isn't in list display, to be able to test
     137        # whether ordering is preserved.
     138        return super(PersonAdmin, self).queryset(request).order_by('age')
     139
     140
     141class FooAccount(Account):
     142    """A service-specific account of type Foo."""
     143    servicename = u'foo'
     144
     145
     146class BarAccount(Account):
     147    """A service-specific account of type Bar."""
     148    servicename = u'bar'
     149
     150
     151class FooAccountAdmin(admin.StackedInline):
     152    model = FooAccount
     153    extra = 1
     154
     155
     156class BarAccountAdmin(admin.StackedInline):
     157    model = BarAccount
     158    extra = 1
     159
     160
     161class PersonaAdmin(admin.ModelAdmin):
     162    inlines = (
     163        FooAccountAdmin,
     164        BarAccountAdmin
     165    )
     166
     167
     168class SubscriberAdmin(admin.ModelAdmin):
     169    actions = ['mail_admin']
     170
     171    def mail_admin(self, request, selected):
     172        EmailMessage(
     173            'Greetings from a ModelAdmin action',
     174            'This is the test email from a admin action',
     175            'from@example.com',
     176            ['to@example.com']
     177        ).send()
     178
     179
     180def external_mail(modeladmin, request, selected):
     181    EmailMessage(
     182        'Greetings from a function action',
     183        'This is the test email from a function action',
     184        'from@example.com',
     185        ['to@example.com']
     186    ).send()
     187external_mail.short_description = 'External mail (Another awesome action)'
     188
     189
     190def redirect_to(modeladmin, request, selected):
     191    from django.http import HttpResponseRedirect
     192    return HttpResponseRedirect('/some-where-else/')
     193redirect_to.short_description = 'Redirect to (Awesome action)'
     194
     195
     196class ExternalSubscriberAdmin(admin.ModelAdmin):
     197    actions = [redirect_to, external_mail]
     198
     199
     200class Podcast(Media):
     201    release_date = models.DateField()
     202
     203    class Meta:
     204        ordering = ('release_date',) # overridden in PodcastAdmin
     205
     206
     207class PodcastAdmin(admin.ModelAdmin):
     208    list_display = ('name', 'release_date')
     209    list_editable = ('release_date',)
     210    date_hierarchy = 'release_date'
     211    ordering = ('name',)
     212
     213
     214class VodcastAdmin(admin.ModelAdmin):
     215    list_display = ('name', 'released')
     216    list_editable = ('released',)
     217
     218    ordering = ('name',)
     219
     220
     221class ChildInline(admin.StackedInline):
     222    model = Child
     223
     224
     225class ParentAdmin(admin.ModelAdmin):
     226    model = Parent
     227    inlines = [ChildInline]
     228
     229    list_editable = ('name',)
     230
     231    def save_related(self, request, form, formsets, change):
     232        super(ParentAdmin, self).save_related(request, form, formsets, change)
     233        first_name, last_name = form.instance.name.split()
     234        for child in form.instance.child_set.all():
     235            if len(child.name.split()) < 2:
     236                child.name = child.name + ' ' + last_name
     237                child.save()
     238
     239
     240class EmptyModelAdmin(admin.ModelAdmin):
     241    def queryset(self, request):
     242        return super(EmptyModelAdmin, self).queryset(request).filter(pk__gt=1)
     243
     244
     245class OldSubscriberAdmin(admin.ModelAdmin):
     246    actions = None
     247
     248
     249temp_storage = FileSystemStorage(tempfile.mkdtemp())
     250UPLOAD_TO = os.path.join(temp_storage.location, 'test_upload')
     251
     252
     253class PictureInline(admin.TabularInline):
     254    model = Picture
     255    extra = 1
     256
     257
     258class GalleryAdmin(admin.ModelAdmin):
     259    inlines = [PictureInline]
     260
     261
     262class PictureAdmin(admin.ModelAdmin):
     263    pass
     264
     265
     266class LanguageAdmin(admin.ModelAdmin):
     267    list_display = ['iso', 'shortlist', 'english_name', 'name']
     268    list_editable = ['shortlist']
     269
     270
     271class RecommendationAdmin(admin.ModelAdmin):
     272    search_fields = ('=titletranslation__text', '=recommender__titletranslation__text',)
     273
     274
     275class WidgetInline(admin.StackedInline):
     276    model = Widget
     277
     278
     279class DooHickeyInline(admin.StackedInline):
     280    model = DooHickey
     281
     282
     283class GrommetInline(admin.StackedInline):
     284    model = Grommet
     285
     286
     287class WhatsitInline(admin.StackedInline):
     288    model = Whatsit
     289
     290
     291class FancyDoodadInline(admin.StackedInline):
     292    model = FancyDoodad
     293
     294
     295class CategoryAdmin(admin.ModelAdmin):
     296    list_display = ('id', 'collector', 'order')
     297    list_editable = ('order',)
     298
     299
     300class CategoryInline(admin.StackedInline):
     301    model = Category
     302
     303
     304class CollectorAdmin(admin.ModelAdmin):
     305    inlines = [
     306        WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline,
     307        FancyDoodadInline, CategoryInline
     308    ]
     309
     310
     311class LinkInline(admin.TabularInline):
     312    model = Link
     313    extra = 1
     314
     315    readonly_fields = ("posted",)
     316
     317
     318class SubPostInline(admin.TabularInline):
     319    model = PrePopulatedSubPost
     320
     321    prepopulated_fields = {
     322        'subslug' : ('subtitle',)
     323    }
     324
     325    def get_readonly_fields(self, request, obj=None):
     326        if obj and obj.published:
     327            return ('subslug',)
     328        return self.readonly_fields
     329
     330    def get_prepopulated_fields(self, request, obj=None):
     331        if obj and obj.published:
     332            return {}
     333        return self.prepopulated_fields
     334
     335
     336class PrePopulatedPostAdmin(admin.ModelAdmin):
     337    list_display = ['title', 'slug']
     338    prepopulated_fields = {
     339        'slug' : ('title',)
     340    }
     341
     342    inlines = [SubPostInline]
     343
     344    def get_readonly_fields(self, request, obj=None):
     345        if obj and obj.published:
     346            return ('slug',)
     347        return self.readonly_fields
     348
     349    def get_prepopulated_fields(self, request, obj=None):
     350        if obj and obj.published:
     351            return {}
     352        return self.prepopulated_fields
     353
     354
     355class PostAdmin(admin.ModelAdmin):
     356    list_display = ['title', 'public']
     357    readonly_fields = ('posted', 'awesomeness_level', 'coolness', 'value', lambda obj: "foo")
     358
     359    inlines = [
     360        LinkInline
     361    ]
     362
     363    def coolness(self, instance):
     364        if instance.pk:
     365            return "%d amount of cool." % instance.pk
     366        else:
     367            return "Unkown coolness."
     368
     369    def value(self, instance):
     370        return 1000
     371    value.short_description = 'Value in $US'
     372
     373
     374class CustomChangeList(ChangeList):
     375    def get_query_set(self, request):
     376        return self.root_query_set.filter(pk=9999) # Does not exist
     377
     378
     379class GadgetAdmin(admin.ModelAdmin):
     380    def get_changelist(self, request, **kwargs):
     381        return CustomChangeList
     382
     383
     384class PizzaAdmin(admin.ModelAdmin):
     385    readonly_fields = ('toppings',)
     386
     387
     388class WorkHourAdmin(admin.ModelAdmin):
     389    list_display = ('datum', 'employee')
     390    list_filter = ('employee',)
     391
     392
     393class FoodDeliveryAdmin(admin.ModelAdmin):
     394    list_display=('reference', 'driver', 'restaurant')
     395    list_editable = ('driver', 'restaurant')
     396
     397
     398class PaperAdmin(admin.ModelAdmin):
     399    """
     400    A ModelAdin with a custom queryset() method that uses only(), to test
     401    verbose_name display in messages shown after adding Paper instances.
     402    """
     403
     404    def queryset(self, request):
     405        return super(PaperAdmin, self).queryset(request).only('title')
     406
     407
     408class CoverLetterAdmin(admin.ModelAdmin):
     409    """
     410    A ModelAdin with a custom queryset() method that uses only(), to test
     411    verbose_name display in messages shown after adding CoverLetter instances.
     412    Note that the CoverLetter model defines a __unicode__ method.
     413    """
     414
     415    def queryset(self, request):
     416        return super(CoverLetterAdmin, self).queryset(request).defer('date_written')
     417
     418
     419class StoryForm(forms.ModelForm):
     420    class Meta:
     421        widgets = {'title': forms.HiddenInput}
     422
     423
     424class StoryAdmin(admin.ModelAdmin):
     425    list_display = ('id', 'title', 'content')
     426    list_display_links = ('title',) # 'id' not in list_display_links
     427    list_editable = ('content', )
     428    form = StoryForm
     429    ordering = ["-pk"]
     430
     431
     432class OtherStoryAdmin(admin.ModelAdmin):
     433    list_display = ('id', 'title', 'content')
     434    list_display_links = ('title', 'id') # 'id' in list_display_links
     435    list_editable = ('content', )
     436    ordering = ["-pk"]
     437
     438
     439class ComplexSortedPersonAdmin(admin.ModelAdmin):
     440    list_display = ('name', 'age', 'is_employee', 'colored_name')
     441    ordering = ('name',)
     442
     443    def colored_name(self, obj):
     444        return '<span style="color: #%s;">%s</span>' % ('ff00ff', obj.name)
     445    colored_name.allow_tags = True
     446    colored_name.admin_order_field = 'name'
     447
     448
     449class AlbumAdmin(admin.ModelAdmin):
     450    list_filter = ['title']
     451 
     452 
     453class WorkHourAdmin(admin.ModelAdmin):
     454    list_display = ('datum', 'employee')
     455    list_filter = ('employee',)
     456
     457
     458site = admin.AdminSite(name="admin")
     459site.register(Article, ArticleAdmin)
     460site.register(CustomArticle, CustomArticleAdmin)
     461site.register(Section, save_as=True, inlines=[ArticleInline])
     462site.register(ModelWithStringPrimaryKey)
     463site.register(Color)
     464site.register(Thing, ThingAdmin)
     465site.register(Actor)
     466site.register(Inquisition, InquisitionAdmin)
     467site.register(Sketch, SketchAdmin)
     468site.register(Person, PersonAdmin)
     469site.register(Persona, PersonaAdmin)
     470site.register(Subscriber, SubscriberAdmin)
     471site.register(ExternalSubscriber, ExternalSubscriberAdmin)
     472site.register(OldSubscriber, OldSubscriberAdmin)
     473site.register(Podcast, PodcastAdmin)
     474site.register(Vodcast, VodcastAdmin)
     475site.register(Parent, ParentAdmin)
     476site.register(EmptyModel, EmptyModelAdmin)
     477site.register(Fabric, FabricAdmin)
     478site.register(Gallery, GalleryAdmin)
     479site.register(Picture, PictureAdmin)
     480site.register(Language, LanguageAdmin)
     481site.register(Recommendation, RecommendationAdmin)
     482site.register(Recommender)
     483site.register(Collector, CollectorAdmin)
     484site.register(Category, CategoryAdmin)
     485site.register(Post, PostAdmin)
     486site.register(Gadget, GadgetAdmin)
     487site.register(Villain)
     488site.register(SuperVillain)
     489site.register(Plot)
     490site.register(PlotDetails)
     491site.register(CyclicOne)
     492site.register(CyclicTwo)
     493site.register(WorkHour, WorkHourAdmin)
     494site.register(Reservation)
     495site.register(FoodDelivery, FoodDeliveryAdmin)
     496site.register(RowLevelChangePermissionModel, RowLevelChangePermissionModelAdmin)
     497site.register(Paper, PaperAdmin)
     498site.register(CoverLetter, CoverLetterAdmin)
     499site.register(Story, StoryAdmin)
     500site.register(OtherStory, OtherStoryAdmin)
     501
     502# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
     503# That way we cover all four cases:
     504#     related ForeignKey object registered in admin
     505#     related ForeignKey object not registered in admin
     506#     related OneToOne object registered in admin
     507#     related OneToOne object not registered in admin
     508# when deleting Book so as exercise all four troublesome (w.r.t escaping
     509# and calling force_unicode to avoid problems on Python 2.3) paths through
     510# contrib.admin.util's get_deleted_objects function.
     511site.register(Book, inlines=[ChapterInline])
     512site.register(Promo)
     513site.register(ChapterXtra1, ChapterXtra1Admin)
     514site.register(Pizza, PizzaAdmin)
     515site.register(Topping)
     516site.register(Album, AlbumAdmin)
     517site.register(Question)
     518site.register(Answer)
     519site.register(PrePopulatedPost, PrePopulatedPostAdmin)
     520site.register(ComplexSortedPerson, ComplexSortedPersonAdmin)
     521
     522# Register core models we need in our tests
     523from django.contrib.auth.models import User, Group
     524from django.contrib.auth.admin import UserAdmin, GroupAdmin
     525site.register(User, UserAdmin)
     526site.register(Group, GroupAdmin)
  • tests/regressiontests/admin_views/customadmin.py

    diff --git a/tests/regressiontests/admin_views/customadmin.py b/tests/regressiontests/admin_views/customadmin.py
    index c31d379..8b2ad65 100644
    a b from django.conf.urls.defaults import patterns 
    55from django.contrib import admin
    66from django.http import HttpResponse
    77
    8 import models, forms
     8import models, forms, admin as base_admin
    99
    1010class Admin2(admin.AdminSite):
    1111    login_form = forms.CustomAdminAuthenticationForm
    class Admin2(admin.AdminSite): 
    2929
    3030site = Admin2(name="admin2")
    3131
    32 site.register(models.Article, models.ArticleAdmin)
    33 site.register(models.Section, inlines=[models.ArticleInline])
    34 site.register(models.Thing, models.ThingAdmin)
    35 site.register(models.Fabric, models.FabricAdmin)
    36 site.register(models.ChapterXtra1, models.ChapterXtra1Admin)
     32site.register(models.Article, base_admin.ArticleAdmin)
     33site.register(models.Section, inlines=[base_admin.ArticleInline])
     34site.register(models.Thing, base_admin.ThingAdmin)
     35site.register(models.Fabric, base_admin.FabricAdmin)
     36site.register(models.ChapterXtra1, base_admin.ChapterXtra1Admin)
  • tests/regressiontests/admin_views/models.py

    diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py
    index 8dc61e3..bb8d026 100644
    a b import datetime 
    33import tempfile
    44import os
    55
    6 from django.contrib import admin
    76from django.core.files.storage import FileSystemStorage
    8 from django.contrib.admin.views.main import ChangeList
    9 from django.core.mail import EmailMessage
    107from django.db import models
    118from django import forms
    12 from django.forms.models import BaseModelFormSet
    139from django.contrib.auth.models import User
    1410from django.contrib.contenttypes import generic
    1511from django.contrib.contenttypes.models import ContentType
    1612
     13
    1714class Section(models.Model):
    1815    """
    1916    A simple section that links to articles, to test linking to related items
    class Section(models.Model): 
    2118    """
    2219    name = models.CharField(max_length=100)
    2320
     21
    2422class Article(models.Model):
    2523    """
    2624    A simple article to test admin views. Test backwards compatibility.
    class Article(models.Model): 
    3836    model_year.admin_order_field = 'date'
    3937    model_year.short_description = ''
    4038
     39
    4140class Book(models.Model):
    4241    """
    4342    A simple book that has chapters.
    class Book(models.Model): 
    4746    def __unicode__(self):
    4847        return self.name
    4948
     49
    5050class Promo(models.Model):
    5151    name = models.CharField(max_length=100, verbose_name=u'¿Name?')
    5252    book = models.ForeignKey(Book)
    class Promo(models.Model): 
    5454    def __unicode__(self):
    5555        return self.name
    5656
     57
    5758class Chapter(models.Model):
    5859    title = models.CharField(max_length=100, verbose_name=u'¿Title?')
    5960    content = models.TextField()
    class Chapter(models.Model): 
    6667        # Use a utf-8 bytestring to ensure it works (see #11710)
    6768        verbose_name = '¿Chapter?'
    6869
     70
    6971class ChapterXtra1(models.Model):
    7072    chap = models.OneToOneField(Chapter, verbose_name=u'¿Chap?')
    7173    xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?')
    class ChapterXtra1(models.Model): 
    7375    def __unicode__(self):
    7476        return u'¿Xtra1: %s' % self.xtra
    7577
     78
    7679class ChapterXtra2(models.Model):
    7780    chap = models.OneToOneField(Chapter, verbose_name=u'¿Chap?')
    7881    xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?')
    class ChapterXtra2(models.Model): 
    8083    def __unicode__(self):
    8184        return u'¿Xtra2: %s' % self.xtra
    8285
    83 def callable_year(dt_value):
    84     return dt_value.year
    85 callable_year.admin_order_field = 'date'
    86 
    87 class ArticleInline(admin.TabularInline):
    88     model = Article
    89 
    90 class ChapterInline(admin.TabularInline):
    91     model = Chapter
    92 
    93 class ChapterXtra1Admin(admin.ModelAdmin):
    94     list_filter = ('chap',
    95                    'chap__title',
    96                    'chap__book',
    97                    'chap__book__name',
    98                    'chap__book__promo',
    99                    'chap__book__promo__name',)
    100 
    101 class ArticleAdmin(admin.ModelAdmin):
    102     list_display = ('content', 'date', callable_year, 'model_year', 'modeladmin_year')
    103     list_filter = ('date', 'section')
    104 
    105     def changelist_view(self, request):
    106         "Test that extra_context works"
    107         return super(ArticleAdmin, self).changelist_view(
    108             request, extra_context={
    109                 'extra_var': 'Hello!'
    110             }
    111         )
    112 
    113     def modeladmin_year(self, obj):
    114         return obj.date.year
    115     modeladmin_year.admin_order_field = 'date'
    116     modeladmin_year.short_description = None
    117 
    118     def delete_model(self, request, obj):
    119         EmailMessage(
    120             'Greetings from a deleted object',
    121             'I hereby inform you that some user deleted me',
    122             'from@example.com',
    123             ['to@example.com']
    124         ).send()
    125         return super(ArticleAdmin, self).delete_model(request, obj)
    126 
    127     def save_model(self, request, obj, form, change=True):
    128         EmailMessage(
    129             'Greetings from a created object',
    130             'I hereby inform you that some user created me',
    131             'from@example.com',
    132             ['to@example.com']
    133         ).send()
    134         return super(ArticleAdmin, self).save_model(request, obj, form, change)
    13586
    13687class RowLevelChangePermissionModel(models.Model):
    13788    name = models.CharField(max_length=100, blank=True)
    13889
    139 class RowLevelChangePermissionModelAdmin(admin.ModelAdmin):
    140     def has_change_permission(self, request, obj=None):
    141         """ Only allow changing objects with even id number """
    142         return request.user.is_staff and (obj is not None) and (obj.id % 2 == 0)
    14390
    14491class CustomArticle(models.Model):
    14592    content = models.TextField()
    14693    date = models.DateTimeField()
    14794
    148 class CustomArticleAdmin(admin.ModelAdmin):
    149     """
    150     Tests various hooks for using custom templates and contexts.
    151     """
    152     change_list_template = 'custom_admin/change_list.html'
    153     change_form_template = 'custom_admin/change_form.html'
    154     add_form_template = 'custom_admin/add_form.html'
    155     object_history_template = 'custom_admin/object_history.html'
    156     delete_confirmation_template = 'custom_admin/delete_confirmation.html'
    157     delete_selected_confirmation_template = 'custom_admin/delete_selected_confirmation.html'
    158 
    159     def changelist_view(self, request):
    160         "Test that extra_context works"
    161         return super(CustomArticleAdmin, self).changelist_view(
    162             request, extra_context={
    163                 'extra_var': 'Hello!'
    164             }
    165         )
    16695
    16796class ModelWithStringPrimaryKey(models.Model):
    16897    id = models.CharField(max_length=255, primary_key=True)
    class ModelWithStringPrimaryKey(models.Model): 
    17099    def __unicode__(self):
    171100        return self.id
    172101
     102
    173103class Color(models.Model):
    174104    value = models.CharField(max_length=10)
    175105    warm = models.BooleanField()
    176106    def __unicode__(self):
    177107        return self.value
    178108
     109
    179110class Thing(models.Model):
    180111    title = models.CharField(max_length=20)
    181112    color = models.ForeignKey(Color, limit_choices_to={'warm': True})
    182113    def __unicode__(self):
    183114        return self.title
    184115
    185 class ThingAdmin(admin.ModelAdmin):
    186     list_filter = ('color__warm', 'color__value')
    187116
    188117class Actor(models.Model):
    189118    name = models.CharField(max_length=50)
    class Actor(models.Model): 
    191120    def __unicode__(self):
    192121        return self.name
    193122
     123
    194124class Inquisition(models.Model):
    195125    expected = models.BooleanField()
    196126    leader = models.ForeignKey(Actor)
    class Inquisition(models.Model): 
    199129    def __unicode__(self):
    200130        return u"by %s from %s" % (self.leader, self.country)
    201131
    202 class InquisitionAdmin(admin.ModelAdmin):
    203     list_display = ('leader', 'country', 'expected')
    204132
    205133class Sketch(models.Model):
    206134    title = models.CharField(max_length=100)
    class Sketch(models.Model): 
    212140    def __unicode__(self):
    213141        return self.title
    214142
    215 class SketchAdmin(admin.ModelAdmin):
    216     raw_id_fields = ('inquisition',)
    217143
    218144class Fabric(models.Model):
    219145    NG_CHOICES = (
    class Fabric(models.Model): 
    226152    )
    227153    surface = models.CharField(max_length=20, choices=NG_CHOICES)
    228154
    229 class FabricAdmin(admin.ModelAdmin):
    230     list_display = ('surface',)
    231     list_filter = ('surface',)
    232155
    233156class Person(models.Model):
    234157    GENDER_CHOICES = (
    class Person(models.Model): 
    243166    def __unicode__(self):
    244167        return self.name
    245168
    246 class BasePersonModelFormSet(BaseModelFormSet):
    247     def clean(self):
    248         for person_dict in self.cleaned_data:
    249             person = person_dict.get('id')
    250             alive = person_dict.get('alive')
    251             if person and alive and person.name == "Grace Hopper":
    252                 raise forms.ValidationError("Grace is not a Zombie")
    253 
    254 class PersonAdmin(admin.ModelAdmin):
    255     list_display = ('name', 'gender', 'alive')
    256     list_editable = ('gender', 'alive')
    257     list_filter = ('gender',)
    258     search_fields = ('^name',)
    259     save_as = True
    260 
    261     def get_changelist_formset(self, request, **kwargs):
    262         return super(PersonAdmin, self).get_changelist_formset(request,
    263             formset=BasePersonModelFormSet, **kwargs)
    264 
    265     def queryset(self, request):
    266         # Order by a field that isn't in list display, to be able to test
    267         # whether ordering is preserved.
    268         return super(PersonAdmin, self).queryset(request).order_by('age')
    269 
    270169
    271170class Persona(models.Model):
    272171    """
    class Persona(models.Model): 
    277176    def __unicode__(self):
    278177        return self.name
    279178
     179
    280180class Account(models.Model):
    281181    """
    282182    A simple, generic account encapsulating the information shared by all
    class Account(models.Model): 
    289189    def __unicode__(self):
    290190        return "%s: %s" % (self.servicename, self.username)
    291191
     192
    292193class FooAccount(Account):
    293194    """A service-specific account of type Foo."""
    294195    servicename = u'foo'
    295196
     197
    296198class BarAccount(Account):
    297199    """A service-specific account of type Bar."""
    298200    servicename = u'bar'
    299201
    300 class FooAccountAdmin(admin.StackedInline):
    301     model = FooAccount
    302     extra = 1
    303 
    304 class BarAccountAdmin(admin.StackedInline):
    305     model = BarAccount
    306     extra = 1
    307 
    308 class PersonaAdmin(admin.ModelAdmin):
    309     inlines = (
    310         FooAccountAdmin,
    311         BarAccountAdmin
    312     )
    313202
    314203class Subscriber(models.Model):
    315204    name = models.CharField(blank=False, max_length=80)
    class Subscriber(models.Model): 
    318207    def __unicode__(self):
    319208        return "%s (%s)" % (self.name, self.email)
    320209
    321 class SubscriberAdmin(admin.ModelAdmin):
    322     actions = ['mail_admin']
    323 
    324     def mail_admin(self, request, selected):
    325         EmailMessage(
    326             'Greetings from a ModelAdmin action',
    327             'This is the test email from a admin action',
    328             'from@example.com',
    329             ['to@example.com']
    330         ).send()
    331210
    332211class ExternalSubscriber(Subscriber):
    333212    pass
    334213
     214
    335215class OldSubscriber(Subscriber):
    336216    pass
    337217
    338 def external_mail(modeladmin, request, selected):
    339     EmailMessage(
    340         'Greetings from a function action',
    341         'This is the test email from a function action',
    342         'from@example.com',
    343         ['to@example.com']
    344     ).send()
    345 external_mail.short_description = 'External mail (Another awesome action)'
    346 
    347 def redirect_to(modeladmin, request, selected):
    348     from django.http import HttpResponseRedirect
    349     return HttpResponseRedirect('/some-where-else/')
    350 redirect_to.short_description = 'Redirect to (Awesome action)'
    351 
    352 class ExternalSubscriberAdmin(admin.ModelAdmin):
    353     actions = [redirect_to, external_mail]
    354218
    355219class Media(models.Model):
    356220    name = models.CharField(max_length=60)
    357221
     222
    358223class Podcast(Media):
    359224    release_date = models.DateField()
    360225
    361226    class Meta:
    362227        ordering = ('release_date',) # overridden in PodcastAdmin
    363228
    364 class PodcastAdmin(admin.ModelAdmin):
    365     list_display = ('name', 'release_date')
    366     list_editable = ('release_date',)
    367     date_hierarchy = 'release_date'
    368     ordering = ('name',)
    369229
    370230class Vodcast(Media):
    371231    media = models.OneToOneField(Media, primary_key=True, parent_link=True)
    372232    released = models.BooleanField(default=False)
    373233
    374 class VodcastAdmin(admin.ModelAdmin):
    375     list_display = ('name', 'released')
    376     list_editable = ('released',)
    377 
    378     ordering = ('name',)
    379234
    380235class Parent(models.Model):
    381236    name = models.CharField(max_length=128)
    382237
     238
    383239class Child(models.Model):
    384240    parent = models.ForeignKey(Parent, editable=False)
    385241    name = models.CharField(max_length=30, blank=True)
    386242
    387 class ChildInline(admin.StackedInline):
    388     model = Child
    389 
    390 class ParentAdmin(admin.ModelAdmin):
    391     model = Parent
    392     inlines = [ChildInline]
    393 
    394     list_editable = ('name',)
    395 
    396     def save_related(self, request, form, formsets, change):
    397         super(ParentAdmin, self).save_related(request, form, formsets, change)
    398         first_name, last_name = form.instance.name.split()
    399         for child in form.instance.child_set.all():
    400             if len(child.name.split()) < 2:
    401                 child.name = child.name + ' ' + last_name
    402                 child.save()
    403243
    404244class EmptyModel(models.Model):
    405245    def __unicode__(self):
    406246        return "Primary key = %s" % self.id
    407247
    408 class EmptyModelAdmin(admin.ModelAdmin):
    409     def queryset(self, request):
    410         return super(EmptyModelAdmin, self).queryset(request).filter(pk__gt=1)
    411 
    412 class OldSubscriberAdmin(admin.ModelAdmin):
    413     actions = None
    414248
    415249temp_storage = FileSystemStorage(tempfile.mkdtemp())
    416250UPLOAD_TO = os.path.join(temp_storage.location, 'test_upload')
    417251
     252
    418253class Gallery(models.Model):
    419254    name = models.CharField(max_length=100)
    420255
     256
    421257class Picture(models.Model):
    422258    name = models.CharField(max_length=100)
    423259    image = models.FileField(storage=temp_storage, upload_to='test_upload')
    424260    gallery = models.ForeignKey(Gallery, related_name="pictures")
    425261
    426 class PictureInline(admin.TabularInline):
    427     model = Picture
    428     extra = 1
    429 
    430 class GalleryAdmin(admin.ModelAdmin):
    431     inlines = [PictureInline]
    432 
    433 class PictureAdmin(admin.ModelAdmin):
    434     pass
    435262
    436263class Language(models.Model):
    437264    iso = models.CharField(max_length=5, primary_key=True)
    class Language(models.Model): 
    442269    class Meta:
    443270        ordering = ('iso',)
    444271
    445 class LanguageAdmin(admin.ModelAdmin):
    446     list_display = ['iso', 'shortlist', 'english_name', 'name']
    447     list_editable = ['shortlist']
    448272
    449273# a base class for Recommender and Recommendation
    450274class Title(models.Model):
    451275    pass
    452276
     277
    453278class TitleTranslation(models.Model):
    454279    title = models.ForeignKey(Title)
    455280    text = models.CharField(max_length=100)
    456281
     282
    457283class Recommender(Title):
    458284    pass
    459285
     286
    460287class Recommendation(Title):
    461288    recommender = models.ForeignKey(Recommender)
    462289
    463 class RecommendationAdmin(admin.ModelAdmin):
    464     search_fields = ('=titletranslation__text', '=recommender__titletranslation__text',)
    465290
    466291class Collector(models.Model):
    467292    name = models.CharField(max_length=100)
    468293
     294
    469295class Widget(models.Model):
    470296    owner = models.ForeignKey(Collector)
    471297    name = models.CharField(max_length=100)
    472298
     299
    473300class DooHickey(models.Model):
    474301    code = models.CharField(max_length=10, primary_key=True)
    475302    owner = models.ForeignKey(Collector)
    476303    name = models.CharField(max_length=100)
    477304
     305
    478306class Grommet(models.Model):
    479307    code = models.AutoField(primary_key=True)
    480308    owner = models.ForeignKey(Collector)
    481309    name = models.CharField(max_length=100)
    482310
     311
    483312class Whatsit(models.Model):
    484313    index = models.IntegerField(primary_key=True)
    485314    owner = models.ForeignKey(Collector)
    486315    name = models.CharField(max_length=100)
    487316
     317
    488318class Doodad(models.Model):
    489319    name = models.CharField(max_length=100)
    490320
     321
    491322class FancyDoodad(Doodad):
    492323    owner = models.ForeignKey(Collector)
    493324    expensive = models.BooleanField(default=True)
    494325
    495 class WidgetInline(admin.StackedInline):
    496     model = Widget
    497 
    498 class DooHickeyInline(admin.StackedInline):
    499     model = DooHickey
    500 
    501 class GrommetInline(admin.StackedInline):
    502     model = Grommet
    503 
    504 class WhatsitInline(admin.StackedInline):
    505     model = Whatsit
    506 
    507 class FancyDoodadInline(admin.StackedInline):
    508     model = FancyDoodad
    509326
    510327class Category(models.Model):
    511328    collector = models.ForeignKey(Collector)
    class Category(models.Model): 
    517334    def __unicode__(self):
    518335        return u'%s:o%s' % (self.id, self.order)
    519336
    520 class CategoryAdmin(admin.ModelAdmin):
    521     list_display = ('id', 'collector', 'order')
    522     list_editable = ('order',)
    523 
    524 class CategoryInline(admin.StackedInline):
    525     model = Category
    526 
    527 class CollectorAdmin(admin.ModelAdmin):
    528     inlines = [
    529         WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline,
    530         FancyDoodadInline, CategoryInline
    531     ]
    532337
    533338class Link(models.Model):
    534339    posted = models.DateField(
    class Link(models.Model): 
    538343    post = models.ForeignKey("Post")
    539344
    540345
    541 class LinkInline(admin.TabularInline):
    542     model = Link
    543     extra = 1
    544 
    545     readonly_fields = ("posted",)
    546 
    547 
    548346class PrePopulatedPost(models.Model):
    549347    title = models.CharField(max_length=100)
    550348    published = models.BooleanField()
    551349    slug = models.SlugField()
    552350
     351
    553352class PrePopulatedSubPost(models.Model):
    554353    post = models.ForeignKey(PrePopulatedPost)
    555354    subtitle = models.CharField(max_length=100)
    556355    subslug = models.SlugField()
    557356
    558 class SubPostInline(admin.TabularInline):
    559     model = PrePopulatedSubPost
    560 
    561     prepopulated_fields = {
    562         'subslug' : ('subtitle',)
    563     }
    564 
    565     def get_readonly_fields(self, request, obj=None):
    566         if obj and obj.published:
    567             return ('subslug',)
    568         return self.readonly_fields
    569 
    570     def get_prepopulated_fields(self, request, obj=None):
    571         if obj and obj.published:
    572             return {}
    573         return self.prepopulated_fields
    574 
    575 class PrePopulatedPostAdmin(admin.ModelAdmin):
    576     list_display = ['title', 'slug']
    577     prepopulated_fields = {
    578         'slug' : ('title',)
    579     }
    580 
    581     inlines = [SubPostInline]
    582 
    583     def get_readonly_fields(self, request, obj=None):
    584         if obj and obj.published:
    585             return ('slug',)
    586         return self.readonly_fields
    587 
    588     def get_prepopulated_fields(self, request, obj=None):
    589         if obj and obj.published:
    590             return {}
    591         return self.prepopulated_fields
    592357
    593358class Post(models.Model):
    594359    title = models.CharField(max_length=100, help_text="Some help text for the title (with unicode ŠĐĆŽćžšđ)")
    class Post(models.Model): 
    602367    def awesomeness_level(self):
    603368        return "Very awesome."
    604369
    605 class PostAdmin(admin.ModelAdmin):
    606     list_display = ['title', 'public']
    607     readonly_fields = ('posted', 'awesomeness_level', 'coolness', 'value', lambda obj: "foo")
    608 
    609     inlines = [
    610         LinkInline
    611     ]
    612 
    613     def coolness(self, instance):
    614         if instance.pk:
    615             return "%d amount of cool." % instance.pk
    616         else:
    617             return "Unkown coolness."
    618 
    619     def value(self, instance):
    620         return 1000
    621     value.short_description = 'Value in $US'
    622370
    623371class Gadget(models.Model):
    624372    name = models.CharField(max_length=100)
    class Gadget(models.Model): 
    626374    def __unicode__(self):
    627375        return self.name
    628376
    629 class CustomChangeList(ChangeList):
    630     def get_query_set(self, request):
    631         return self.root_query_set.filter(pk=9999) # Does not exist
    632 
    633 class GadgetAdmin(admin.ModelAdmin):
    634     def get_changelist(self, request, **kwargs):
    635         return CustomChangeList
    636377
    637378class Villain(models.Model):
    638379    name = models.CharField(max_length=100)
    class Villain(models.Model): 
    640381    def __unicode__(self):
    641382        return self.name
    642383
     384
    643385class SuperVillain(Villain):
    644386    pass
    645387
     388
    646389class FunkyTag(models.Model):
    647390    "Because we all know there's only one real use case for GFKs."
    648391    name = models.CharField(max_length=25)
    class FunkyTag(models.Model): 
    653396    def __unicode__(self):
    654397        return self.name
    655398
     399
    656400class Plot(models.Model):
    657401    name = models.CharField(max_length=100)
    658402    team_leader = models.ForeignKey(Villain, related_name='lead_plots')
    class Plot(models.Model): 
    662406    def __unicode__(self):
    663407        return self.name
    664408
     409
    665410class PlotDetails(models.Model):
    666411    details = models.CharField(max_length=100)
    667412    plot = models.OneToOneField(Plot)
    class PlotDetails(models.Model): 
    669414    def __unicode__(self):
    670415        return self.details
    671416
     417
    672418class SecretHideout(models.Model):
    673419    """ Secret! Not registered with the admin! """
    674420    location = models.CharField(max_length=100)
    class SecretHideout(models.Model): 
    677423    def __unicode__(self):
    678424        return self.location
    679425
     426
    680427class SuperSecretHideout(models.Model):
    681428    """ Secret! Not registered with the admin! """
    682429    location = models.CharField(max_length=100)
    class SuperSecretHideout(models.Model): 
    685432    def __unicode__(self):
    686433        return self.location
    687434
     435
    688436class CyclicOne(models.Model):
    689437    name = models.CharField(max_length=25)
    690438    two = models.ForeignKey('CyclicTwo')
    class CyclicOne(models.Model): 
    692440    def __unicode__(self):
    693441        return self.name
    694442
     443
    695444class CyclicTwo(models.Model):
    696445    name = models.CharField(max_length=25)
    697446    one = models.ForeignKey(CyclicOne)
    class CyclicTwo(models.Model): 
    699448    def __unicode__(self):
    700449        return self.name
    701450
     451
    702452class Topping(models.Model):
    703453    name = models.CharField(max_length=20)
    704454
     455
    705456class Pizza(models.Model):
    706457    name = models.CharField(max_length=20)
    707458    toppings = models.ManyToManyField('Topping')
    708459
    709 class PizzaAdmin(admin.ModelAdmin):
    710     readonly_fields = ('toppings',)
    711460
    712461class Album(models.Model):
    713462    owner = models.ForeignKey(User)
    714463    title = models.CharField(max_length=30)
    715464
    716 class AlbumAdmin(admin.ModelAdmin):
    717     list_filter = ['title']
    718465
    719466class Employee(Person):
    720467    code = models.CharField(max_length=20)
    721468
     469
    722470class WorkHour(models.Model):
    723471    datum = models.DateField()
    724472    employee = models.ForeignKey(Employee)
    725473
    726 class WorkHourAdmin(admin.ModelAdmin):
    727     list_display = ('datum', 'employee')
    728     list_filter = ('employee',)
    729474
    730475class Question(models.Model):
    731476    question = models.CharField(max_length=20)
    732477
     478
    733479class Answer(models.Model):
    734480    question = models.ForeignKey(Question, on_delete=models.PROTECT)
    735481    answer = models.CharField(max_length=20)
    class Answer(models.Model): 
    737483    def __unicode__(self):
    738484        return self.answer
    739485
     486
    740487class Reservation(models.Model):
    741488    start_date = models.DateTimeField()
    742489    price = models.IntegerField()
    RESTAURANT_CHOICES = ( 
    753500    (u'pizza', u'Pizza Mama'),
    754501)
    755502
     503
    756504class FoodDelivery(models.Model):
    757505    reference = models.CharField(max_length=100)
    758506    driver = models.CharField(max_length=100, choices=DRIVER_CHOICES, blank=True)
    class FoodDelivery(models.Model): 
    761509    class Meta:
    762510        unique_together = (("driver", "restaurant"),)
    763511
    764 class FoodDeliveryAdmin(admin.ModelAdmin):
    765     list_display=('reference', 'driver', 'restaurant')
    766     list_editable = ('driver', 'restaurant')
    767512
    768513class Paper(models.Model):
    769514    title = models.CharField(max_length=30)
    770515    author = models.CharField(max_length=30, blank=True, null=True)
    771516
     517
    772518class CoverLetter(models.Model):
    773519    author = models.CharField(max_length=30)
    774520    date_written = models.DateField(null=True, blank=True)
    class CoverLetter(models.Model): 
    776522    def __unicode__(self):
    777523        return self.author
    778524
    779 class PaperAdmin(admin.ModelAdmin):
    780     """
    781     A ModelAdin with a custom queryset() method that uses only(), to test
    782     verbose_name display in messages shown after adding Paper instances.
    783     """
    784 
    785     def queryset(self, request):
    786         return super(PaperAdmin, self).queryset(request).only('title')
    787 
    788 class CoverLetterAdmin(admin.ModelAdmin):
    789     """
    790     A ModelAdin with a custom queryset() method that uses only(), to test
    791     verbose_name display in messages shown after adding CoverLetter instances.
    792     Note that the CoverLetter model defines a __unicode__ method.
    793     """
    794 
    795     def queryset(self, request):
    796         return super(CoverLetterAdmin, self).queryset(request).defer('date_written')
    797525
    798526class Story(models.Model):
    799527    title = models.CharField(max_length=100)
    800528    content = models.TextField()
    801529
    802 class StoryForm(forms.ModelForm):
    803     class Meta:
    804         widgets = {'title': forms.HiddenInput}
    805 
    806 class StoryAdmin(admin.ModelAdmin):
    807     list_display = ('id', 'title', 'content')
    808     list_display_links = ('title',) # 'id' not in list_display_links
    809     list_editable = ('content', )
    810     form = StoryForm
    811     ordering = ["-pk"]
    812530
    813531class OtherStory(models.Model):
    814532    title = models.CharField(max_length=100)
    815533    content = models.TextField()
    816534
    817 class OtherStoryAdmin(admin.ModelAdmin):
    818     list_display = ('id', 'title', 'content')
    819     list_display_links = ('title', 'id') # 'id' in list_display_links
    820     list_editable = ('content', )
    821     ordering = ["-pk"]
    822535
    823536class ComplexSortedPerson(models.Model):
    824537    name = models.CharField(max_length=100)
    825538    age = models.PositiveIntegerField()
    826539    is_employee = models.NullBooleanField()
    827540
    828 class ComplexSortedPersonAdmin(admin.ModelAdmin):
    829     list_display = ('name', 'age', 'is_employee', 'colored_name')
    830     ordering = ('name',)
    831 
    832     def colored_name(self, obj):
    833         return '<span style="color: #%s;">%s</span>' % ('ff00ff', obj.name)
    834     colored_name.allow_tags = True
    835     colored_name.admin_order_field = 'name'
    836 
    837 admin.site.register(Article, ArticleAdmin)
    838 admin.site.register(CustomArticle, CustomArticleAdmin)
    839 admin.site.register(Section, save_as=True, inlines=[ArticleInline])
    840 admin.site.register(ModelWithStringPrimaryKey)
    841 admin.site.register(Color)
    842 admin.site.register(Thing, ThingAdmin)
    843 admin.site.register(Actor)
    844 admin.site.register(Inquisition, InquisitionAdmin)
    845 admin.site.register(Sketch, SketchAdmin)
    846 admin.site.register(Person, PersonAdmin)
    847 admin.site.register(Persona, PersonaAdmin)
    848 admin.site.register(Subscriber, SubscriberAdmin)
    849 admin.site.register(ExternalSubscriber, ExternalSubscriberAdmin)
    850 admin.site.register(OldSubscriber, OldSubscriberAdmin)
    851 admin.site.register(Podcast, PodcastAdmin)
    852 admin.site.register(Vodcast, VodcastAdmin)
    853 admin.site.register(Parent, ParentAdmin)
    854 admin.site.register(EmptyModel, EmptyModelAdmin)
    855 admin.site.register(Fabric, FabricAdmin)
    856 admin.site.register(Gallery, GalleryAdmin)
    857 admin.site.register(Picture, PictureAdmin)
    858 admin.site.register(Language, LanguageAdmin)
    859 admin.site.register(Recommendation, RecommendationAdmin)
    860 admin.site.register(Recommender)
    861 admin.site.register(Collector, CollectorAdmin)
    862 admin.site.register(Category, CategoryAdmin)
    863 admin.site.register(Post, PostAdmin)
    864 admin.site.register(Gadget, GadgetAdmin)
    865 admin.site.register(Villain)
    866 admin.site.register(SuperVillain)
    867 admin.site.register(Plot)
    868 admin.site.register(PlotDetails)
    869 admin.site.register(CyclicOne)
    870 admin.site.register(CyclicTwo)
    871 admin.site.register(WorkHour, WorkHourAdmin)
    872 admin.site.register(Reservation)
    873 admin.site.register(FoodDelivery, FoodDeliveryAdmin)
    874 admin.site.register(RowLevelChangePermissionModel, RowLevelChangePermissionModelAdmin)
    875 admin.site.register(Paper, PaperAdmin)
    876 admin.site.register(CoverLetter, CoverLetterAdmin)
    877 admin.site.register(Story, StoryAdmin)
    878 admin.site.register(OtherStory, OtherStoryAdmin)
    879 
    880 # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
    881 # That way we cover all four cases:
    882 #     related ForeignKey object registered in admin
    883 #     related ForeignKey object not registered in admin
    884 #     related OneToOne object registered in admin
    885 #     related OneToOne object not registered in admin
    886 # when deleting Book so as exercise all four troublesome (w.r.t escaping
    887 # and calling force_unicode to avoid problems on Python 2.3) paths through
    888 # contrib.admin.util's get_deleted_objects function.
    889 admin.site.register(Book, inlines=[ChapterInline])
    890 admin.site.register(Promo)
    891 admin.site.register(ChapterXtra1, ChapterXtra1Admin)
    892 admin.site.register(Pizza, PizzaAdmin)
    893 admin.site.register(Topping)
    894 admin.site.register(Album, AlbumAdmin)
    895 admin.site.register(Question)
    896 admin.site.register(Answer)
    897 admin.site.register(PrePopulatedPost, PrePopulatedPostAdmin)
    898 admin.site.register(ComplexSortedPerson, ComplexSortedPersonAdmin)
  • tests/regressiontests/admin_views/tests.py

    diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
    index 22b65a6..d4b2715 100644
    a b from django.utils import unittest 
    3232
    3333# local test models
    3434from models import (Article, BarAccount, CustomArticle, EmptyModel,
    35     FooAccount, Gallery, PersonAdmin, ModelWithStringPrimaryKey,
     35    FooAccount, Gallery, ModelWithStringPrimaryKey,
    3636    Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast,
    3737    Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit,
    3838    Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee,
    from models import (Article, BarAccount, CustomArticle, EmptyModel, 
    4040    RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory,
    4141    ComplexSortedPerson, Parent, Child)
    4242
     43from admin import PersonAdmin
     44
    4345
    4446class AdminViewBasicTest(TestCase):
    4547    fixtures = ['admin-views-users.xml', 'admin-views-colors.xml',
    class AdminViewBasicTest(TestCase): 
    5052    # this test case and changing urlbit.
    5153    urlbit = 'admin'
    5254
     55    urls = "regressiontests.admin_views.urls"
     56
    5357    def setUp(self):
    5458        self.old_USE_I18N = settings.USE_I18N
    5559        self.old_USE_L10N = settings.USE_L10N
    class AdminViewBasicTest(TestCase): 
    542546            self.fail("Filters should be allowed if they are defined on a ForeignKey pointing to this model")
    543547
    544548class AdminJavaScriptTest(AdminViewBasicTest):
     549    urls = "regressiontests.admin_views.urls"
     550
    545551    def testSingleWidgetFirsFieldFocus(self):
    546552        """
    547553        JavaScript-assisted auto-focus on first field.
    class AdminJavaScriptTest(AdminViewBasicTest): 
    565571
    566572
    567573class SaveAsTests(TestCase):
     574    urls = "regressiontests.admin_views.urls"
    568575    fixtures = ['admin-views-users.xml','admin-views-person.xml']
    569576
    570577    def setUp(self):
    class SaveAsTests(TestCase): 
    590597        self.assertTrue(response.context['save_as'])
    591598        post_data = {'_saveasnew':'', 'name':'John M', 'gender':3, 'alive':'checked'}
    592599        response = self.client.post('/test_admin/admin/admin_views/person/1/', post_data)
    593         self.assertEqual(response.context['form_url'], '../add/')
     600        self.assertEqual(response.context['form_url'], '/test_admin/admin/admin_views/person/add/')
    594601
    595602class CustomModelAdminTest(AdminViewBasicTest):
     603    urls = "regressiontests.admin_views.urls"
    596604    urlbit = "admin2"
    597605
    598606    def testCustomAdminSiteLoginForm(self):
    def get_perm(Model, perm): 
    654662class AdminViewPermissionsTest(TestCase):
    655663    """Tests for Admin Views Permissions."""
    656664
     665    urls = "regressiontests.admin_views.urls"
    657666    fixtures = ['admin-views-users.xml']
    658667
    659668    def setUp(self):
    class AdminViewPermissionsTest(TestCase): 
    835844        self.client.post('/test_admin/admin/', self.adduser_login)
    836845        addpage = self.client.get('/test_admin/admin/admin_views/article/add/')
    837846        self.assertEqual(addpage.status_code, 200)
    838         change_list_link = '<a href="../">Articles</a> &rsaquo;'
     847        change_list_link = '&rsaquo; <a href="/test_admin/admin/admin_views/article/">Articles</a>'
    839848        self.assertFalse(change_list_link in addpage.content,
    840849                    'User restricted to add permission is given link to change list view in breadcrumbs.')
    841850        post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict)
    class AdminViewPermissionsTest(TestCase): 
    10551064
    10561065
    10571066class AdminViewDeletedObjectsTest(TestCase):
     1067    urls = "regressiontests.admin_views.urls"
    10581068    fixtures = ['admin-views-users.xml', 'deleted-objects.xml']
    10591069
    10601070    def setUp(self):
    class AdminViewDeletedObjectsTest(TestCase): 
    11701180        self.assertContains(response, should_contain)
    11711181
    11721182class AdminViewStringPrimaryKeyTest(TestCase):
     1183    urls = "regressiontests.admin_views.urls"
    11731184    fixtures = ['admin-views-users.xml', 'string-primary-key.xml']
    11741185
    11751186    def __init__(self, *args):
    class AdminViewStringPrimaryKeyTest(TestCase): 
    12611272
    12621273
    12631274class SecureViewTests(TestCase):
     1275    urls = "regressiontests.admin_views.urls"
    12641276    fixtures = ['admin-views-users.xml']
    12651277
    12661278    def setUp(self):
    class SecureViewTests(TestCase): 
    14181430        self.assertEqual(response['Location'], 'http://example.com/users/super/')
    14191431
    14201432class AdminViewUnicodeTest(TestCase):
     1433    urls = "regressiontests.admin_views.urls"
    14211434    fixtures = ['admin-views-unicode.xml']
    14221435
    14231436    def setUp(self):
    class AdminViewUnicodeTest(TestCase): 
    14711484
    14721485
    14731486class AdminViewListEditable(TestCase):
     1487    urls = "regressiontests.admin_views.urls"
    14741488    fixtures = ['admin-views-users.xml', 'admin-views-person.xml']
    14751489
    14761490    def setUp(self):
    class AdminViewListEditable(TestCase): 
    18271841
    18281842
    18291843class AdminSearchTest(TestCase):
     1844    urls = "regressiontests.admin_views.urls"
    18301845    fixtures = ['admin-views-users', 'multiple-child-classes',
    18311846                'admin-views-person']
    18321847
    class AdminSearchTest(TestCase): 
    18731888
    18741889
    18751890class AdminInheritedInlinesTest(TestCase):
     1891    urls = "regressiontests.admin_views.urls"
    18761892    fixtures = ['admin-views-users.xml',]
    18771893
    18781894    def setUp(self):
    class AdminInheritedInlinesTest(TestCase): 
    19581974        self.assertEqual(Persona.objects.all()[0].accounts.count(), 2)
    19591975
    19601976class AdminActionsTest(TestCase):
     1977    urls = "regressiontests.admin_views.urls"
    19611978    fixtures = ['admin-views-users.xml', 'admin-views-actions.xml']
    19621979
    19631980    def setUp(self):
    class AdminActionsTest(TestCase): 
    21792196
    21802197
    21812198class TestCustomChangeList(TestCase):
     2199    urls = "regressiontests.admin_views.urls"
    21822200    fixtures = ['admin-views-users.xml']
    21832201    urlbit = 'admin'
    21842202
    class TestCustomChangeList(TestCase): 
    22062224
    22072225
    22082226class TestInlineNotEditable(TestCase):
     2227    urls = "regressiontests.admin_views.urls"
    22092228    fixtures = ['admin-views-users.xml']
    22102229
    22112230    def setUp(self):
    class TestInlineNotEditable(TestCase): 
    22232242        self.assertEqual(response.status_code, 200)
    22242243
    22252244class AdminCustomQuerysetTest(TestCase):
     2245    urls = "regressiontests.admin_views.urls"
    22262246    fixtures = ['admin-views-users.xml']
    22272247
    22282248    def setUp(self):
    class AdminCustomQuerysetTest(TestCase): 
    22772297        self.assertContains(response, '<li class="info">The cover letter &quot;John Doe II&quot; was changed successfully.</li>')
    22782298
    22792299class AdminInlineFileUploadTest(TestCase):
     2300    urls = "regressiontests.admin_views.urls"
    22802301    fixtures = ['admin-views-users.xml', 'admin-views-actions.xml']
    22812302    urlbit = 'admin'
    22822303
    class AdminInlineFileUploadTest(TestCase): 
    23222343
    23232344
    23242345class AdminInlineTests(TestCase):
     2346    urls = "regressiontests.admin_views.urls"
    23252347    fixtures = ['admin-views-users.xml']
    23262348
    23272349    def setUp(self):
    class AdminInlineTests(TestCase): 
    26392661
    26402662
    26412663class NeverCacheTests(TestCase):
     2664    urls = "regressiontests.admin_views.urls"
    26422665    fixtures = ['admin-views-users.xml', 'admin-views-colors.xml', 'admin-views-fabrics.xml']
    26432666
    26442667    def setUp(self):
    class NeverCacheTests(TestCase): 
    27112734
    27122735
    27132736class PrePopulatedTest(TestCase):
     2737    urls = "regressiontests.admin_views.urls"
    27142738    fixtures = ['admin-views-users.xml']
    27152739
    27162740    def setUp(self):
    class PrePopulatedTest(TestCase): 
    27352759        self.assertNotContains(response, "id: '#id_prepopulatedsubpost_set-0-subslug',")
    27362760
    27372761class ReadonlyTest(TestCase):
     2762    urls = "regressiontests.admin_views.urls"
    27382763    fixtures = ['admin-views-users.xml']
    27392764
    27402765    def setUp(self):
    class ReadonlyTest(TestCase): 
    28022827
    28032828
    28042829class RawIdFieldsTest(TestCase):
     2830    urls = "regressiontests.admin_views.urls"
    28052831    fixtures = ['admin-views-users.xml']
    28062832
    28072833    def setUp(self):
    class UserAdminTest(TestCase): 
    28372863    """
    28382864    Tests user CRUD functionality.
    28392865    """
     2866    urls = "regressiontests.admin_views.urls"
    28402867    fixtures = ['admin-views-users.xml']
    28412868
    28422869    def setUp(self):
    class GroupAdminTest(TestCase): 
    29282955    """
    29292956    Tests group CRUD functionality.
    29302957    """
     2958    urls = "regressiontests.admin_views.urls"
    29312959    fixtures = ['admin-views-users.xml']
    29322960
    29332961    def setUp(self):
    except ImportError: 
    29612989
    29622990#@unittest.skipUnless(docutils, "no docutils installed.")
    29632991class AdminDocsTest(TestCase):
     2992    urls = "regressiontests.admin_views.urls"
    29642993    fixtures = ['admin-views-users.xml']
    29652994
    29662995    def setUp(self):
    class AdminDocsTest(TestCase): 
    30033032AdminDocsTest = unittest.skipUnless(docutils, "no docutils installed.")(AdminDocsTest)
    30043033
    30053034class ValidXHTMLTests(TestCase):
     3035    urls = "regressiontests.admin_views.urls"
    30063036    fixtures = ['admin-views-users.xml']
    30073037    urlbit = 'admin'
    30083038
    class ValidXHTMLTests(TestCase): 
    30333063
    30343064
    30353065class DateHierarchyTests(TestCase):
     3066    urls = "regressiontests.admin_views.urls"
    30363067    fixtures = ['admin-views-users.xml']
    30373068
    30383069    def setUp(self):
    class AdminCustomSaveRelatedTests(TestCase): 
    31623193    Ensure that one can easily customize the way related objects are saved.
    31633194    Refs #16115.
    31643195    """
     3196    urls = "regressiontests.admin_views.urls"
    31653197    fixtures = ['admin-views-users.xml']
    31663198
    31673199    def setUp(self):
  • tests/regressiontests/admin_views/urls.py

    diff --git a/tests/regressiontests/admin_views/urls.py b/tests/regressiontests/admin_views/urls.py
    index f3f1fbd..05be6a6 100644
    a b from django.conf.urls.defaults import * 
    22from django.contrib import admin
    33import views
    44import customadmin
     5import admin
    56
    67urlpatterns = patterns('',
    7     (r'^admin/doc/', include('django.contrib.admindocs.urls')),
    8     (r'^admin/secure-view/$', views.secure_view),
    9     (r'^admin/', include(admin.site.urls)),
    10     (r'^admin2/', include(customadmin.site.urls)),
     8    (r'^test_admin/admin/doc/', include('django.contrib.admindocs.urls')),
     9    (r'^test_admin/admin/secure-view/$', views.secure_view),
     10    (r'^test_admin/admin/', include(admin.site.urls)),
     11    (r'^test_admin/admin2/', include(customadmin.site.urls)),
    1112)
  • tests/regressiontests/cache/tests.py

    diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
    index d307030..07bcab7 100644
    a b TestWithTemplateResponse = override_settings( 
    15911591
    15921592class TestEtagWithAdmin(TestCase):
    15931593    # See https://code.djangoproject.com/ticket/16003
     1594    urls = "regressiontests.admin_views.urls"
     1595
    15941596    def test_admin(self):
    15951597        with self.settings(USE_ETAGS=False):
    15961598            response = self.client.get('/test_admin/admin/')
  • new file tests/regressiontests/generic_inline_admin/admin.py

    diff --git a/tests/regressiontests/generic_inline_admin/admin.py b/tests/regressiontests/generic_inline_admin/admin.py
    new file mode 100644
    index 0000000..87224ed
    - +  
     1from django.contrib import admin
     2from django.contrib.contenttypes import generic
     3
     4from models import (Media, PhoneNumber, Episode, EpisodeExtra, Contact,
     5    Category, EpisodePermanent, EpisodeMaxNum)
     6
     7site = admin.AdminSite(name="admin")
     8
     9class MediaInline(generic.GenericTabularInline):
     10    model = Media
     11
     12
     13class EpisodeAdmin(admin.ModelAdmin):
     14    inlines = [
     15        MediaInline,
     16    ]
     17
     18
     19class MediaExtraInline(generic.GenericTabularInline):
     20    model = Media
     21    extra = 0
     22
     23
     24class MediaMaxNumInline(generic.GenericTabularInline):
     25    model = Media
     26    extra = 5
     27    max_num = 2
     28
     29
     30class PhoneNumberInline(generic.GenericTabularInline):
     31    model = PhoneNumber
     32
     33
     34class MediaPermanentInline(generic.GenericTabularInline):
     35    model = Media
     36    can_delete = False
     37
     38
     39site.register(Episode, EpisodeAdmin)
     40site.register(EpisodeExtra, inlines=[MediaExtraInline])
     41site.register(EpisodeMaxNum, inlines=[MediaMaxNumInline])
     42site.register(Contact, inlines=[PhoneNumberInline])
     43site.register(Category)
     44site.register(EpisodePermanent, inlines=[MediaPermanentInline])
  • tests/regressiontests/generic_inline_admin/models.py

    diff --git a/tests/regressiontests/generic_inline_admin/models.py b/tests/regressiontests/generic_inline_admin/models.py
    index 32ecd3b..e78f110 100644
    a b  
    11from django.db import models
    2 from django.contrib import admin
    32from django.contrib.contenttypes import generic
    43from django.contrib.contenttypes.models import ContentType
    54
     5
    66class Episode(models.Model):
    77    name = models.CharField(max_length=100)
    88    length = models.CharField(max_length=100, blank=True)
    99    author = models.CharField(max_length=100, blank=True)
    1010
     11
    1112class Media(models.Model):
    1213    """
    1314    Media that can associated to any object.
    class Media(models.Model): 
    2223    def __unicode__(self):
    2324        return self.url
    2425
    25 class MediaInline(generic.GenericTabularInline):
    26     model = Media
    27 
    28 class EpisodeAdmin(admin.ModelAdmin):
    29     inlines = [
    30         MediaInline,
    31     ]
    32 admin.site.register(Episode, EpisodeAdmin)
    33 
    3426#
    3527# These models let us test the different GenericInline settings at
    3628# different urls in the admin site.
    admin.site.register(Episode, EpisodeAdmin) 
    4335class EpisodeExtra(Episode):
    4436    pass
    4537
    46 class MediaExtraInline(generic.GenericTabularInline):
    47     model = Media
    48     extra = 0
    49 
    50 admin.site.register(EpisodeExtra, inlines=[MediaExtraInline])
    5138
    5239#
    5340# Generic inline with extra and max_num
    5441#
    55 
    5642class EpisodeMaxNum(Episode):
    5743    pass
    5844
    59 class MediaMaxNumInline(generic.GenericTabularInline):
    60     model = Media
    61     extra = 5
    62     max_num = 2
    63 
    64 admin.site.register(EpisodeMaxNum, inlines=[MediaMaxNumInline])
    65 
    6645
    6746#
    6847# Generic inline with unique_together
    6948#
    70 
    7149class Category(models.Model):
    7250    name = models.CharField(max_length=50)
    7351
     52
    7453class PhoneNumber(models.Model):
    7554    content_type = models.ForeignKey(ContentType)
    7655    object_id = models.PositiveIntegerField()
    class PhoneNumber(models.Model): 
    8160    class Meta:
    8261        unique_together = (('content_type', 'object_id', 'phone_number',),)
    8362
     63
    8464class Contact(models.Model):
    8565    name = models.CharField(max_length=50)
    8666    phone_numbers = generic.GenericRelation(PhoneNumber)
    8767
    88 class PhoneNumberInline(generic.GenericTabularInline):
    89     model = PhoneNumber
    90 
    91 admin.site.register(Contact, inlines=[PhoneNumberInline])
    92 admin.site.register(Category)
    93 
    9468#
    9569# Generic inline with can_delete=False
    9670#
    97 
    9871class EpisodePermanent(Episode):
    9972    pass
    10073
    101 class MediaPermanentInline(generic.GenericTabularInline):
    102     model = Media
    103     can_delete = False
    10474
    105 admin.site.register(EpisodePermanent, inlines=[MediaPermanentInline])
  • tests/regressiontests/generic_inline_admin/tests.py

    diff --git a/tests/regressiontests/generic_inline_admin/tests.py b/tests/regressiontests/generic_inline_admin/tests.py
    index da59922..858d6a5 100644
    a b from django.test import TestCase 
    1010
    1111# local test models
    1212from models import (Episode, EpisodeExtra, EpisodeMaxNum, Media,
    13     MediaInline, EpisodePermanent, MediaPermanentInline, Category)
     13    EpisodePermanent, Category)
     14from admin import MediaInline, MediaPermanentInline
    1415
    1516
    1617class GenericAdminViewTest(TestCase):
     18    urls = "regressiontests.generic_inline_admin.urls"
    1719    fixtures = ['users.xml']
    1820
    1921    def setUp(self):
    class GenericAdminViewTest(TestCase): 
    125127        self.assertTrue(formset.get_queryset().ordered)
    126128
    127129class GenericInlineAdminParametersTest(TestCase):
     130    urls = "regressiontests.generic_inline_admin.urls"
    128131    fixtures = ['users.xml']
    129132
    130133    def setUp(self):
    class GenericInlineAdminParametersTest(TestCase): 
    177180
    178181
    179182class GenericInlineAdminWithUniqueTogetherTest(TestCase):
     183    urls = "regressiontests.generic_inline_admin.urls"
    180184    fixtures = ['users.xml']
    181185
    182186    def setUp(self):
    class GenericInlineAdminWithUniqueTogetherTest(TestCase): 
    203207        self.assertEqual(response.status_code, 302) # redirect somewhere
    204208
    205209class NoInlineDeletionTest(TestCase):
     210    urls = "regressiontests.generic_inline_admin.urls"
     211
    206212    def test_no_deletion(self):
    207213        fake_site = object()
    208214        inline = MediaPermanentInline(EpisodePermanent, fake_site)
    class NoInlineDeletionTest(TestCase): 
    211217        self.assertFalse(formset.can_delete)
    212218
    213219class GenericInlineModelAdminTest(TestCase):
     220    urls = "regressiontests.generic_inline_admin.urls"
    214221
    215222    def setUp(self):
    216223        self.site = AdminSite()
  • tests/regressiontests/generic_inline_admin/urls.py

    diff --git a/tests/regressiontests/generic_inline_admin/urls.py b/tests/regressiontests/generic_inline_admin/urls.py
    index c3e8af8..f3a4b50 100644
    a b  
    11from django.conf.urls.defaults import *
    2 from django.contrib import admin
     2
     3import admin
    34
    45urlpatterns = patterns('',
    5     (r'^admin/', include(admin.site.urls)),
     6    (r'^generic_inline_admin/admin/', include(admin.site.urls)),
    67)
  • tests/urls.py

    diff --git a/tests/urls.py b/tests/urls.py
    index b3f719d..96fee42 100644
    a b  
    11from django.conf.urls.defaults import *
    22
    3 
    43urlpatterns = patterns('',
    54    # test_client modeltest urls
    65    (r'^test_client/', include('modeltests.test_client.urls')),
    urlpatterns = patterns('', 
    2221    # test urlconf for middleware tests
    2322    (r'^middleware/', include('regressiontests.middleware.urls')),
    2423
    25     # admin view tests
    26     (r'^test_admin/', include('regressiontests.admin_views.urls')),
    27     (r'^generic_inline_admin/', include('regressiontests.generic_inline_admin.urls')),
    28 
    2924    # admin widget tests
    3025    (r'widget_admin/', include('regressiontests.admin_widgets.urls')),
    3126
     27    # admin custom URL tests
     28    (r'^custom_urls/', include('regressiontests.admin_custom_urls.urls')),
     29
    3230)
Back to Top