Commit e81d1c99 authored by Andrew Kuchev's avatar Andrew Kuchev Committed by Tim Graham
Browse files

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

Thanks Tim Graham for the review.
parent cdbd8745
Loading
Loading
Loading
Loading
+29 −2
Original line number Diff line number Diff line
@@ -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
@@ -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):
    """
@@ -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 ''

@@ -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 ''

+5 −5
Original line number Diff line number Diff line
@@ -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>
@@ -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>
@@ -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>
@@ -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 %}
+34 −0
Original line number Diff line number Diff line
@@ -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``
+3 −0
Original line number Diff line number Diff line
@@ -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
~~~~~

+18 −0
Original line number Diff line number Diff line
@@ -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