Opened 4 years ago

Closed 4 years ago

Last modified 3 years ago

#31975 closed New feature (wontfix)

Add support for list of fields to admin_order_field.

Reported by: Petr Dlouhý Owned by: nobody
Component: contrib.admin Version: 3.1
Severity: Normal Keywords: admin_order_field, ModelAdmin
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

If I try to assign list to admin_order_field to sort on multiple fields (like Model.Meta.ordering does) like this:

@admin.register(Invoice)
class InvoiceAdmin(ModelAdmin):

    def get_full_number(self, invoice):
        return invoice.full_number
    get_full_number.admin_order_field = ['issued__year', 'issued__month', '-number']

I got following error:

  File "/home/petr/.local/share/virtualenvs/blenderhub_server-eFlwzMqz/lib/python3.7/site-packages/django/contrib/admin/options.py", line 1693, in changelist_view
    cl = self.get_changelist_instance(request)
  File "/home/petr/.local/share/virtualenvs/blenderhub_server-eFlwzMqz/lib/python3.7/site-packages/django/contrib/admin/options.py", line 748, in get_changelist_instance
    sortable_by,
  File "/home/petr/.local/share/virtualenvs/blenderhub_server-eFlwzMqz/lib/python3.7/site-packages/django/contrib/admin/views/main.py", line 99, in __init__
    self.queryset = self.get_queryset(request)
  File "/home/petr/.local/share/virtualenvs/blenderhub_server-eFlwzMqz/lib/python3.7/site-packages/django/contrib/admin/views/main.py", line 479, in get_queryset
    ordering = self.get_ordering(request, qs)
  File "/home/petr/.local/share/virtualenvs/blenderhub_server-eFlwzMqz/lib/python3.7/site-packages/django/contrib/admin/views/main.py", line 328, in get_ordering
    elif order_field.startswith('-') and pfx == '-':
AttributeError: 'list' object has no attribute 'startswith'

This is similar to https://code.djangoproject.com/ticket/30981 which has been fixed, but sorting on multiple fields is still hard to achieve.

Change History (7)

comment:1 by Petr Dlouhý, 4 years ago

Keywords: admin_order_field ModelAdmin added

comment:2 by Mariusz Felisiak, 4 years ago

Resolution: invalid
Status: newclosed

admin_order_field doesn't support multiple columns as documented, you can try to use admin_order_field = Concat('issued__year', 'issued__month', 'number').

comment:3 by Petr Dlouhý, 4 years ago

@Mariusz Felisiak You cannot achieve the intended behaviour by that Concat, because
a) I wanted to sort the number in reverse order
b) The length and content of the concatenated strings can vary.

I spend quite some time figuring up that concatenate function, until finally I gave up.

So I think, this is valid use case for feature request, because the concatenation is either not possible at all, or it would be quite complex and very unreadable code.
I looked at the ChangeList.get_ordering() function, and since it returns ordering list it might be quite simple code.

comment:4 by Petr Dlouhý, 4 years ago

Resolution: invalid
Status: closednew

I tried to modify the code of the get_ordering function, and I came with following changed code (based on Django 3.0 code), that seems to be working as expected (it is just the section in the try - catch block):

                    none, pfx, idx = p.rpartition('-')
                    field_name = self.list_display[int(idx)]
                    ordering_fields = self.get_ordering_field(field_name)
                    if not isinstance(ordering_fields, list):
                        ordering_fields = [ordering_fields]
                    for order_field in ordering_fields:
                        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
                        elif order_field.startswith('-') and pfx == '-':
                            ordering.append(order_field[1:])
                        else:
                            ordering.append(pfx + order_field)

comment:5 by Petr Dlouhý, 4 years ago

Type: UncategorizedNew feature

comment:6 by Mariusz Felisiak, 4 years ago

Resolution: wontfix
Status: newclosed
Summary: admin_order_field in ModelAdmin doesn't support sorting on multiple columnsAdd support for list of fields to admin_order_field.

I'm not convinced, it looks niche. We don't want to add unnecessary complexity to the API. If you have a property that generates full_number you should be able to use database functions to do the same, e.g. use LPad() to get the same length etc. On the other hand, if full_number is really complex I would recommend to store it in the database instead of using a dynamic property.

You can start a discussion on DevelopersMailingList if you don't agree.

comment:7 by Petr Dlouhý, 3 years ago

I don't think, that is that niche, I even found Django snippet for this created years ago: https://djangosnippets.org/snippets/10471/

On one hand it is small modification of Django code and small increase in API complexity (which is consistent with the logic of other order fields, BTW), on the other hand is many hours spend by programmers creating unreadable Concat code in users applications and/or copying of large portion of get_queryset function.

Note: See TracTickets for help on using tickets.
Back to Top