summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Pelme <andreas@pelme.se>2018-02-16 03:00:31 +0100
committerTim Graham <timograham@gmail.com>2018-02-15 21:00:31 -0500
commite307ff29d28737d5a764ce2fa7db010231d6fc8e (patch)
treeac6cf4a88d36e5cb5de2c7a28f32a55ded650bfa
parentd368784bacc7e58b426f29937ee842aa14d439ad (diff)
Fixed #27810 -- Allowed query expressions in admin_order_field.
-rw-r--r--django/contrib/admin/views/main.py5
-rw-r--r--docs/ref/contrib/admin/index.txt18
-rw-r--r--docs/releases/2.1.txt3
-rw-r--r--tests/admin_views/admin.py8
-rw-r--r--tests/admin_views/tests.py26
5 files changed, 59 insertions, 1 deletions
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index 761ed3a754..fa7b2a6036 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -286,8 +286,11 @@ class ChangeList:
order_field = self.get_ordering_field(field_name)
if not order_field:
continue # No 'admin_order_field', skip it
+ if hasattr(order_field, 'as_sql'):
+ # order_field is an expression.
+ ordering.append(order_field.desc() if pfx == '-' else order_field.asc())
# reverse order if order_field has already "-" as prefix
- if order_field.startswith('-') and pfx == "-":
+ elif order_field.startswith('-') and pfx == '-':
ordering.append(order_field[1:])
else:
ordering.append(pfx + order_field)
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index a917498aed..760cce69be 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -745,6 +745,24 @@ subclass::
author_first_name.admin_order_field = 'author__first_name'
+ :doc:`Query expressions </ref/models/expressions>` may be used in
+ ``admin_order_field``. For example::
+
+ from django.db.models import Value
+ from django.db.models.functions import Concat
+
+ class Person(models.Model):
+ first_name = models.CharField(max_length=50)
+ last_name = models.CharField(max_length=50)
+
+ def full_name(self):
+ return self.first_name + ' ' + self.last_name
+ full_name.admin_order_field = Concat('first_name', Value(' '), 'last_name')
+
+ .. versionadded:: 2.1
+
+ Support for expressions in ``admin_order_field`` was added.
+
* Elements of ``list_display`` can also be properties. Please note however,
that due to the way properties work in Python, setting
``short_description`` on a property is only possible when using the
diff --git a/docs/releases/2.1.txt b/docs/releases/2.1.txt
index 33d6982443..414da31109 100644
--- a/docs/releases/2.1.txt
+++ b/docs/releases/2.1.txt
@@ -47,6 +47,9 @@ Minor features
:meth:`.ModelAdmin.get_sortable_by` method allow limiting the columns that
can be sorted in the change list page.
+* The ``admin_order_field`` attribute for elements in
+ :attr:`.ModelAdmin.list_display` may now be a query expression.
+
:mod:`django.contrib.admindocs`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py
index 04e40c2e0d..ceae4c11a2 100644
--- a/tests/admin_views/admin.py
+++ b/tests/admin_views/admin.py
@@ -1,3 +1,4 @@
+import datetime
import os
import tempfile
from io import StringIO
@@ -94,6 +95,7 @@ class ArticleAdmin(admin.ModelAdmin):
list_display = (
'content', 'date', callable_year, 'model_year', 'modeladmin_year',
'model_year_reversed', 'section', lambda obj: obj.title,
+ 'order_by_expression',
)
list_editable = ('section',)
list_filter = ('date', 'section')
@@ -110,6 +112,12 @@ class ArticleAdmin(admin.ModelAdmin):
})
)
+ def order_by_expression(self, obj):
+ return obj.model_year
+ # This ordering isn't particularly useful but shows that expressions can
+ # be used for admin_order_field.
+ order_by_expression.admin_order_field = models.F('date') + datetime.timedelta(days=3)
+
def changelist_view(self, request):
return super().changelist_view(request, extra_context={'extra_var': 'Hello!'})
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 54a0590d21..8cdaa2c9a8 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -349,6 +349,32 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
"Results of sorting on callable are out of order."
)
+ def test_change_list_sorting_callable_query_expression(self):
+ """
+ Query expressions may be used for admin_order_field. (column 9 is
+ order_by_expression in ArticleAdmin).
+ """
+ response = self.client.get(reverse('admin:admin_views_article_changelist'), {'o': '9'})
+ self.assertContentBefore(
+ response, 'Oldest content', 'Middle content',
+ 'Results of sorting on callable are out of order.'
+ )
+ self.assertContentBefore(
+ response, 'Middle content', 'Newest content',
+ 'Results of sorting on callable are out of order.'
+ )
+
+ def test_change_list_sorting_callable_query_expression_reverse(self):
+ response = self.client.get(reverse('admin:admin_views_article_changelist'), {'o': '-9'})
+ self.assertContentBefore(
+ response, 'Middle content', 'Oldest content',
+ 'Results of sorting on callable are out of order.'
+ )
+ self.assertContentBefore(
+ response, 'Newest content', 'Middle content',
+ 'Results of sorting on callable are out of order.'
+ )
+
def test_change_list_sorting_model(self):
"""
Ensure we can sort on a list_display field that is a Model method