mirror of https://github.com/django/django.git
Fixed #25670 -- Allowed dictsort to sort a list of lists.
Thanks Tim Graham for the review.
This commit is contained in:
parent
cdbd8745f6
commit
e81d1c995c
|
@ -5,6 +5,7 @@ import random as random_module
|
||||||
import re
|
import re
|
||||||
from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation
|
from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from operator import itemgetter
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
|
|
||||||
from django.utils import formats, six
|
from django.utils import formats, six
|
||||||
|
@ -510,6 +511,32 @@ def striptags(value):
|
||||||
# LISTS #
|
# 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)
|
@register.filter(is_safe=False)
|
||||||
def dictsort(value, arg):
|
def dictsort(value, arg):
|
||||||
"""
|
"""
|
||||||
|
@ -517,7 +544,7 @@ def dictsort(value, arg):
|
||||||
the argument.
|
the argument.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return sorted(value, key=Variable(arg).resolve)
|
return sorted(value, key=_property_resolver(arg))
|
||||||
except (TypeError, VariableDoesNotExist):
|
except (TypeError, VariableDoesNotExist):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
@ -529,7 +556,7 @@ def dictsortreversed(value, arg):
|
||||||
property given in the argument.
|
property given in the argument.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return sorted(value, key=Variable(arg).resolve, reverse=True)
|
return sorted(value, key=_property_resolver(arg), reverse=True)
|
||||||
except (TypeError, VariableDoesNotExist):
|
except (TypeError, VariableDoesNotExist):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
|
@ -813,7 +813,7 @@ TECHNICAL_500_TEMPLATE = ("""
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for var in frame.vars|dictsort:"0" %}
|
{% for var in frame.vars|dictsort:0 %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ var.0|force_escape }}</td>
|
<td>{{ var.0|force_escape }}</td>
|
||||||
<td class="code"><pre>{{ var.1 }}</pre></td>
|
<td class="code"><pre>{{ var.1 }}</pre></td>
|
||||||
|
@ -995,7 +995,7 @@ Exception Value: {{ exception_value|force_escape }}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for var in request.META.items|dictsort:"0" %}
|
{% for var in request.META.items|dictsort:0 %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ var.0 }}</td>
|
<td>{{ var.0 }}</td>
|
||||||
<td class="code"><pre>{{ var.1|pprint }}</pre></td>
|
<td class="code"><pre>{{ var.1|pprint }}</pre></td>
|
||||||
|
@ -1017,7 +1017,7 @@ Exception Value: {{ exception_value|force_escape }}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for var in settings.items|dictsort:"0" %}
|
{% for var in settings.items|dictsort:0 %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ var.0 }}</td>
|
<td>{{ var.0 }}</td>
|
||||||
<td class="code"><pre>{{ var.1|pprint }}</pre></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 %}
|
COOKIES:{% for k, v in request.COOKIES.items %}
|
||||||
{{ k }} = {{ v|stringformat:"r" }}{% empty %} No cookie data{% endfor %}
|
{{ 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 %}
|
{{ k }} = {{ v|stringformat:"r" }}{% endfor %}
|
||||||
{% else %}Request data not supplied
|
{% else %}Request data not supplied
|
||||||
{% endif %}
|
{% endif %}
|
||||||
Settings:
|
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 %}
|
{{ k }} = {{ v|stringformat:"r" }}{% endfor %}
|
||||||
|
|
||||||
{% if not is_email %}
|
{% if not is_email %}
|
||||||
|
|
|
@ -1443,6 +1443,40 @@ then the output would be::
|
||||||
* 1984 (George)
|
* 1984 (George)
|
||||||
* Timequake (Kurt)
|
* 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
|
.. templatefilter:: dictsortreversed
|
||||||
|
|
||||||
``dictsortreversed``
|
``dictsortreversed``
|
||||||
|
|
|
@ -333,6 +333,9 @@ Templates
|
||||||
|
|
||||||
* Added the ``is`` comparison operator to the :ttag:`if` tag.
|
* 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
|
Tests
|
||||||
~~~~~
|
~~~~~
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,24 @@ class FunctionTests(SimpleTestCase):
|
||||||
|
|
||||||
self.assertEqual([d['foo']['bar'] for d in sorted_data], [3, 2, 1])
|
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):
|
def test_invalid_values(self):
|
||||||
"""
|
"""
|
||||||
If dictsort is passed something other than a list of dictionaries,
|
If dictsort is passed something other than a list of dictionaries,
|
||||||
|
|
|
@ -19,6 +19,24 @@ class FunctionTests(SimpleTestCase):
|
||||||
[('age', 18), ('name', 'Jonny B Goode')]],
|
[('age', 18), ('name', 'Jonny B Goode')]],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_sort_list_of_tuples(self):
|
||||||
|
data = [('a', '42'), ('c', 'string'), ('b', 'foo')]
|
||||||
|
expected = [('c', 'string'), ('b', 'foo'), ('a', '42')]
|
||||||
|
self.assertEqual(dictsortreversed(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': 'c', '1': 'string'},
|
||||||
|
{'0': 'b', '1': 'foo'},
|
||||||
|
{'0': 'a', '1': '42'},
|
||||||
|
]
|
||||||
|
self.assertEqual(dictsortreversed(data, '0'), expected)
|
||||||
|
|
||||||
def test_invalid_values(self):
|
def test_invalid_values(self):
|
||||||
"""
|
"""
|
||||||
If dictsortreversed is passed something other than a list of
|
If dictsortreversed is passed something other than a list of
|
||||||
|
|
Loading…
Reference in New Issue