Kaydet (Commit) 93fbb77d authored tarafından Julien Phalip's avatar Julien Phalip

Fixed #16716 -- Fixed two small regressions in the development version…

Fixed #16716 -- Fixed two small regressions in the development version introduced in r16144 where the changelist crashed with a 500 error instead of nicely operating a 302 redirection back to the changelist.

The two specific cases were:

* a lookup through a non-existing field and apparently spanning multiple relationships (e.g. "?nonexistant__whatever=xxxx").
* a proper list_filter's queryset failing with an exception. In Django 1.3 the queryset was only directly manipulated by the changelist, whereas in 1.4 the list_filters may manipulate the queryset themselves. The fix here implies catching potential failures from the list_filters too.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16705 bcc190cf-cafb-0310-a4f2-bffc1f526a37
üst ce477f08
...@@ -267,6 +267,7 @@ class ChangeList(object): ...@@ -267,6 +267,7 @@ class ChangeList(object):
del lookup_params[key] del lookup_params[key]
lookup_params[smart_str(key)] = value lookup_params[smart_str(key)] = value
field = None
if not use_distinct: if not use_distinct:
# Check if it's a relationship that might return more than one # Check if it's a relationship that might return more than one
# instance # instance
...@@ -291,7 +292,7 @@ class ChangeList(object): ...@@ -291,7 +292,7 @@ class ChangeList(object):
value = True value = True
lookup_params[key] = value lookup_params[key] = value
if not self.model_admin.lookup_allowed(key, value): if field and not self.model_admin.lookup_allowed(key, value):
raise SuspiciousOperation("Filtering by %s not allowed" % key) raise SuspiciousOperation("Filtering by %s not allowed" % key)
return lookup_params, use_distinct return lookup_params, use_distinct
...@@ -300,28 +301,30 @@ class ChangeList(object): ...@@ -300,28 +301,30 @@ class ChangeList(object):
lookup_params, use_distinct = self.get_lookup_params(use_distinct=False) lookup_params, use_distinct = self.get_lookup_params(use_distinct=False)
self.filter_specs, self.has_filters = self.get_filters(request, use_distinct) self.filter_specs, self.has_filters = self.get_filters(request, use_distinct)
# Let every list filter modify the qs and params to its liking
qs = self.root_query_set
for filter_spec in self.filter_specs:
new_qs = filter_spec.queryset(request, qs)
if new_qs is not None:
qs = new_qs
for param in filter_spec.used_params():
try:
del lookup_params[param]
except KeyError:
pass
# Apply the remaining lookup parameters from the query string (i.e.
# those that haven't already been processed by the filters).
try: try:
# First, let every list filter modify the qs and params to its
# liking.
qs = self.root_query_set
for filter_spec in self.filter_specs:
new_qs = filter_spec.queryset(request, qs)
if new_qs is not None:
qs = new_qs
for param in filter_spec.used_params():
try:
del lookup_params[param]
except KeyError:
pass
# Then, apply the remaining lookup parameters from the query string
# (i.e. those that haven't already been processed by the filters).
qs = qs.filter(**lookup_params) qs = qs.filter(**lookup_params)
# Naked except! Because we don't have any other way of validating "params".
# They might be invalid if the keyword arguments are incorrect, or if the
# values are not in the correct type, so we might get FieldError, ValueError,
# ValicationError, or ? from a custom field that raises yet something else
# when handed impossible data.
except Exception, e: except Exception, e:
# Naked except! Because we don't have any other way of validating
# "lookup_params". They might be invalid if the keyword arguments
# are incorrect, or if the values are not in the correct type, so
# we might get FieldError, ValueError, ValicationError, or ? from a
# custom field that raises yet something else when handed
# impossible data.
raise IncorrectLookupParameters(e) raise IncorrectLookupParameters(e)
# Use select_related() if one of the list_display options is a field # Use select_related() if one of the list_display options is a field
......
import datetime import datetime
from django.contrib.admin.options import IncorrectLookupParameters
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase, RequestFactory from django.test import TestCase, RequestFactory
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.admin.views.main import ChangeList from django.contrib.admin.views.main import ChangeList
...@@ -50,6 +50,11 @@ class DecadeListFilterWithNoneReturningLookups(DecadeListFilterWithTitleAndParam ...@@ -50,6 +50,11 @@ class DecadeListFilterWithNoneReturningLookups(DecadeListFilterWithTitleAndParam
def lookups(self, request, model_admin): def lookups(self, request, model_admin):
pass pass
class DecadeListFilterWithFailingQueryset(DecadeListFilterWithTitleAndParameter):
def queryset(self, request, queryset):
raise Exception
class DecadeListFilterWithQuerysetBasedLookups(DecadeListFilterWithTitleAndParameter): class DecadeListFilterWithQuerysetBasedLookups(DecadeListFilterWithTitleAndParameter):
def lookups(self, request, model_admin): def lookups(self, request, model_admin):
...@@ -84,6 +89,9 @@ class DecadeFilterBookAdminWithoutParameter(ModelAdmin): ...@@ -84,6 +89,9 @@ class DecadeFilterBookAdminWithoutParameter(ModelAdmin):
class DecadeFilterBookAdminWithNoneReturningLookups(ModelAdmin): class DecadeFilterBookAdminWithNoneReturningLookups(ModelAdmin):
list_filter = (DecadeListFilterWithNoneReturningLookups,) list_filter = (DecadeListFilterWithNoneReturningLookups,)
class DecadeFilterBookAdminWithFailingQueryset(ModelAdmin):
list_filter = (DecadeListFilterWithFailingQueryset,)
class DecadeFilterBookAdminWithQuerysetBasedLookups(ModelAdmin): class DecadeFilterBookAdminWithQuerysetBasedLookups(ModelAdmin):
list_filter = (DecadeListFilterWithQuerysetBasedLookups,) list_filter = (DecadeListFilterWithQuerysetBasedLookups,)
...@@ -509,6 +517,17 @@ class ListFiltersTests(TestCase): ...@@ -509,6 +517,17 @@ class ListFiltersTests(TestCase):
filterspec = changelist.get_filters(request)[0] filterspec = changelist.get_filters(request)[0]
self.assertEqual(len(filterspec), 0) self.assertEqual(len(filterspec), 0)
def test_filter_with_failing_queryset(self):
"""
Ensure that a filter's failing queryset is interpreted as if incorrect
lookup parameters were passed (therefore causing a 302 redirection to
the changelist).
Refs #16716, #16714.
"""
modeladmin = DecadeFilterBookAdminWithFailingQueryset(Book, site)
request = self.request_factory.get('/', {})
self.assertRaises(IncorrectLookupParameters, self.get_changelist, request, Book, modeladmin)
def test_simplelistfilter_with_queryset_based_lookups(self): def test_simplelistfilter_with_queryset_based_lookups(self):
modeladmin = DecadeFilterBookAdminWithQuerysetBasedLookups(Book, site) modeladmin = DecadeFilterBookAdminWithQuerysetBasedLookups(Book, site)
request = self.request_factory.get('/', {}) request = self.request_factory.get('/', {})
......
...@@ -410,6 +410,11 @@ class AdminViewBasicTest(TestCase): ...@@ -410,6 +410,11 @@ class AdminViewBasicTest(TestCase):
"""Ensure incorrect lookup parameters are handled gracefully.""" """Ensure incorrect lookup parameters are handled gracefully."""
response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'notarealfield': '5'}) response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'notarealfield': '5'})
self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit) self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit)
# Spanning relationships through an inexistant related object (Refs #16716)
response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'notarealfield__whatever': '5'})
self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit)
response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'color__id__exact': 'StringNotInteger!'}) response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'color__id__exact': 'StringNotInteger!'})
self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit) self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment