Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

The sum filter can now sum up attributes

  • Loading branch information...
commit dd8afa2204149cf134e4ea30ebe51086e775218f 1 parent bc56cd2
@mitsuhiko authored
Showing with 55 additions and 13 deletions.
  1. +4 −0 CHANGES
  2. +30 −8 jinja2/filters.py
  3. +21 −5 jinja2/testsuite/filters.py
View
4 CHANGES
@@ -24,6 +24,10 @@ Version 2.6
earlier versions after an upgrade of the Python interpreter you don't have
to upgrade, it's enough to flush the bytecode cache. This just no longer
makes this necessary, Jinja2 will automatically detect these cases now.
+- the sum filter can now sum up values by attribute. This is a backwards
+ incompatible change. The argument to the filter previously was the
+ optional starting index which defaultes to zero. This now became the
+ second argument to the function because it's rarely used.
Version 2.5.5
-------------
View
38 jinja2/filters.py
@@ -48,6 +48,21 @@ def environmentfilter(f):
return f
+def make_attrgetter(environment, attribute):
+ """Returns a callable that looks up the given attribute from a
+ passed object with the rules of the environment. Dots are allowed
+ to access attributes of attributes.
+ """
+ if '.' not in attribute:
+ return lambda x: environment.getitem(x, attribute)
+ attribute = attribute.split('.')
+ def attrgetter(item):
+ for part in attribute:
+ item = environment.getitem(item, part)
+ return item
+ return attrgetter
+
+
def do_forceescape(value):
"""Enforce HTML escaping. This will probably double escape variables."""
if hasattr(value, '__html__'):
@@ -600,13 +615,7 @@ def do_groupby(environment, value, attribute):
It's now possible to use dotted notation to group by the child
attribute of another attribute.
"""
- if '.' in attribute:
- def expr(item):
- for part in attribute.split('.'):
- item = environment.getitem(item, part)
- return item
- else:
- expr = lambda x: environment.getitem(x, attribute)
+ expr = make_attrgetter(environment, attribute)
return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
@@ -619,6 +628,19 @@ def __new__(cls, (key, value)):
return tuple.__new__(cls, (key, list(value)))
+@environmentfilter
+def do_sum(environment, iterable, attribute=None, start=0):
+ """Sums up an iterable.
+
+ .. versionchanged:: 2.6
+ The `attribute` parameter was added to allow suming up over
+ attributes.
+ """
+ if attribute is not None:
+ iterable = imap(make_attrgetter(environment, attribute), iterable)
+ return sum(iterable, start)
+
+
def do_list(value):
"""Convert the value into a list. If it was a string the returned list
will be a list of characters.
@@ -720,7 +742,7 @@ def do_attr(environment, obj, name):
'striptags': do_striptags,
'slice': do_slice,
'batch': do_batch,
- 'sum': sum,
+ 'sum': do_sum,
'abs': abs,
'round': do_round,
'groupby': do_groupby,
View
26 jinja2/testsuite/filters.py
@@ -204,6 +204,22 @@ def test_sum(self):
tmpl = env.from_string('''{{ [1, 2, 3, 4, 5, 6]|sum }}''')
assert tmpl.render() == '21'
+ def test_sum_attributes(self):
+ tmpl = env.from_string('''{{ values|sum('value') }}''')
+ assert tmpl.render(values=[
+ {'value': 23},
+ {'value': 1},
+ {'value': 18},
+ ]) == '42'
+
+ def test_sum_attributes_nested(self):
+ tmpl = env.from_string('''{{ values|sum('real.value') }}''')
+ assert tmpl.render(values=[
+ {'real': {'value': 23}},
+ {'real': {'value': 1}},
+ {'real': {'value': 18}},
+ ]) == '42'
+
def test_abs(self):
tmpl = env.from_string('''{{ -1|abs }}|{{ 1|abs }}''')
assert tmpl.render() == '1|1', tmpl.render()
@@ -234,9 +250,13 @@ def test_sort1(self):
assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
def test_sort2(self):
- tmpl = env.from_string('{{ "".join(["c", "A", "b", "D"]|sort(false, true)) }}')
+ tmpl = env.from_string('{{ "".join(["c", "A", "b", "D"]|sort) }}')
assert tmpl.render() == 'AbcD'
+ def test_sort3(self):
+ tmpl = env.from_string('''{{ ['foo', 'Bar', 'blah']|sort }}''')
+ assert tmpl.render() == "['Bar', 'blah', 'foo']"
+
def test_groupby(self):
tmpl = env.from_string('''
{%- for grouper, list in [{'foo': 1, 'bar': 2},
@@ -306,10 +326,6 @@ def test_safe(self):
tmpl = env.from_string('{{ "<div>foo</div>" }}')
assert tmpl.render() == '&lt;div&gt;foo&lt;/div&gt;'
- def test_sort2(self):
- tmpl = env.from_string('''{{ ['foo', 'Bar', 'blah']|sort }}''')
- assert tmpl.render() == "['Bar', 'blah', 'foo']"
-
def suite():
suite = unittest.TestSuite()
Please sign in to comment.
Something went wrong with that request. Please try again.