summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRamiro Morales <cramm0@gmail.com>2011-09-20 18:30:06 +0000
committerRamiro Morales <cramm0@gmail.com>2011-09-20 18:30:06 +0000
commitaaf77c1676e44019abe544911ff7a06eb2690295 (patch)
treef73ab5be90d41006ee52ca8942cfb542ea662a0f
parent7b21bfc0745f92f83a6826a3d3e58797fb74e326 (diff)
Converted internal link generation in the admin and admin document generator to use named URLs.
Thanks to Florian Apolloner for both the initial patch and his final push to get this fixed, to Dario Ocles for his great work on the admin templates and switching the admin_doc application to also use named URLs, to Mikko Hellsing for his comments and to Jannis and Julien for their review and design guidance. Fixes #15294. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16857 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/contrib/admin/options.py42
-rw-r--r--django/contrib/admin/sites.py10
-rw-r--r--django/contrib/admin/templates/admin/500.html8
-rw-r--r--django/contrib/admin/templates/admin/app_index.html20
-rw-r--r--django/contrib/admin/templates/admin/auth/user/change_password.html19
-rw-r--r--django/contrib/admin/templates/admin/base.html13
-rw-r--r--django/contrib/admin/templates/admin/change_form.html16
-rw-r--r--django/contrib/admin/templates/admin/change_list.html24
-rw-r--r--django/contrib/admin/templates/admin/delete_confirmation.html12
-rw-r--r--django/contrib/admin/templates/admin/delete_selected_confirmation.html10
-rw-r--r--django/contrib/admin/templates/admin/index.html2
-rw-r--r--django/contrib/admin/templates/admin/invalid_setup.html8
-rw-r--r--django/contrib/admin/templates/admin/object_history.html12
-rw-r--r--django/contrib/admin/templates/registration/logged_out.html5
-rw-r--r--django/contrib/admin/templates/registration/password_change_done.html9
-rw-r--r--django/contrib/admin/templates/registration/password_change_form.html9
-rw-r--r--django/contrib/admin/templates/registration/password_reset_complete.html8
-rw-r--r--django/contrib/admin/templates/registration/password_reset_confirm.html8
-rw-r--r--django/contrib/admin/templates/registration/password_reset_done.html8
-rw-r--r--django/contrib/admin/templates/registration/password_reset_form.html8
-rw-r--r--django/contrib/admin/templatetags/admin_urls.py8
-rw-r--r--django/contrib/admindocs/templates/admin_doc/bookmarklets.html10
-rw-r--r--django/contrib/admindocs/templates/admin_doc/index.html9
-rw-r--r--django/contrib/admindocs/templates/admin_doc/missing_docutils.html8
-rw-r--r--django/contrib/admindocs/templates/admin_doc/model_detail.html13
-rw-r--r--django/contrib/admindocs/templates/admin_doc/model_index.html13
-rw-r--r--django/contrib/admindocs/templates/admin_doc/template_detail.html13
-rw-r--r--django/contrib/admindocs/templates/admin_doc/template_filter_index.html10
-rw-r--r--django/contrib/admindocs/templates/admin_doc/template_tag_index.html10
-rw-r--r--django/contrib/admindocs/templates/admin_doc/view_detail.html13
-rw-r--r--django/contrib/admindocs/templates/admin_doc/view_index.html14
-rw-r--r--django/contrib/admindocs/views.py3
-rw-r--r--docs/ref/contrib/admin/index.txt15
-rw-r--r--tests/regressiontests/admin_custom_urls/__init__.py1
-rw-r--r--tests/regressiontests/admin_custom_urls/fixtures/actions.json44
-rw-r--r--tests/regressiontests/admin_custom_urls/fixtures/users.json20
-rw-r--r--tests/regressiontests/admin_custom_urls/models.py50
-rw-r--r--tests/regressiontests/admin_custom_urls/tests.py72
-rw-r--r--tests/regressiontests/admin_custom_urls/urls.py7
-rw-r--r--tests/regressiontests/admin_views/tests.py4
-rw-r--r--tests/urls.py4
41 files changed, 488 insertions, 104 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 5271e021ef..2825c99988 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -11,6 +11,7 @@ from django.contrib import messages
from django.views.decorators.csrf import csrf_protect
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.paginator import Paginator
+from django.core.urlresolvers import reverse
from django.db import models, transaction, router
from django.db.models.related import RelatedObject
from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist
@@ -776,9 +777,12 @@ class ModelAdmin(BaseModelAdmin):
# redirect to the change-list page for this object. Otherwise,
# redirect to the admin index.
if self.has_change_permission(request, None):
- post_url = '../'
+ post_url = reverse('admin:%s_%s_changelist' %
+ (opts.app_label, opts.module_name),
+ current_app=self.admin_site.name)
else:
- post_url = '../../../'
+ post_url = reverse('admin:index',
+ current_app=self.admin_site.name)
return HttpResponseRedirect(post_url)
def response_change(self, request, obj):
@@ -787,11 +791,14 @@ class ModelAdmin(BaseModelAdmin):
"""
opts = obj._meta
- # Handle proxy models automatically created by .only() or .defer()
+ # Handle proxy models automatically created by .only() or .defer().
+ # Refs #14529
verbose_name = opts.verbose_name
+ module_name = opts.module_name
if obj._deferred:
opts_ = opts.proxy_for_model._meta
verbose_name = opts_.verbose_name
+ module_name = opts_.module_name
pk_value = obj._get_pk_val()
@@ -805,19 +812,28 @@ class ModelAdmin(BaseModelAdmin):
elif "_saveasnew" in request.POST:
msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(verbose_name), 'obj': obj}
self.message_user(request, msg)
- return HttpResponseRedirect("../%s/" % pk_value)
+ return HttpResponseRedirect(reverse('admin:%s_%s_change' %
+ (opts.app_label, module_name),
+ args=(pk_value,),
+ current_app=self.admin_site.name))
elif "_addanother" in request.POST:
self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(verbose_name)))
- return HttpResponseRedirect("../add/")
+ return HttpResponseRedirect(reverse('admin:%s_%s_add' %
+ (opts.app_label, module_name),
+ current_app=self.admin_site.name))
else:
self.message_user(request, msg)
# Figure out where to redirect. If the user has change permission,
# redirect to the change-list page for this object. Otherwise,
# redirect to the admin index.
if self.has_change_permission(request, None):
- return HttpResponseRedirect('../')
+ post_url = reverse('admin:%s_%s_changelist' %
+ (opts.app_label, module_name),
+ current_app=self.admin_site.name)
else:
- return HttpResponseRedirect('../../../')
+ post_url = reverse('admin:index',
+ current_app=self.admin_site.name)
+ return HttpResponseRedirect(post_url)
def response_action(self, request, queryset):
"""
@@ -990,7 +1006,9 @@ class ModelAdmin(BaseModelAdmin):
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)})
if request.method == 'POST' and "_saveasnew" in request.POST:
- return self.add_view(request, form_url='../add/')
+ return self.add_view(request, form_url=reverse('admin:%s_%s_add' %
+ (opts.app_label, opts.module_name),
+ current_app=self.admin_site.name))
ModelForm = self.get_form(request, obj)
formsets = []
@@ -1246,8 +1264,11 @@ class ModelAdmin(BaseModelAdmin):
self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)})
if not self.has_change_permission(request, None):
- return HttpResponseRedirect("../../../../")
- return HttpResponseRedirect("../../")
+ return HttpResponseRedirect(reverse('admin:index',
+ current_app=self.admin_site.name))
+ return HttpResponseRedirect(reverse('admin:%s_%s_changelist' %
+ (opts.app_label, opts.module_name),
+ current_app=self.admin_site.name))
object_name = force_unicode(opts.verbose_name)
@@ -1292,6 +1313,7 @@ class ModelAdmin(BaseModelAdmin):
'module_name': capfirst(force_unicode(opts.verbose_name_plural)),
'object': obj,
'app_label': app_label,
+ 'opts': opts,
}
context.update(extra_context or {})
return TemplateResponse(request, self.object_history_template or [
diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
index 9bb9f4ab46..3a5c12b70a 100644
--- a/django/contrib/admin/sites.py
+++ b/django/contrib/admin/sites.py
@@ -339,9 +339,11 @@ class AdminSite(object):
# Check whether user has any perm for this module.
# If so, add the module to the model_list.
if True in perms.values():
+ info = (app_label, model._meta.module_name)
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
- 'admin_url': mark_safe('%s/%s/' % (app_label, model.__name__.lower())),
+ 'admin_url': reverse('admin:%s_%s_changelist' % info, current_app=self.name),
+ 'add_url': reverse('admin:%s_%s_add' % info, current_app=self.name),
'perms': perms,
}
if app_label in app_dict:
@@ -349,7 +351,7 @@ class AdminSite(object):
else:
app_dict[app_label] = {
'name': app_label.title(),
- 'app_url': app_label + '/',
+ 'app_url': reverse('admin:app_list', kwargs={'app_label': app_label}, current_app=self.name),
'has_module_perms': has_module_perms,
'models': [model_dict],
}
@@ -383,9 +385,11 @@ class AdminSite(object):
# Check whether user has any perm for this module.
# If so, add the module to the model_list.
if True in perms.values():
+ info = (app_label, model._meta.module_name)
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
- 'admin_url': '%s/' % model.__name__.lower(),
+ 'admin_url': reverse('admin:%s_%s_changelist' % info, current_app=self.name),
+ 'add_url': reverse('admin:%s_%s_add' % info, current_app=self.name),
'perms': perms,
}
if app_dict:
diff --git a/django/contrib/admin/templates/admin/500.html b/django/contrib/admin/templates/admin/500.html
index b30e43170d..eeb9e8da79 100644
--- a/django/contrib/admin/templates/admin/500.html
+++ b/django/contrib/admin/templates/admin/500.html
@@ -1,7 +1,13 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="/">{% trans "Home" %}</a> &rsaquo; {% trans "Server error" %}</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; {% trans 'Server error' %}
+</div>
+{% endblock %}
{% block title %}{% trans 'Server error (500)' %}{% endblock %}
diff --git a/django/contrib/admin/templates/admin/app_index.html b/django/contrib/admin/templates/admin/app_index.html
index 120433d708..4616b16d3d 100644
--- a/django/contrib/admin/templates/admin/app_index.html
+++ b/django/contrib/admin/templates/admin/app_index.html
@@ -1,15 +1,17 @@
-{% extends "admin/index.html" %}
-{% load i18n %}
+{% extends "admin/index.html" %}
+{% load i18n %}
+{% load url from future %}
{% if not is_popup %}
-
{% block breadcrumbs %}
-<div class="breadcrumbs"><a href="../">
-{% trans "Home" %}</a> &rsaquo;
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo;
{% for app in app_list %}
{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}
-{% endfor %}</div>{% endblock %}
-
-{% endif %}
+{% endfor %}
+</div>
+{% endblock %}
+{% endif %}
-{% block sidebar %}{% endblock %} \ No newline at end of file
+{% block sidebar %}{% endblock %}
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 b18b0aabf3..f79eacd459 100644
--- a/django/contrib/admin/templates/admin/auth/user/change_password.html
+++ b/django/contrib/admin/templates/admin/auth/user/change_password.html
@@ -1,21 +1,24 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_static admin_modify %}
{% load url from future %}
+{% load admin_urls %}
+
{% block extrahead %}{{ block.super }}
{% url 'admin:jsi18n' as jsi18nurl %}
<script type="text/javascript" src="{{ jsi18nurl|default:"../../../../jsi18n/" }}"></script>
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
-{% block breadcrumbs %}{% if not is_popup %}
-<div class="breadcrumbs">
- <a href="../../../../">{% trans "Home" %}</a> &rsaquo;
- <a href="../../../">{{ opts.app_label|capfirst|escape }}</a> &rsaquo;
- <a href="../../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
- <a href="../">{{ original|truncatewords:"18" }}</a> &rsaquo;
- {% trans 'Change password' %}
+{% if not is_popup %}
+{% block breadcrumbs %}
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_label|capfirst|escape }}</a>
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}{{ original.pk }}">{{ original|truncatewords:"18" }}</a>
+&rsaquo; {% trans 'Change password' %}
</div>
-{% endif %}{% endblock %}
+{% endblock %}
+{% endif %}
{% block content %}<div id="content-main">
<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %}
<div>
diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html
index 4b3c42934f..3b50adcd6e 100644
--- a/django/contrib/admin/templates/admin/base.html
+++ b/django/contrib/admin/templates/admin/base.html
@@ -32,17 +32,20 @@
{% if docsroot %}
<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> /
{% endif %}
- <a href="{% url 'admin:password_change' %}">
- {% trans 'Change password' %}</a> /
- <a href="{% url 'admin:logout' %}">
- {% trans 'Log out' %}</a>
+ <a href="{% url 'admin:password_change' %}">{% trans 'Change password' %}</a> /
+ <a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>
{% endblock %}
</div>
{% endif %}
{% block nav-global %}{% endblock %}
</div>
<!-- END Header -->
- {% block breadcrumbs %}<div class="breadcrumbs"><a href="/">{% trans 'Home' %}</a>{% if title %} &rsaquo; {{ title }}{% endif %}</div>{% endblock %}
+ {% block breadcrumbs %}
+ <div class="breadcrumbs">
+ <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+ {% if title %} &rsaquo; {{ title }}{% endif %}
+ </div>
+ {% endblock %}
{% endif %}
{% block messages %}
diff --git a/django/contrib/admin/templates/admin/change_form.html b/django/contrib/admin/templates/admin/change_form.html
index 56661e9a8e..0e2803d671 100644
--- a/django/contrib/admin/templates/admin/change_form.html
+++ b/django/contrib/admin/templates/admin/change_form.html
@@ -1,6 +1,7 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_static admin_modify %}
{% load url from future %}
+{% load admin_urls %}
{% block extrahead %}{{ block.super }}
{% url 'admin:jsi18n' as jsi18nurl %}
@@ -14,14 +15,15 @@
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
-{% block breadcrumbs %}{% if not is_popup %}
-<div class="breadcrumbs">
- <a href="../../../">{% trans "Home" %}</a> &rsaquo;
- <a href="../../">{{ app_label|capfirst|escape }}</a> &rsaquo;
- {% if has_change_permission %}<a href="../">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %} &rsaquo;
- {% if add %}{% trans "Add" %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
+{% if not is_popup %}
+{% block breadcrumbs %}
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst|escape }}</a>
+&rsaquo; {% if has_change_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
+&rsaquo; {% if add %}{% trans 'Add' %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
</div>
-{% endif %}{% endblock %}
+{% endblock %}
+{% endif %}
{% block content %}<div id="content-main">
{% block object-tools %}
diff --git a/django/contrib/admin/templates/admin/change_list.html b/django/contrib/admin/templates/admin/change_list.html
index 24c6d8cced..98869cfa5c 100644
--- a/django/contrib/admin/templates/admin/change_list.html
+++ b/django/contrib/admin/templates/admin/change_list.html
@@ -1,6 +1,8 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_static admin_list %}
{% load url from future %}
+{% load admin_urls %}
+
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}" />
@@ -36,19 +38,13 @@
{% block bodyclass %}change-list{% endblock %}
{% if not is_popup %}
- {% block breadcrumbs %}
- <div class="breadcrumbs">
- <a href="../../">
- {% trans "Home" %}
- </a>
- &rsaquo;
- <a href="../">
- {{ app_label|capfirst }}
- </a>
- &rsaquo;
- {{ cl.opts.verbose_name_plural|capfirst }}
- </div>
- {% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ app_label|capfirst|escape }}</a>
+&rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}
+</div>
+{% endblock %}
{% endif %}
{% block coltype %}flex{% endblock %}
@@ -60,7 +56,7 @@
<ul class="object-tools">
{% block object-tools-items %}
<li>
- <a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">
+ <a href="{% url cl.opts|admin_urlname:'add' %}{% if is_popup %}?_popup=1{% endif %}" class="addlink">
{% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}
</a>
</li>
diff --git a/django/contrib/admin/templates/admin/delete_confirmation.html b/django/contrib/admin/templates/admin/delete_confirmation.html
index 0e6d47e68d..f8b36e9c40 100644
--- a/django/contrib/admin/templates/admin/delete_confirmation.html
+++ b/django/contrib/admin/templates/admin/delete_confirmation.html
@@ -1,13 +1,15 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
+{% load admin_urls %}
{% block breadcrumbs %}
<div class="breadcrumbs">
- <a href="../../../../">{% trans "Home" %}</a> &rsaquo;
- <a href="../../../">{{ app_label|capfirst }}</a> &rsaquo;
- <a href="../../">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
- <a href="../">{{ object|truncatewords:"18" }}</a> &rsaquo;
- {% trans 'Delete' %}
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst }}</a>
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a>
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}{{ object.pk }}">{{ object|truncatewords:"18" }}</a>
+&rsaquo; {% trans 'Delete' %}
</div>
{% endblock %}
diff --git a/django/contrib/admin/templates/admin/delete_selected_confirmation.html b/django/contrib/admin/templates/admin/delete_selected_confirmation.html
index 127519bea7..3120cc5289 100644
--- a/django/contrib/admin/templates/admin/delete_selected_confirmation.html
+++ b/django/contrib/admin/templates/admin/delete_selected_confirmation.html
@@ -1,12 +1,14 @@
{% extends "admin/base_site.html" %}
{% load i18n l10n %}
+{% load url from future %}
+{% load admin_urls %}
{% block breadcrumbs %}
<div class="breadcrumbs">
- <a href="../../">{% trans "Home" %}</a> &rsaquo;
- <a href="../">{{ app_label|capfirst }}</a> &rsaquo;
- <a href="./">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo;
- {% trans 'Delete multiple objects' %}
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
+&rsaquo; {% trans 'Delete multiple objects' %}
</div>
{% endblock %}
diff --git a/django/contrib/admin/templates/admin/index.html b/django/contrib/admin/templates/admin/index.html
index 7164220ce8..86c795b249 100644
--- a/django/contrib/admin/templates/admin/index.html
+++ b/django/contrib/admin/templates/admin/index.html
@@ -26,7 +26,7 @@
{% endif %}
{% if model.perms.add %}
- <td><a href="{{ model.admin_url }}add/" class="addlink">{% trans 'Add' %}</a></td>
+ <td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
diff --git a/django/contrib/admin/templates/admin/invalid_setup.html b/django/contrib/admin/templates/admin/invalid_setup.html
index f09b316b06..c2df4df7f0 100644
--- a/django/contrib/admin/templates/admin/invalid_setup.html
+++ b/django/contrib/admin/templates/admin/invalid_setup.html
@@ -1,7 +1,13 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {{ title }}</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; {{ title }}
+</div>
+{% endblock %}
{% block content %}
<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>
diff --git a/django/contrib/admin/templates/admin/object_history.html b/django/contrib/admin/templates/admin/object_history.html
index 5ae7847cfa..ebd19609cd 100644
--- a/django/contrib/admin/templates/admin/object_history.html
+++ b/django/contrib/admin/templates/admin/object_history.html
@@ -1,13 +1,15 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
+{% load admin_urls %}
{% block breadcrumbs %}
<div class="breadcrumbs">
- <a href="../../../../">{% trans 'Home' %}</a> &rsaquo;
- <a href="../../../">{{ app_label|capfirst }}</a> &rsaquo;
- <a href="../../">{{ module_name }}</a> &rsaquo;
- <a href="../">{{ object|truncatewords:"18" }}</a> &rsaquo;
- {% trans 'History' %}
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ module_name }}</a>
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}{{ object.pk }}">{{ object|truncatewords:"18" }}</a>
+&rsaquo; {% trans 'History' %}
</div>
{% endblock %}
diff --git a/django/contrib/admin/templates/registration/logged_out.html b/django/contrib/admin/templates/registration/logged_out.html
index d339ef0a49..e95d864803 100644
--- a/django/contrib/admin/templates/registration/logged_out.html
+++ b/django/contrib/admin/templates/registration/logged_out.html
@@ -1,12 +1,13 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a></div>{% endblock %}
+{% block breadcrumbs %}<div class="breadcrumbs"><a href="{% url 'admin:index' %}">{% trans 'Home' %}</a></div>{% endblock %}
{% block content %}
<p>{% trans "Thanks for spending some quality time with the Web site today." %}</p>
-<p><a href="../">{% trans 'Log in again' %}</a></p>
+<p><a href="{% url 'admin:index' %}">{% trans 'Log in again' %}</a></p>
{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_change_done.html b/django/contrib/admin/templates/registration/password_change_done.html
index 0c0690d5e2..863fc9671d 100644
--- a/django/contrib/admin/templates/registration/password_change_done.html
+++ b/django/contrib/admin/templates/registration/password_change_done.html
@@ -1,8 +1,13 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
{% load url from future %}
-{% 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 %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %}
+{% 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 %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; {% trans 'Password change' %}
+</div>
+{% endblock %}
{% block title %}{% trans 'Password change successful' %}{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_change_form.html b/django/contrib/admin/templates/registration/password_change_form.html
index f76692d185..0e4ea535af 100644
--- a/django/contrib/admin/templates/registration/password_change_form.html
+++ b/django/contrib/admin/templates/registration/password_change_form.html
@@ -2,8 +2,13 @@
{% load i18n static %}
{% load url from future %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
-{% 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 %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password change' %}</div>{% endblock %}
+{% 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 %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; {% trans 'Password change' %}
+</div>
+{% endblock %}
{% block title %}{% trans 'Password change' %}{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_reset_complete.html b/django/contrib/admin/templates/registration/password_reset_complete.html
index fceb167a88..7c07707f09 100644
--- a/django/contrib/admin/templates/registration/password_reset_complete.html
+++ b/django/contrib/admin/templates/registration/password_reset_complete.html
@@ -1,7 +1,13 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset' %}</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; {% trans 'Password reset' %}
+</div>
+{% endblock %}
{% block title %}{% trans 'Password reset complete' %}{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_reset_confirm.html b/django/contrib/admin/templates/registration/password_reset_confirm.html
index df9cf1b316..d5299ebd91 100644
--- a/django/contrib/admin/templates/registration/password_reset_confirm.html
+++ b/django/contrib/admin/templates/registration/password_reset_confirm.html
@@ -1,7 +1,13 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset confirmation' %}</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; {% trans 'Password reset confirmation' %}
+</div>
+{% endblock %}
{% block title %}{% trans 'Password reset' %}{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_reset_done.html b/django/contrib/admin/templates/registration/password_reset_done.html
index e223bdb9de..fd73af8928 100644
--- a/django/contrib/admin/templates/registration/password_reset_done.html
+++ b/django/contrib/admin/templates/registration/password_reset_done.html
@@ -1,7 +1,13 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset' %}</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; {% trans 'Password reset' %}
+</div>
+{% endblock %}
{% block title %}{% trans 'Password reset successful' %}{% endblock %}
diff --git a/django/contrib/admin/templates/registration/password_reset_form.html b/django/contrib/admin/templates/registration/password_reset_form.html
index d3a128428a..2b591fe22e 100644
--- a/django/contrib/admin/templates/registration/password_reset_form.html
+++ b/django/contrib/admin/templates/registration/password_reset_form.html
@@ -1,7 +1,13 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">{% trans 'Home' %}</a> &rsaquo; {% trans 'Password reset' %}</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; {% trans 'Password reset' %}
+</div>
+{% endblock %}
{% block title %}{% trans "Password reset" %}{% endblock %}
diff --git a/django/contrib/admin/templatetags/admin_urls.py b/django/contrib/admin/templatetags/admin_urls.py
new file mode 100644
index 0000000000..53dc65b567
--- /dev/null
+++ b/django/contrib/admin/templatetags/admin_urls.py
@@ -0,0 +1,8 @@
+from django.core.urlresolvers import reverse, NoReverseMatch
+from django import template
+
+register = template.Library()
+
+@register.filter
+def admin_urlname(value, arg):
+ return 'admin:%s_%s_%s' % (value.app_label, value.module_name, arg)
diff --git a/django/contrib/admindocs/templates/admin_doc/bookmarklets.html b/django/contrib/admindocs/templates/admin_doc/bookmarklets.html
index 64475299c2..baa717c4b3 100644
--- a/django/contrib/admindocs/templates/admin_doc/bookmarklets.html
+++ b/django/contrib/admindocs/templates/admin_doc/bookmarklets.html
@@ -1,6 +1,14 @@
{% extends "admin/base_site.html" %}
+{% load i18n %}
+{% load url from future %}
-{% block breadcrumbs %}{% load i18n %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; <a href="../">{% trans "Documentation" %}</a> &rsaquo; {% trans "Bookmarklets" %}</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
+&rsaquo; {% trans 'Bookmarklets' %}
+</div>
+{% endblock %}
{% block title %}{% trans "Documentation bookmarklets" %}{% endblock %}
{% block content %}
diff --git a/django/contrib/admindocs/templates/admin_doc/index.html b/django/contrib/admindocs/templates/admin_doc/index.html
index a8b21c330d..652fbfd576 100644
--- a/django/contrib/admindocs/templates/admin_doc/index.html
+++ b/django/contrib/admindocs/templates/admin_doc/index.html
@@ -1,6 +1,13 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="{{ root_path }}">Home</a> &rsaquo; Documentation</div>{% endblock %}
+{% load url from future %}
+
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; {% trans 'Documentation' %}</a>
+</div>
+{% endblock %}
{% block title %}Documentation{% endblock %}
{% block content %}
diff --git a/django/contrib/admindocs/templates/admin_doc/missing_docutils.html b/django/contrib/admindocs/templates/admin_doc/missing_docutils.html
index 97c9d4711c..34c18ffb7d 100644
--- a/django/contrib/admindocs/templates/admin_doc/missing_docutils.html
+++ b/django/contrib/admindocs/templates/admin_doc/missing_docutils.html
@@ -1,6 +1,12 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Documentation</div>{% endblock %}
+{% load url from future %}
+
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; {% trans 'Documentation' %}</a>
+</div>
{% block title %}Please install docutils{% endblock %}
{% block content %}
diff --git a/django/contrib/admindocs/templates/admin_doc/model_detail.html b/django/contrib/admindocs/templates/admin_doc/model_detail.html
index 828df18377..82cb405639 100644
--- a/django/contrib/admindocs/templates/admin_doc/model_detail.html
+++ b/django/contrib/admindocs/templates/admin_doc/model_detail.html
@@ -1,5 +1,7 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
+
{% block extrahead %}
{{ block.super }}
<style type="text/css">
@@ -8,7 +10,14 @@
</style>
{% endblock %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Models</a> &rsaquo; {{ name }}</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
+&rsaquo; <a href="{% url 'django-admindocs-models-index' %}">{% trans 'Models' %}</a>
+&rsaquo; {{ name }}
+</div>
+{% endblock %}
{% block title %}Model: {{ name }}{% endblock %}
@@ -41,6 +50,6 @@
</table>
</div>
-<p class="small"><a href="../">&lsaquo; Back to Models Documentation</a></p>
+<p class="small"><a href="{% url 'django-admindocs-models-index' %}">&lsaquo; Back to Models Documentation</a></p>
</div>
{% endblock %}
diff --git a/django/contrib/admindocs/templates/admin_doc/model_index.html b/django/contrib/admindocs/templates/admin_doc/model_index.html
index 47c94c0c70..4eb3cf69c9 100644
--- a/django/contrib/admindocs/templates/admin_doc/model_index.html
+++ b/django/contrib/admindocs/templates/admin_doc/model_index.html
@@ -1,7 +1,16 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
+
{% block coltype %}colSM{% endblock %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Models</div>{% endblock %}
+
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
+&rsaquo; {% trans 'Models' %}
+</div>
+{% endblock %}
{% block title %}Models{% endblock %}
@@ -19,7 +28,7 @@
<table class="xfull">
{% for model in group.list %}
<tr>
-<th><a href="{{ model.app_label }}.{{ model.object_name.lower }}/">{{ model.object_name }}</a></th>
+<th><a href="{% url 'django-admindocs-models-detail' app_label=model.app_label model_name=model.object_name.lower %}">{{ model.object_name }}</a></th>
</tr>
{% endfor %}
</table>
diff --git a/django/contrib/admindocs/templates/admin_doc/template_detail.html b/django/contrib/admindocs/templates/admin_doc/template_detail.html
index c04dedc530..307dd627df 100644
--- a/django/contrib/admindocs/templates/admin_doc/template_detail.html
+++ b/django/contrib/admindocs/templates/admin_doc/template_detail.html
@@ -1,6 +1,15 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; Templates &rsaquo; {{ name }}</div>{% endblock %}
+{% load url from future %}
+
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
+&rsaquo; {% trans 'Templates' %}
+&rsaquo; {{ name }}
+</div>
+{% endblock %}
{% block title %}Template: {{ name }}{% endblock %}
@@ -17,5 +26,5 @@
</ol>
{% endfor %}
-<p class="small"><a href="../../">&lsaquo; Back to Documentation</a></p>
+<p class="small"><a href="{% url 'django-admindocs-docroot' %}">&lsaquo; Back to Documentation</a></p>
{% endblock %}
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 46ccf0fd0a..1809bc9182 100644
--- a/django/contrib/admindocs/templates/admin_doc/template_filter_index.html
+++ b/django/contrib/admindocs/templates/admin_doc/template_filter_index.html
@@ -1,7 +1,15 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
+
{% block coltype %}colSM{% endblock %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; filters</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
+&rsaquo; {% trans 'Filters' %}
+</div>
+{% endblock %}
{% block title %}Template filters{% endblock %}
{% block content %}
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 676c025acb..18e5d95298 100644
--- a/django/contrib/admindocs/templates/admin_doc/template_tag_index.html
+++ b/django/contrib/admindocs/templates/admin_doc/template_tag_index.html
@@ -1,7 +1,15 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
+
{% block coltype %}colSM{% endblock %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Tags</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
+&rsaquo; {% trans 'Tags' %}
+</div>
+{% endblock %}
{% block title %}Template tags{% endblock %}
{% block content %}
diff --git a/django/contrib/admindocs/templates/admin_doc/view_detail.html b/django/contrib/admindocs/templates/admin_doc/view_detail.html
index c6d080c9ef..41c812110f 100644
--- a/django/contrib/admindocs/templates/admin_doc/view_detail.html
+++ b/django/contrib/admindocs/templates/admin_doc/view_detail.html
@@ -1,6 +1,15 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Views</a> &rsaquo; {{ name }}</div>{% endblock %}
+{% load url from future %}
+
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
+&rsaquo; <a href="{% url 'django-admindocs-views-index' %}">{% trans 'Views' %}</a>
+&rsaquo; {{ name }}
+</div>
+{% endblock %}
{% block title %}View: {{ name }}{% endblock %}
{% block content %}
@@ -21,5 +30,5 @@
<p>{{ meta.Templates }}</p>
{% endif %}
-<p class="small"><a href="../">&lsaquo; Back to Views Documentation</a></p>
+<p class="small"><a href="{% url 'django-admindocs-views-index' %}">&lsaquo; Back to Views Documentation</a></p>
{% endblock %}
diff --git a/django/contrib/admindocs/templates/admin_doc/view_index.html b/django/contrib/admindocs/templates/admin_doc/view_index.html
index 6b10fa65e3..e508c84e19 100644
--- a/django/contrib/admindocs/templates/admin_doc/view_index.html
+++ b/django/contrib/admindocs/templates/admin_doc/view_index.html
@@ -1,7 +1,15 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
+{% load url from future %}
+
{% block coltype %}colSM{% endblock %}
-{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Views</div>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% url 'django-admindocs-docroot' %}">{% trans 'Documentation' %}</a>
+&rsaquo; {% trans 'Views' %}
+</div>
+{% endblock %}
{% block title %}Views{% endblock %}
{% block content %}
@@ -29,8 +37,8 @@
{% for view in site_views.list|dictsort:"url" %}
{% ifchanged %}
-<h3><a href="{{ view.module }}.{{ view.name }}/">{{ view.url }}</a></h3>
-<p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p>
+<h3><a href="{% url 'django-admindocs-views-detail' view=view.full_name %}">{{ view.url }}</a></h3>
+<p class="small quiet">View function: {{ view.full_name }}</p>
<p>{{ view.title }}</p>
<hr />
{% endifchanged %}
diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py
index 28319be9d5..9725865d81 100644
--- a/django/contrib/admindocs/views.py
+++ b/django/contrib/admindocs/views.py
@@ -136,8 +136,7 @@ def view_index(request):
site_obj = GenericSite()
for (func, regex) in view_functions:
views.append({
- 'name': getattr(func, '__name__', func.__class__.__name__),
- 'module': func.__module__,
+ 'full_name': '%s.%s' % (func.__module__, getattr(func, '__name__', func.__class__.__name__)),
'site_id': settings_mod.SITE_ID,
'site': site_obj,
'url': simplify_regex(regex),
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index 2da6774d44..21145c5652 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -1960,3 +1960,18 @@ if you specifically wanted the admin view from the admin instance named
For more details, see the documentation on :ref:`reversing namespaced URLs
<topics-http-reversing-url-namespaces>`.
+
+To allow easier reversing of the admin urls in templates, Django provides an
+``admin_url`` filter which takes an action as argument:
+
+.. code-block:: html+django
+
+ {% load admin_urls %}
+ {% load url from future %}
+ <a href="{% url opts|admin_urlname:'add' %}">Add user</a>
+ <a href="{% url opts|admin_urlname:'delete' user.pk %}">Delete this user</a>
+
+The action in the examples above match the last part of the URL names for
+:class:`ModelAdmin` instances described above. The ``opts`` variable can be any
+object which has an ``app_label`` and ``module_name`` and is usually supplied
+by the admin views for the current model.
diff --git a/tests/regressiontests/admin_custom_urls/__init__.py b/tests/regressiontests/admin_custom_urls/__init__.py
new file mode 100644
index 0000000000..792d600548
--- /dev/null
+++ b/tests/regressiontests/admin_custom_urls/__init__.py
@@ -0,0 +1 @@
+#
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 0000000000..d803393a12
--- /dev/null
+++ b/tests/regressiontests/admin_custom_urls/fixtures/actions.json
@@ -0,0 +1,44 @@
+[
+ {
+ "pk": "delete",
+ "model": "admin_custom_urls.action",
+ "fields": {
+ "description": "Remove things."
+ }
+ },
+ {
+ "pk": "rename",
+ "model": "admin_custom_urls.action",
+ "fields": {
+ "description": "Gives things other names."
+ }
+ },
+ {
+ "pk": "add",
+ "model": "admin_custom_urls.action",
+ "fields": {
+ "description": "Add things."
+ }
+ },
+ {
+ "pk": "path/to/file/",
+ "model": "admin_custom_urls.action",
+ "fields": {
+ "description": "An action with '/' in its name."
+ }
+ },
+ {
+ "pk": "path/to/html/document.html",
+ "model": "admin_custom_urls.action",
+ "fields": {
+ "description": "An action with a name similar to a HTML doc path."
+ }
+ },
+ {
+ "pk": "javascript:alert('Hello world');\">Click here</a>",
+ "model": "admin_custom_urls.action",
+ "fields": {
+ "description": "An action with a name suspected of being a XSS attempt"
+ }
+ }
+] \ No newline at end of file
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 0000000000..72d86d70ad
--- /dev/null
+++ b/tests/regressiontests/admin_custom_urls/fixtures/users.json
@@ -0,0 +1,20 @@
+[
+ {
+ "pk": 100,
+ "model": "auth.user",
+ "fields": {
+ "username": "super",
+ "first_name": "Super",
+ "last_name": "User",
+ "is_active": true,
+ "is_superuser": true,
+ "is_staff": true,
+ "last_login": "2007-05-30 13:20:10",
+ "groups": [],
+ "user_permissions": [],
+ "password": "sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158",
+ "email": "super@example.com",
+ "date_joined": "2007-05-30 13:20:10"
+ }
+ }
+]
diff --git a/tests/regressiontests/admin_custom_urls/models.py b/tests/regressiontests/admin_custom_urls/models.py
new file mode 100644
index 0000000000..f8c83a9024
--- /dev/null
+++ b/tests/regressiontests/admin_custom_urls/models.py
@@ -0,0 +1,50 @@
+from functools import update_wrapper
+
+from django.contrib import admin
+from django.db import models
+
+
+class Action(models.Model):
+ name = models.CharField(max_length=50, primary_key=True)
+ description = models.CharField(max_length=70)
+
+ def __unicode__(self):
+ return self.name
+
+
+class ActionAdmin(admin.ModelAdmin):
+ """
+ A ModelAdmin for the Action model that changes the URL of the add_view
+ to '<app name>/<model name>/!add/'
+ The Action model has a CharField PK.
+ """
+
+ list_display = ('name', 'description')
+
+ def remove_url(self, name):
+ """
+ Remove all entries named 'name' from the ModelAdmin instance URL
+ patterns list
+ """
+ return filter(lambda e: e.name != name, super(ActionAdmin, self).get_urls())
+
+ def get_urls(self):
+ # Add the URL of our custom 'add_view' view to the front of the URLs
+ # list. Remove the existing one(s) first
+ from django.conf.urls.defaults import patterns, url
+
+ def wrap(view):
+ def wrapper(*args, **kwargs):
+ return self.admin_site.admin_view(view)(*args, **kwargs)
+ return update_wrapper(wrapper, view)
+
+ info = self.model._meta.app_label, self.model._meta.module_name
+
+ view_name = '%s_%s_add' % info
+
+ return patterns('',
+ url(r'^!add/$', wrap(self.add_view), name=view_name),
+ ) + self.remove_url(view_name)
+
+
+admin.site.register(Action, ActionAdmin)
diff --git a/tests/regressiontests/admin_custom_urls/tests.py b/tests/regressiontests/admin_custom_urls/tests.py
new file mode 100644
index 0000000000..cfc6b8583e
--- /dev/null
+++ b/tests/regressiontests/admin_custom_urls/tests.py
@@ -0,0 +1,72 @@
+from django.core.urlresolvers import reverse
+from django.template.response import TemplateResponse
+from django.test import TestCase
+
+from models import Action
+
+
+class AdminCustomUrlsTest(TestCase):
+ fixtures = ['users.json', 'actions.json']
+
+ def setUp(self):
+ self.client.login(username='super', password='secret')
+
+ def tearDown(self):
+ self.client.logout()
+
+ def testBasicAddGet(self):
+ """
+ A smoke test to ensure GET on the add_view works.
+ """
+ response = self.client.get('/custom_urls/admin/admin_custom_urls/action/!add/')
+ self.assertIsInstance(response, TemplateResponse)
+ self.assertEqual(response.status_code, 200)
+
+ def testAddWithGETArgs(self):
+ response = self.client.get('/custom_urls/admin/admin_custom_urls/action/!add/', {'name': 'My Action'})
+ self.assertEqual(response.status_code, 200)
+ self.assertTrue(
+ 'value="My Action"' in response.content,
+ "Couldn't find an input with the right value in the response."
+ )
+
+ def testBasicAddPost(self):
+ """
+ A smoke test to ensure POST on add_view works.
+ """
+ post_data = {
+ '_popup': u'1',
+ "name": u'Action added through a popup',
+ "description": u"Description of added action",
+ }
+ response = self.client.post('/custom_urls/admin/admin_custom_urls/action/!add/', post_data)
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, 'dismissAddAnotherPopup')
+ self.assertContains(response, 'Action added through a popup')
+
+ def testAdminUrlsNoClash(self):
+ """
+ Test that some admin URLs work correctly. The model has a CharField
+ PK and the add_view URL has been customized.
+ """
+ # Should get the change_view for model instance with PK 'add', not show
+ # the add_view
+ response = self.client.get('/custom_urls/admin/admin_custom_urls/action/add/')
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, 'Change action')
+
+ # Ditto, but use reverse() to build the URL
+ path = reverse('admin:%s_action_change' % Action._meta.app_label,
+ args=('add',))
+ response = self.client.get(path)
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, 'Change action')
+
+ # Should correctly get the change_view for the model instance with the
+ # funny-looking PK
+ path = reverse('admin:%s_action_change' % Action._meta.app_label,
+ args=("path/to/html/document.html",))
+ response = self.client.get(path)
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, 'Change action')
+ self.assertContains(response, 'value="path/to/html/document.html"')
diff --git a/tests/regressiontests/admin_custom_urls/urls.py b/tests/regressiontests/admin_custom_urls/urls.py
new file mode 100644
index 0000000000..6c2761a222
--- /dev/null
+++ b/tests/regressiontests/admin_custom_urls/urls.py
@@ -0,0 +1,7 @@
+from django.conf.urls.defaults import *
+from django.contrib import admin
+
+urlpatterns = patterns('',
+ (r'^admin/', include(admin.site.urls)),
+)
+
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index 668c71076e..78457d97ab 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -595,7 +595,7 @@ class SaveAsTests(TestCase):
self.assertTrue(response.context['save_as'])
post_data = {'_saveasnew':'', 'name':'John M', 'gender':3, 'alive':'checked'}
response = self.client.post('/test_admin/admin/admin_views/person/1/', post_data)
- self.assertEqual(response.context['form_url'], '../add/')
+ self.assertEqual(response.context['form_url'], '/test_admin/admin/admin_views/person/add/')
class CustomModelAdminTest(AdminViewBasicTest):
urls = "regressiontests.admin_views.urls"
@@ -842,7 +842,7 @@ class AdminViewPermissionsTest(TestCase):
self.client.post('/test_admin/admin/', self.adduser_login)
addpage = self.client.get('/test_admin/admin/admin_views/article/add/')
self.assertEqual(addpage.status_code, 200)
- change_list_link = '<a href="../">Articles</a> &rsaquo;'
+ change_list_link = '&rsaquo; <a href="/test_admin/admin/admin_views/article/">Articles</a>'
self.assertFalse(change_list_link in addpage.content,
'User restricted to add permission is given link to change list view in breadcrumbs.')
post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict)
diff --git a/tests/urls.py b/tests/urls.py
index 044395039d..e7c23e5144 100644
--- a/tests/urls.py
+++ b/tests/urls.py
@@ -1,6 +1,5 @@
from django.conf.urls import patterns, include
-
urlpatterns = patterns('',
# test_client modeltest urls
(r'^test_client/', include('modeltests.test_client.urls')),
@@ -25,4 +24,7 @@ urlpatterns = patterns('',
# admin widget tests
(r'widget_admin/', include('regressiontests.admin_widgets.urls')),
+ # admin custom URL tests
+ (r'^custom_urls/', include('regressiontests.admin_custom_urls.urls')),
+
)