Loading django/template/defaultfilters.py +29 −2 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import random as random_module import re from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation from functools import wraps from operator import itemgetter from pprint import pformat from django.utils import formats, six Loading Loading @@ -510,6 +511,32 @@ def striptags(value): # LISTS # ################### def _property_resolver(arg): """ When arg is convertible to float, behave like operator.itemgetter(arg) Otherwise, behave like Variable(arg).resolve >>> _property_resolver(1)('abc') 'b' >>> _property_resolver('1')('abc') Traceback (most recent call last): ... TypeError: string indices must be integers >>> class Foo: ... a = 42 ... b = 3.14 ... c = 'Hey!' >>> _property_resolver('b')(Foo()) 3.14 """ try: float(arg) except ValueError: return Variable(arg).resolve else: return itemgetter(arg) @register.filter(is_safe=False) def dictsort(value, arg): """ Loading @@ -517,7 +544,7 @@ def dictsort(value, arg): the argument. """ try: return sorted(value, key=Variable(arg).resolve) return sorted(value, key=_property_resolver(arg)) except (TypeError, VariableDoesNotExist): return '' Loading @@ -529,7 +556,7 @@ def dictsortreversed(value, arg): property given in the argument. """ try: return sorted(value, key=Variable(arg).resolve, reverse=True) return sorted(value, key=_property_resolver(arg), reverse=True) except (TypeError, VariableDoesNotExist): return '' Loading django/views/debug.py +5 −5 Original line number Diff line number Diff line Loading @@ -813,7 +813,7 @@ TECHNICAL_500_TEMPLATE = (""" </tr> </thead> <tbody> {% for var in frame.vars|dictsort:"0" %} {% for var in frame.vars|dictsort:0 %} <tr> <td>{{ var.0|force_escape }}</td> <td class="code"><pre>{{ var.1 }}</pre></td> Loading Loading @@ -995,7 +995,7 @@ Exception Value: {{ exception_value|force_escape }} </tr> </thead> <tbody> {% for var in request.META.items|dictsort:"0" %} {% for var in request.META.items|dictsort:0 %} <tr> <td>{{ var.0 }}</td> <td class="code"><pre>{{ var.1|pprint }}</pre></td> Loading @@ -1017,7 +1017,7 @@ Exception Value: {{ exception_value|force_escape }} </tr> </thead> <tbody> {% for var in settings.items|dictsort:"0" %} {% for var in settings.items|dictsort:0 %} <tr> <td>{{ var.0 }}</td> <td class="code"><pre>{{ var.1|pprint }}</pre></td> Loading Loading @@ -1107,12 +1107,12 @@ FILES:{% for k, v in request.FILES.items %} COOKIES:{% for k, v in request.COOKIES.items %} {{ k }} = {{ v|stringformat:"r" }}{% empty %} No cookie data{% endfor %} META:{% for k, v in request.META.items|dictsort:"0" %} META:{% for k, v in request.META.items|dictsort:0 %} {{ k }} = {{ v|stringformat:"r" }}{% endfor %} {% else %}Request data not supplied {% endif %} Settings: Using settings module {{ settings.SETTINGS_MODULE }}{% for k, v in settings.items|dictsort:"0" %} Using settings module {{ settings.SETTINGS_MODULE }}{% for k, v in settings.items|dictsort:0 %} {{ k }} = {{ v|stringformat:"r" }}{% endfor %} {% if not is_email %} Loading docs/ref/templates/builtins.txt +34 −0 Original line number Diff line number Diff line Loading @@ -1443,6 +1443,40 @@ then the output would be:: * 1984 (George) * Timequake (Kurt) ``dictsort`` can also order a list of lists (or any other object implementing ``__getitem__()``) by elements at specified index. For example:: {{ value|dictsort:0 }} If ``value`` is: .. code-block:: python [ ('a', '42'), ('c', 'string'), ('b', 'foo'), ] then the output would be: .. code-block:: python [ ('a', '42'), ('b', 'foo'), ('c', 'string'), ] You must pass the index as an integer rather than a string. The following produce empty output:: {{ values|dictsort:"0" }} .. versionchanged:: 1.10 The ability to order a list of lists was added. .. templatefilter:: dictsortreversed ``dictsortreversed`` Loading docs/releases/1.10.txt +3 −0 Original line number Diff line number Diff line Loading @@ -333,6 +333,9 @@ Templates * Added the ``is`` comparison operator to the :ttag:`if` tag. * Allowed :tfilter:`dictsort` to order a list of lists by an element at a specified index. Tests ~~~~~ Loading tests/template_tests/filter_tests/test_dictsort.py +18 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,24 @@ class FunctionTests(SimpleTestCase): self.assertEqual([d['foo']['bar'] for d in sorted_data], [3, 2, 1]) def test_sort_list_of_tuples(self): data = [('a', '42'), ('c', 'string'), ('b', 'foo')] expected = [('a', '42'), ('b', 'foo'), ('c', 'string')] self.assertEqual(dictsort(data, 0), expected) def test_sort_list_of_tuple_like_dicts(self): data = [ {'0': 'a', '1': '42'}, {'0': 'c', '1': 'string'}, {'0': 'b', '1': 'foo'}, ] expected = [ {'0': 'a', '1': '42'}, {'0': 'b', '1': 'foo'}, {'0': 'c', '1': 'string'}, ] self.assertEqual(dictsort(data, '0'), expected) def test_invalid_values(self): """ If dictsort is passed something other than a list of dictionaries, Loading Loading
django/template/defaultfilters.py +29 −2 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import random as random_module import re from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation from functools import wraps from operator import itemgetter from pprint import pformat from django.utils import formats, six Loading Loading @@ -510,6 +511,32 @@ def striptags(value): # LISTS # ################### def _property_resolver(arg): """ When arg is convertible to float, behave like operator.itemgetter(arg) Otherwise, behave like Variable(arg).resolve >>> _property_resolver(1)('abc') 'b' >>> _property_resolver('1')('abc') Traceback (most recent call last): ... TypeError: string indices must be integers >>> class Foo: ... a = 42 ... b = 3.14 ... c = 'Hey!' >>> _property_resolver('b')(Foo()) 3.14 """ try: float(arg) except ValueError: return Variable(arg).resolve else: return itemgetter(arg) @register.filter(is_safe=False) def dictsort(value, arg): """ Loading @@ -517,7 +544,7 @@ def dictsort(value, arg): the argument. """ try: return sorted(value, key=Variable(arg).resolve) return sorted(value, key=_property_resolver(arg)) except (TypeError, VariableDoesNotExist): return '' Loading @@ -529,7 +556,7 @@ def dictsortreversed(value, arg): property given in the argument. """ try: return sorted(value, key=Variable(arg).resolve, reverse=True) return sorted(value, key=_property_resolver(arg), reverse=True) except (TypeError, VariableDoesNotExist): return '' Loading
django/views/debug.py +5 −5 Original line number Diff line number Diff line Loading @@ -813,7 +813,7 @@ TECHNICAL_500_TEMPLATE = (""" </tr> </thead> <tbody> {% for var in frame.vars|dictsort:"0" %} {% for var in frame.vars|dictsort:0 %} <tr> <td>{{ var.0|force_escape }}</td> <td class="code"><pre>{{ var.1 }}</pre></td> Loading Loading @@ -995,7 +995,7 @@ Exception Value: {{ exception_value|force_escape }} </tr> </thead> <tbody> {% for var in request.META.items|dictsort:"0" %} {% for var in request.META.items|dictsort:0 %} <tr> <td>{{ var.0 }}</td> <td class="code"><pre>{{ var.1|pprint }}</pre></td> Loading @@ -1017,7 +1017,7 @@ Exception Value: {{ exception_value|force_escape }} </tr> </thead> <tbody> {% for var in settings.items|dictsort:"0" %} {% for var in settings.items|dictsort:0 %} <tr> <td>{{ var.0 }}</td> <td class="code"><pre>{{ var.1|pprint }}</pre></td> Loading Loading @@ -1107,12 +1107,12 @@ FILES:{% for k, v in request.FILES.items %} COOKIES:{% for k, v in request.COOKIES.items %} {{ k }} = {{ v|stringformat:"r" }}{% empty %} No cookie data{% endfor %} META:{% for k, v in request.META.items|dictsort:"0" %} META:{% for k, v in request.META.items|dictsort:0 %} {{ k }} = {{ v|stringformat:"r" }}{% endfor %} {% else %}Request data not supplied {% endif %} Settings: Using settings module {{ settings.SETTINGS_MODULE }}{% for k, v in settings.items|dictsort:"0" %} Using settings module {{ settings.SETTINGS_MODULE }}{% for k, v in settings.items|dictsort:0 %} {{ k }} = {{ v|stringformat:"r" }}{% endfor %} {% if not is_email %} Loading
docs/ref/templates/builtins.txt +34 −0 Original line number Diff line number Diff line Loading @@ -1443,6 +1443,40 @@ then the output would be:: * 1984 (George) * Timequake (Kurt) ``dictsort`` can also order a list of lists (or any other object implementing ``__getitem__()``) by elements at specified index. For example:: {{ value|dictsort:0 }} If ``value`` is: .. code-block:: python [ ('a', '42'), ('c', 'string'), ('b', 'foo'), ] then the output would be: .. code-block:: python [ ('a', '42'), ('b', 'foo'), ('c', 'string'), ] You must pass the index as an integer rather than a string. The following produce empty output:: {{ values|dictsort:"0" }} .. versionchanged:: 1.10 The ability to order a list of lists was added. .. templatefilter:: dictsortreversed ``dictsortreversed`` Loading
docs/releases/1.10.txt +3 −0 Original line number Diff line number Diff line Loading @@ -333,6 +333,9 @@ Templates * Added the ``is`` comparison operator to the :ttag:`if` tag. * Allowed :tfilter:`dictsort` to order a list of lists by an element at a specified index. Tests ~~~~~ Loading
tests/template_tests/filter_tests/test_dictsort.py +18 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,24 @@ class FunctionTests(SimpleTestCase): self.assertEqual([d['foo']['bar'] for d in sorted_data], [3, 2, 1]) def test_sort_list_of_tuples(self): data = [('a', '42'), ('c', 'string'), ('b', 'foo')] expected = [('a', '42'), ('b', 'foo'), ('c', 'string')] self.assertEqual(dictsort(data, 0), expected) def test_sort_list_of_tuple_like_dicts(self): data = [ {'0': 'a', '1': '42'}, {'0': 'c', '1': 'string'}, {'0': 'b', '1': 'foo'}, ] expected = [ {'0': 'a', '1': '42'}, {'0': 'b', '1': 'foo'}, {'0': 'c', '1': 'string'}, ] self.assertEqual(dictsort(data, '0'), expected) def test_invalid_values(self): """ If dictsort is passed something other than a list of dictionaries, Loading