Stopped unconditionally reversing admin model add/change URLs.

Starting with [16857] this could cause HTTP 500 errors when
`ModelAdmin.get_urls()` has been customized to the point it doesn't
provide these standard URLs.

Fixes #17333. Refs #15294.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17237 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Ramiro Morales 2011-12-19 14:59:14 +00:00
parent 259ebcdeea
commit 554f0601b5
5 changed files with 78 additions and 11 deletions

View File

@ -7,7 +7,7 @@ from django.contrib.contenttypes import views as contenttype_views
from django.views.decorators.csrf import csrf_protect
from django.db.models.base import ModelBase
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse, NoReverseMatch
from django.template.response import TemplateResponse
from django.utils.safestring import mark_safe
from django.utils.text import capfirst
@ -342,10 +342,18 @@ class AdminSite(object):
info = (app_label, model._meta.module_name)
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
'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 perms.get('change', False):
try:
model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name)
except NoReverseMatch:
pass
if perms.get('add', False):
try:
model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name)
except NoReverseMatch:
pass
if app_label in app_dict:
app_dict[app_label]['models'].append(model_dict)
else:
@ -388,10 +396,18 @@ class AdminSite(object):
info = (app_label, model._meta.module_name)
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
'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 perms.get('change', False):
try:
model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name)
except NoReverseMatch:
pass
if perms.get('add', False):
try:
model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name)
except NoReverseMatch:
pass
if app_dict:
app_dict['models'].append(model_dict),
else:

View File

@ -19,19 +19,19 @@
<caption><a href="{{ app.app_url }}" class="section">{% blocktrans with name=app.name %}{{ name }}{% endblocktrans %}</a></caption>
{% for model in app.models %}
<tr>
{% if model.perms.change %}
{% if model.admin_url %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.perms.add %}
{% if model.add_url %}
<td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
{% if model.perms.change %}
{% if model.admin_url %}
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
{% else %}
<td>&nbsp;</td>

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import datetime
import tempfile
import os
@ -10,8 +9,10 @@ from django.contrib import admin
from django.contrib.admin.views.main import ChangeList
from django.core.files.storage import FileSystemStorage
from django.core.mail import EmailMessage
from django.conf.urls import patterns, url
from django.db import models
from django.forms.models import BaseModelFormSet
from django.http import HttpResponse
from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
Widget, DooHickey, Grommet, Whatsit, FancyDoodad, Category, Link,
@ -24,7 +25,7 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
CoverLetter, Story, OtherStory, Book, Promo, ChapterXtra1, Pizza, Topping,
Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug,
AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod,
AdminOrderedCallable)
AdminOrderedCallable, Report)
def callable_year(dt_value):
@ -499,6 +500,17 @@ class AdminOrderedCallableAdmin(admin.ModelAdmin):
ordering = ('order',)
list_display = ('stuff', admin_ordered_callable)
class ReportAdmin(admin.ModelAdmin):
def extra(self, request):
return HttpResponse()
def get_urls(self):
# Corner case: Don't call parent implementation
return patterns('',
url(r'^extra/$',
self.extra,
name='cable_extra'),
)
site = admin.AdminSite(name="admin")
site.register(Article, ArticleAdmin)
@ -543,6 +555,7 @@ site.register(Paper, PaperAdmin)
site.register(CoverLetter, CoverLetterAdmin)
site.register(Story, StoryAdmin)
site.register(OtherStory, OtherStoryAdmin)
site.register(Report, ReportAdmin)
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
# That way we cover all four cases:

View File

@ -566,3 +566,9 @@ class AdminOrderedAdminMethod(models.Model):
class AdminOrderedCallable(models.Model):
order = models.IntegerField()
stuff = models.CharField(max_length=200)
class Report(models.Model):
title = models.CharField(max_length=100)
def __unicode__(self):
return self.title

View File

@ -38,7 +38,8 @@ from .models import (Article, BarAccount, CustomArticle, EmptyModel, FooAccount,
Book, Promo, WorkHour, Employee, Question, Answer, Inquisition, Actor,
FoodDelivery, RowLevelChangePermissionModel, Paper, CoverLetter, Story,
OtherStory, ComplexSortedPerson, Parent, Child, AdminOrderedField,
AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable)
AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable,
Report)
ERROR_MESSAGE = "Please enter the correct username and password \
@ -1090,6 +1091,37 @@ class AdminViewPermissionsTest(TestCase):
self.assertContains(response, 'id="login-form"')
class AdminViewsNoUrlTest(TestCase):
"""Regression test for #17333"""
urls = "regressiontests.admin_views.urls"
fixtures = ['admin-views-users.xml']
def setUp(self):
opts = Report._meta
# User who can change Reports
change_user = User.objects.get(username='changeuser')
change_user.user_permissions.add(get_perm(Report,
opts.get_change_permission()))
# login POST dict
self.changeuser_login = {
REDIRECT_FIELD_NAME: '/test_admin/admin/',
LOGIN_FORM_KEY: 1,
'username': 'changeuser',
'password': 'secret',
}
def test_no_standard_modeladmin_urls(self):
"""Admin index views don't break when user's ModelAdmin removes standard urls"""
self.client.get('/test_admin/admin/')
self.client.post('/test_admin/admin/', self.changeuser_login)
r = self.client.get('/test_admin/admin/')
# we shouldn' get an 500 error caused by a NoReverseMatch
self.assertEqual(r.status_code, 200)
self.client.get('/test_admin/admin/logout/')
class AdminViewDeletedObjectsTest(TestCase):
urls = "regressiontests.admin_views.urls"
fixtures = ['admin-views-users.xml', 'deleted-objects.xml']