diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 2d13593b30..263f4ac148 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -15,6 +15,7 @@ from django.core.exceptions import ( ) from django.core.paginator import InvalidPage from django.db import models +from django.db.models.expressions import F, OrderBy from django.urls import reverse from django.utils.http import urlencode from django.utils.translation import gettext @@ -287,7 +288,13 @@ class ChangeList: # the right column numbers absolutely, because there might be more # than one column associated with that ordering, so we guess. 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:] order_type = 'desc' else: diff --git a/docs/releases/2.0.1.txt b/docs/releases/2.0.1.txt index 97260ef5b8..0c3a571913 100644 --- a/docs/releases/2.0.1.txt +++ b/docs/releases/2.0.1.txt @@ -40,3 +40,6 @@ Bugfixes * Fixed a crash when chaining ``values()`` or ``values_list()`` after ``QuerySet.select_for_update(of=(...))`` (:ticket:`28944`). + +* Fixed admin changelist crash when using a query expression in the page's + ordering (:ticket:`28958`). diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index 537949021f..5f3d30fd68 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -8,7 +8,9 @@ from django.contrib.admin.tests import AdminSeleniumTestCase from django.contrib.admin.views.main import ALL_VAR, SEARCH_VAR from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType +from django.db.models import F from django.db.models.fields import Field, IntegerField +from django.db.models.functions import Upper from django.db.models.lookups import Contains, Exact from django.template import Context, Template from django.test import TestCase, override_settings @@ -57,6 +59,20 @@ class ChangeListTests(TestCase): request.user = user 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): """ Regression test for #10348: ChangeList.get_queryset() shouldn't