Opened 11 years ago

Closed 11 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 by Tim Graham, 11 years ago

Triage Stage: Unreviewed → Accepted

This ticket is a spinoff of #25646.

comment:2 by Andrew Kuchev, 11 years ago

Status: new → assigned

comment:3 by Tim Graham, 11 years ago

Has patch: set

comment:4 by Tim Graham, 11 years ago

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 by Tim Graham, 11 years ago

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 by Tim Graham, 11 years ago

Patch needs improvement: set

I left comments for improvement on the pull request.

comment:7 by Tim Graham, 11 years ago

Patch needs improvement: unset

comment:8 by Tim Graham <timograham@…>, 11 years ago

Resolution: → fixed
Status: assigned → closed

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