Browse Source

Fixed #27810 -- Allowed query expressions in admin_order_field.

Andreas Pelme 7 years ago
parent
commit
e307ff29d2

+ 4 - 1
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)

+ 18 - 0
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

+ 3 - 0
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`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 8 - 0
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!'})
 

+ 26 - 0
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