From: Kevin Date: Fri, 31 May 2019 17:59:01 +0000 (-0400) Subject: add default to map filter X-Git-Tag: 2.11.0~78^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F1018%2Fhead;p=thirdparty%2Fjinja.git add default to map filter --- diff --git a/CHANGES.rst b/CHANGES.rst index 63ad274f..2b6b7632 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -16,9 +16,11 @@ unreleased - The ``map`` filter in async mode now automatically awaits - Added a new ``ChainableUndefined`` class to support getitem and getattr on an undefined object. (`#977`_) -- Allow `'{%+'` syntax (with NOP behavior) when - `lstrip_blocks == False` (`#748`_) +- Allow ``{%+`` syntax (with NOP behavior) when + ``lstrip_blocks == False`` (`#748`_) +- Added ``default`` param for ``map`` filter. (`#557`_) +.. _#557: https://github.com/pallets/jinja/issues/557 .. _#765: https://github.com/pallets/jinja/issues/765 .. _#748: https://github.com/pallets/jinja/issues/748 .. _#977: https://github.com/pallets/jinja/issues/977 diff --git a/jinja2/filters.py b/jinja2/filters.py index 834623cd..da3e535c 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -59,7 +59,7 @@ def ignore_case(value): return value.lower() if isinstance(value, string_types) else value -def make_attrgetter(environment, attribute, postprocess=None): +def make_attrgetter(environment, attribute, default=None, postprocess=None): """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. Integer parts in paths are @@ -71,6 +71,9 @@ def make_attrgetter(environment, attribute, postprocess=None): for part in attribute: item = environment.getitem(item, part) + if isinstance(item, Undefined) and default: + item = default + if postprocess is not None: item = postprocess(item) @@ -352,8 +355,9 @@ def _min_or_max(environment, value, func, case_sensitive, attribute): return environment.undefined('No aggregated item, sequence was empty.') key_func = make_attrgetter( - environment, attribute, - ignore_case if not case_sensitive else None + environment, + attribute, + postprocess=ignore_case if not case_sensitive else None ) return func(chain([first], it), key=key_func) @@ -1003,6 +1007,13 @@ def do_map(*args, **kwargs): Users on this page: {{ users|map(attribute='username')|join(', ') }} + You can specify a ``default`` value to use if an object in the list + does not have the given attribute. + + .. sourcecode:: jinja + + Users on this page: {{ users|map(attribute="username", default="Anonymous")|join(", ") }} + Alternatively you can let it invoke a filter by passing the name of the filter and the arguments afterwards. A good example would be applying a text conversion filter on a sequence: @@ -1011,6 +1022,9 @@ def do_map(*args, **kwargs): Users on this page: {{ titles|map('lower')|join(', ') }} + .. versionchanged:: 2.11.0 + Added the ``default`` parameter. + .. versionadded:: 2.7 """ seq, func = prepare_map(args, kwargs) @@ -1137,13 +1151,16 @@ def do_tojson(eval_ctx, value, indent=None): def prepare_map(args, kwargs): context = args[0] seq = args[1] + default = None if len(args) == 2 and 'attribute' in kwargs: attribute = kwargs.pop('attribute') + if 'default' in kwargs: + default = kwargs.pop('default') if kwargs: raise FilterArgumentError('Unexpected keyword argument %r' % next(iter(kwargs))) - func = make_attrgetter(context.environment, attribute) + func = make_attrgetter(context.environment, attribute, default=default) else: try: name = args[2] diff --git a/tests/test_filters.py b/tests/test_filters.py index 0a337b63..fd3a0cf9 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -599,6 +599,28 @@ class TestFilter(object): tmpl = env.from_string('{{ none|map("upper")|list }}') assert tmpl.render() == '[]' + def test_map_default(self, env): + class Fullname(object): + def __init__(self, firstname, lastname): + self.firstname = firstname + self.lastname = lastname + + class Firstname(object): + def __init__(self, firstname): + self.firstname = firstname + + env = Environment() + tmpl = env.from_string( + '{{ users|map(attribute="lastname", default="smith")|join(", ") }}' + ) + users = [ + Fullname("john", "lennon"), + Fullname("jane", "edwards"), + Fullname("jon", None), + Firstname("mike") + ] + assert tmpl.render(users=users) == "lennon, edwards, None, smith" + def test_simple_select(self, env): env = Environment() tmpl = env.from_string('{{ [1, 2, 3, 4, 5]|select("odd")|join("|") }}')