[2.0.x] Fixed #28958 -- Fixed admin changelist crash when using a query expression in the page's ordering.

Thanks Tim Graham for the review.

Backport of c815213740 from master
This commit is contained in:
Mariusz Felisiak 2017-12-27 18:38:30 +01:00
parent 1b27374b0d
commit b54302d226
No known key found for this signature in database
GPG Key ID: 2EF56372BA48CD1B
3 changed files with 27 additions and 1 deletions

View File

@ -15,6 +15,7 @@ from django.core.exceptions import (
) )
from django.core.paginator import InvalidPage from django.core.paginator import InvalidPage
from django.db import models from django.db import models
from django.db.models.expressions import F, OrderBy
from django.urls import reverse from django.urls import reverse
from django.utils.http import urlencode from django.utils.http import urlencode
from django.utils.translation import gettext from django.utils.translation import gettext
@ -288,7 +289,13 @@ class ChangeList:
# the right column numbers absolutely, because there might be more # the right column numbers absolutely, because there might be more
# than one column associated with that ordering, so we guess. # than one column associated with that ordering, so we guess.
for field in ordering: for field in ordering:
if field.startswith('-'): if isinstance(field, OrderBy):
if isinstance(field.expression, F):
order_type = 'desc' if field.descending else 'asc'
field = field.expression.name
else:
continue
elif field.startswith('-'):
field = field[1:] field = field[1:]
order_type = 'desc' order_type = 'desc'
else: else:

View File

@ -40,3 +40,6 @@ Bugfixes
* Fixed a crash when chaining ``values()`` or ``values_list()`` after * Fixed a crash when chaining ``values()`` or ``values_list()`` after
``QuerySet.select_for_update(of=(...))`` (:ticket:`28944`). ``QuerySet.select_for_update(of=(...))`` (:ticket:`28944`).
* Fixed admin changelist crash when using a query expression in the page's
ordering (:ticket:`28958`).

View File

@ -8,6 +8,8 @@ from django.contrib.admin.tests import AdminSeleniumTestCase
from django.contrib.admin.views.main import ALL_VAR, SEARCH_VAR from django.contrib.admin.views.main import ALL_VAR, SEARCH_VAR
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import F
from django.db.models.functions import Upper
from django.template import Context, Template from django.template import Context, Template
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.test.client import RequestFactory from django.test.client import RequestFactory
@ -55,6 +57,20 @@ class ChangeListTests(TestCase):
request.user = user request.user = user
return request return request
def test_specified_ordering_by_f_expression(self):
class OrderedByFBandAdmin(admin.ModelAdmin):
list_display = ['name', 'genres', 'nr_of_members']
ordering = (
F('nr_of_members').desc(nulls_last=True),
Upper(F('name')).asc(),
F('genres').asc(),
)
m = OrderedByFBandAdmin(Band, custom_site)
request = self.factory.get('/band/')
cl = m.get_changelist_instance(request)
self.assertEqual(cl.get_ordering_field_columns(), {3: 'desc', 2: 'asc'})
def test_select_related_preserved(self): def test_select_related_preserved(self):
""" """
Regression test for #10348: ChangeList.get_queryset() shouldn't Regression test for #10348: ChangeList.get_queryset() shouldn't