From: Brendan Date: Mon, 6 May 2019 15:49:48 +0000 (-0400) Subject: Adding a default parameter to builtin map filter X-Git-Tag: 2.11.0~76^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8b09b03aa061bfa0c9a97de382b2a503e38e7304;p=thirdparty%2Fjinja.git Adding a default parameter to builtin map filter --- diff --git a/CHANGES.rst b/CHANGES.rst index a41b887d..5c55276a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,8 +14,10 @@ unreleased slow initial import. (`#765`_) - Python 2.6 and 3.3 are not supported anymore. - The `map` filter in async mode now automatically awaits +- Added `default` parameter for the `map` filter. (`#985`_) .. _#765: https://github.com/pallets/jinja/issues/765 +.. _#985: https://github.com/pallets/jinja/pull/985 Version 2.10.1 diff --git a/jinja2/filters.py b/jinja2/filters.py index bf5173c1..94751b87 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, postprocess=None, default=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 @@ -76,6 +76,10 @@ def make_attrgetter(environment, attribute, postprocess=None): for part in attribute: item = environment.getitem(item, part) + if default and isinstance(item, Undefined): + item = default + break + if postprocess is not None: item = postprocess(item) @@ -961,6 +965,13 @@ def do_map(*args, **kwargs): Users on this page: {{ users|map(attribute='username')|join(', ') }} + If the list of objects may not contain the given attribute, a default + value may be provided. + + .. sourcecode:: jinja + + {{ 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: @@ -1098,10 +1109,11 @@ def prepare_map(args, kwargs): if len(args) == 2 and 'attribute' in kwargs: attribute = kwargs.pop('attribute') + default = kwargs.pop('default', None) 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 60808f2e..f9634389 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -561,6 +561,22 @@ class TestFilter(object): tmpl = env.from_string('{{ users|map(attribute="name")|join("|") }}') assert tmpl.render(users=users) == 'john|jane|mike' + def test_attribute_map_default(self, env): + class User(object): + def __init__(self, name): + self.name = name + class NotUser(object): + def __init__(self, not_name): + self.not_name = not_name + env = Environment() + users = [ + User('john'), + User('jane'), + NotUser('plant'), + ] + tmpl = env.from_string('{{ users|map(attribute="name", default="anonymous")|join("|") }}') + assert tmpl.render(users=users) == 'john|jane|anonymous' + def test_empty_map(self, env): env = Environment() tmpl = env.from_string('{{ none|map("upper")|list }}')