mirror of https://github.com/django/django.git
Fixed #27810 -- Allowed query expressions in admin_order_field.
This commit is contained in:
parent
d368784bac
commit
e307ff29d2
|
@ -286,8 +286,11 @@ class ChangeList:
|
||||||
order_field = self.get_ordering_field(field_name)
|
order_field = self.get_ordering_field(field_name)
|
||||||
if not order_field:
|
if not order_field:
|
||||||
continue # No 'admin_order_field', skip it
|
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
|
# 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:])
|
ordering.append(order_field[1:])
|
||||||
else:
|
else:
|
||||||
ordering.append(pfx + order_field)
|
ordering.append(pfx + order_field)
|
||||||
|
|
|
@ -745,6 +745,24 @@ subclass::
|
||||||
|
|
||||||
author_first_name.admin_order_field = 'author__first_name'
|
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,
|
* Elements of ``list_display`` can also be properties. Please note however,
|
||||||
that due to the way properties work in Python, setting
|
that due to the way properties work in Python, setting
|
||||||
``short_description`` on a property is only possible when using the
|
``short_description`` on a property is only possible when using the
|
||||||
|
|
|
@ -47,6 +47,9 @@ Minor features
|
||||||
:meth:`.ModelAdmin.get_sortable_by` method allow limiting the columns that
|
:meth:`.ModelAdmin.get_sortable_by` method allow limiting the columns that
|
||||||
can be sorted in the change list page.
|
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`
|
:mod:`django.contrib.admindocs`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import datetime
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
@ -94,6 +95,7 @@ class ArticleAdmin(admin.ModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
'content', 'date', callable_year, 'model_year', 'modeladmin_year',
|
'content', 'date', callable_year, 'model_year', 'modeladmin_year',
|
||||||
'model_year_reversed', 'section', lambda obj: obj.title,
|
'model_year_reversed', 'section', lambda obj: obj.title,
|
||||||
|
'order_by_expression',
|
||||||
)
|
)
|
||||||
list_editable = ('section',)
|
list_editable = ('section',)
|
||||||
list_filter = ('date', '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):
|
def changelist_view(self, request):
|
||||||
return super().changelist_view(request, extra_context={'extra_var': 'Hello!'})
|
return super().changelist_view(request, extra_context={'extra_var': 'Hello!'})
|
||||||
|
|
||||||
|
|
|
@ -349,6 +349,32 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
|
||||||
"Results of sorting on callable are out of order."
|
"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):
|
def test_change_list_sorting_model(self):
|
||||||
"""
|
"""
|
||||||
Ensure we can sort on a list_display field that is a Model method
|
Ensure we can sort on a list_display field that is a Model method
|
||||||
|
|
Loading…
Reference in New Issue