- 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
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
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)
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)
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:
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)
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]
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("|") }}')