From 044cc544203fdcf2601ff200547ecae2e51a5db5 Mon Sep 17 00:00:00 2001 From: Jani Tiainen Date: Tue, 19 Mar 2019 17:51:35 +0200 Subject: [PATCH] Fixed #30259 -- Added support for admin_order_field attribute on properties in ModelAdmin.list_display. --- .../contrib/admin/templatetags/admin_list.py | 3 +++ django/contrib/admin/views/main.py | 2 ++ docs/ref/contrib/admin/index.txt | 6 ++++-- docs/releases/3.0.txt | 3 ++- tests/admin_views/admin.py | 2 +- tests/admin_views/models.py | 5 +++++ tests/admin_views/tests.py | 19 +++++++++++++++++++ 7 files changed, 36 insertions(+), 4 deletions(-) diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index b03e4b6470..fe4249ae5b 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -128,6 +128,9 @@ def result_headers(cl): continue admin_order_field = getattr(attr, "admin_order_field", None) + # Set ordering for attr that is a property, if defined. + if isinstance(attr, property) and hasattr(attr, 'fget'): + admin_order_field = getattr(attr.fget, 'admin_order_field') if not admin_order_field: is_field_sortable = False diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 978910df26..7d8d33d9d7 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -264,6 +264,8 @@ class ChangeList: attr = getattr(self.model_admin, field_name) else: attr = getattr(self.model, field_name) + if isinstance(attr, property) and hasattr(attr, 'fget'): + attr = attr.fget return getattr(attr, 'admin_order_field', None) def get_ordering(self, request, queryset): diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index b4fa8c5035..93679c46fd 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -754,8 +754,9 @@ subclass:: * 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 - ``property()`` function and **not** with the ``@property`` decorator. + ``short_description`` or ``admin_order_field`` on a property is only + possible when using the ``property()`` function and **not** with the + ``@property`` decorator. For example:: @@ -766,6 +767,7 @@ subclass:: def my_property(self): return self.first_name + ' ' + self.last_name my_property.short_description = "Full name of the person" + my_property.admin_order_field = 'last_name' full_name = property(my_property) diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt index 69925e7fca..47fa80d30b 100644 --- a/docs/releases/3.0.txt +++ b/docs/releases/3.0.txt @@ -44,7 +44,8 @@ Minor features :mod:`django.contrib.admin` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* ... +* Added support for ``admin_order_field`` attribute on properties in + :attr:`.ModelAdmin.list_display`. :mod:`django.contrib.admindocs` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index 0b0ad41e2e..3e8df5b7c2 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -104,7 +104,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', + 'order_by_expression', 'model_property_year', ) list_editable = ('section',) list_filter = ('date', 'section') diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index d134b34923..aaf86ea1cd 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -55,6 +55,11 @@ class Article(models.Model): model_year_reversed.admin_order_field = '-date' model_year_reversed.short_description = '' + def property_year(self): + return self.date.year + property_year.admin_order_field = 'date' + model_property_year = property(property_year) + class Book(models.Model): """ diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 3acbc3d7ed..87835eb37b 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -344,6 +344,25 @@ class AdminViewBasicTest(AdminViewBasicTestCase): "Results of sorting on callable are out of order." ) + def test_change_list_sorting_property(self): + """ + Sort on a list_display field that is a property (column 10 is + a property in Article model). + """ + response = self.client.get(reverse('admin:admin_views_article_changelist'), {'o': 10}) + self.assertContentBefore( + response, + 'Oldest content', + 'Middle content', + 'Results of sorting on property are out of order.', + ) + self.assertContentBefore( + response, + 'Middle content', + 'Newest content', + 'Results of sorting on property 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