Skip to content

Commit

Permalink
Stopped unconditionally reversing admin model add/change URLs.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ramiro committed Dec 19, 2011
1 parent bffeeec commit 27616b7
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 11 deletions.
26 changes: 21 additions & 5 deletions django/contrib/admin/sites.py
Expand Up @@ -7,7 +7,7 @@
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
Expand Down Expand Up @@ -342,10 +342,18 @@ def index(self, request, extra_context=None):
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:
Expand Down Expand Up @@ -388,10 +396,18 @@ def app_index(self, request, app_label, extra_context=None):
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:
Expand Down
6 changes: 3 additions & 3 deletions django/contrib/admin/templates/admin/index.html
Expand Up @@ -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>
Expand Down
17 changes: 15 additions & 2 deletions tests/regressiontests/admin_views/admin.py
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import

import datetime
import tempfile
import os

Expand All @@ -10,8 +9,10 @@
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,
Expand All @@ -24,7 +25,7 @@
CoverLetter, Story, OtherStory, Book, Promo, ChapterXtra1, Pizza, Topping,
Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug,
AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod,
AdminOrderedCallable)
AdminOrderedCallable, Report)


def callable_year(dt_value):
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -543,6 +555,7 @@ class AdminOrderedCallableAdmin(admin.ModelAdmin):
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:
Expand Down
6 changes: 6 additions & 0 deletions tests/regressiontests/admin_views/models.py
Expand Up @@ -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
34 changes: 33 additions & 1 deletion tests/regressiontests/admin_views/tests.py
Expand Up @@ -38,7 +38,8 @@
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 \
Expand Down Expand Up @@ -1090,6 +1091,37 @@ def testDisabledPermissionsWhenLoggedIn(self):
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']
Expand Down

0 comments on commit 27616b7

Please sign in to comment.