diff options
author | David Lord <davidism@gmail.com> | 2021-04-05 14:44:37 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-05 14:44:37 -0700 |
commit | c752dc81834ecfdc5635e4b5b24b9a74ed474def (patch) | |
tree | 0c6bb6b23598c54f1332d58c2feae705a20d5f03 | |
parent | 4a941cab0ddff1ce58f4c6634e22a7659fd0648b (diff) | |
parent | 34fbec9a726113381a27c30d2363aae676b9086a (diff) | |
download | jinja2-c752dc81834ecfdc5635e4b5b24b9a74ed474def.tar.gz |
Merge pull request #1360 from lisongmin/add-default-value-to-groupby
Ability to set default value in groupby filter
-rw-r--r-- | CHANGES.rst | 2 | ||||
-rw-r--r-- | src/jinja2/asyncfilters.py | 3 | ||||
-rw-r--r-- | src/jinja2/filters.py | 19 | ||||
-rw-r--r-- | tests/test_filters.py | 15 |
4 files changed, 36 insertions, 3 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index dd3739f..c94894a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -58,6 +58,8 @@ Unreleased - ``NativeEnvironment`` supports async mode. :issue:`1362` - Template rendering only treats ``\n``, ``\r\n`` and ``\r`` as line breaks. Other characters are left unchanged. :issue:`769, 952, 1313` +- ``|groupby`` filter takes an optional ``default`` argument. + :issue:`1359` Version 2.11.3 diff --git a/src/jinja2/asyncfilters.py b/src/jinja2/asyncfilters.py index 11b031a..dfd8cba 100644 --- a/src/jinja2/asyncfilters.py +++ b/src/jinja2/asyncfilters.py @@ -107,8 +107,9 @@ async def do_groupby( environment: "Environment", value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", attribute: t.Union[str, int], + default: t.Optional[t.Any] = None, ) -> "t.List[t.Tuple[t.Any, t.List[V]]]": - expr = filters.make_attrgetter(environment, attribute) + expr = filters.make_attrgetter(environment, attribute, default=default) return [ filters._GroupTuple(key, await auto_to_seq(values)) for key, values in groupby(sorted(await auto_to_seq(value), key=expr), expr) diff --git a/src/jinja2/filters.py b/src/jinja2/filters.py index 33353e5..c925f83 100644 --- a/src/jinja2/filters.py +++ b/src/jinja2/filters.py @@ -1116,7 +1116,10 @@ class _GroupTuple(t.NamedTuple): @environmentfilter def do_groupby( - environment: "Environment", value: "t.Iterable[V]", attribute: t.Union[str, int] + environment: "Environment", + value: "t.Iterable[V]", + attribute: t.Union[str, int], + default: t.Optional[t.Any] = None, ) -> "t.List[t.Tuple[t.Any, t.List[V]]]": """Group a sequence of objects by an attribute using Python's :func:`itertools.groupby`. The attribute can use dot notation for @@ -1148,10 +1151,22 @@ def do_groupby( <li>{{ group.grouper }}: {{ group.list|join(", ") }} {% endfor %}</ul> + You can specify a ``default`` value to use if an object in the list + does not have the given attribute. + + .. sourcecode:: jinja + + <ul>{% for city, items in users|groupby("city", default="NY") %} + <li>{{ city }}: {{ items|map(attribute="name")|join(", ") }}</li> + {% endfor %}</ul> + + .. versionchanged:: 3.0 + Added the ``default`` parameter. + .. versionchanged:: 2.6 The attribute supports dot notation for nested access. """ - expr = make_attrgetter(environment, attribute) + expr = make_attrgetter(environment, attribute, default=default) return [ _GroupTuple(key, list(values)) for key, values in groupby(sorted(value, key=expr), expr) diff --git a/tests/test_filters.py b/tests/test_filters.py index 2c119c3..0843246 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -593,6 +593,21 @@ class TestFilter: "", ] + def test_groupby_default(self, env): + tmpl = env.from_string( + "{% for city, items in users|groupby('city', default='NY') %}" + "{{ city }}: {{ items|map(attribute='name')|join(', ') }}\n" + "{% endfor %}" + ) + out = tmpl.render( + users=[ + {"name": "emma", "city": "NY"}, + {"name": "smith", "city": "WA"}, + {"name": "john"}, + ] + ) + assert out == "NY: emma, john\nWA: smith\n" + def test_filtertag(self, env): tmpl = env.from_string( "{% filter upper|replace('FOO', 'foo') %}foobar{% endfilter %}" |