Opened 5 years ago

Closed 4 years ago

#25670 closed Bug (fixed)

`dictsort` does not work when `arg` parameter is numeric

Reported by: Andrew Kuchev Owned by: Andrew Kuchev
Component: Template system Version: 1.8
Severity: Normal Keywords: template filter dictsort
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

According to dictsort documentation, it orders given list of dictionaries, using arg as property in each dictionary:

@register.filter(is_safe=False)
def dictsort(value, arg):
    """
    Takes a list of dicts, returns that list sorted by the property given in
    the argument.
    """
    try:
        return sorted(value, key=Variable(arg).resolve)
    except (TypeError, VariableDoesNotExist):
        return ''

However, it is not possible to order list of dictionaries by a numeric key. Let's consider the following test case:

def test_sort_list_of_tuple_like_dicts(self):
    data = [{'0': 'a', '1': '42'},
            {'0': 'c', '1': 'string'},
            {'0': 'b', '1': 'foo'}]

    sorted_data = dictsort(data, '0')

    self.assertEqual([{'0': 'a', '1': '42'},
                      {'0': 'b', '1': 'foo'},
                      {'0': 'c', '1': 'string'}], sorted_data)

This test fails with the following message:

Traceback (most recent call last):
  File ".../django/tests/template_tests/filter_tests/test_dictsort.py", line 50, in test_sort_list_of_tuple_like_dicts
    {'0': 'c', '1': 'string'}], sorted_data)
AssertionError: Lists differ: [{'0': 'a', '1': '42'}, {'0': 'b', '1': 'foo'}, {'0': 'c', '1': 'string'}] != [{'0': 'a', '1': '42'}, {'0': 'c', '1': 'string'}, {'0': 'b', '1': 'foo'}]

First differing element 1:
{'0': 'b', '1': 'foo'}
{'0': 'c', '1': 'string'}

- [{'0': 'a', '1': '42'}, {'0': 'b', '1': 'foo'}, {'0': 'c', '1': 'string'}]
+ [{'0': 'a', '1': '42'}, {'0': 'c', '1': 'string'}, {'0': 'b', '1': 'foo'}]

The dictsort uses sorted function with key=Variable(arg).resolve. When arg is '0', key function should behave like operator.itemgetter('0'), but Variable('0').resolve(context) returns 0 regardless of given context.

There are five usages of dictsort with "0" as arg in debug.py

As mentioned by bmispelon, this may be some kind of regression in Django 1.3.

Change History (8)

comment:1 Changed 5 years ago by Tim Graham

Triage Stage: UnreviewedAccepted

This ticket is a spinoff of #25646.

comment:2 Changed 5 years ago by Andrew Kuchev

Status: newassigned

comment:3 Changed 5 years ago by Tim Graham

Has patch: set

comment:4 Changed 4 years ago by Tim Graham

I'm not sure about this. It seems like the old behavior was somewhat accidental. Having a filter called "dictsort" work on a list of lists seems a bit odd and unintuitive. Maybe we should ask for other opinions on the django-developers mailing list. Maybe there is a common third-party filter that would do the job that we could add to builtins.

comment:5 Changed 4 years ago by Tim Graham

After some further thought, I changed my mind given the fact that dictsort doesn't work with numeric string keys. That's definitely a bug that should be fixed.

comment:6 Changed 4 years ago by Tim Graham

Patch needs improvement: set

I left comments for improvement on the pull request.

comment:7 Changed 4 years ago by Tim Graham

Patch needs improvement: unset

comment:8 Changed 4 years ago by Tim Graham <timograham@…>

Resolution: fixed
Status: assignedclosed

In e81d1c99:

Fixed #25670 -- Allowed dictsort to sort a list of lists.

Thanks Tim Graham for the review.

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