Code of Conduct
Feature Description
In python, we can use the map function to perform an operation on each member of a list (or list-like object) and then return the results in a new list. For example, assuming a queryset of users:
format_func = lambda user: f'<div><b>{{ user.name }}</b> from {{ user.city }} ({{ user.age }}</div>'
formatted_users_list = map(format_func, users)
It would be helpful to be able to do this directly from within a template, so that, given a users queryset, we could derive formatted_users_list, which could then be passed on to another template tag or fed into another template for further processing. I would propose the following format:
{% map user in users as formatted_users_list %}
<div><b>{{ user.name }}</b> from {{ user.city }} ({{ user.age }}</div>
{% endmap %}
In both of the above cases, formatted_users_list now holds formatted HTML for each user record. This list could then be passed to another template as follows:
{% include 'some/fixed/template.html' only_a_list_no_strings=formatted_users_list %}
Problem
If I have a list-like object, each of whose members need to be formatted in a particular way, and I want to display results directly on the page, a simple for loop will do the trick. If I want to capture the output as a string, a "simple_block_tag" will work. But if I want to format members and then pass results as a list to another template that requires a list rather than a string, then I need a template tag that operates on an iterable in template context and then injects the resulting list back into template context in a manner that is visible to subsequent tags / includes. At present, there is no straightforward way to do this.
Request or proposal
proposal
Additional Details
I've already built it. See below.
Implementation Suggestions
The following code is tested and working.
class MapNode(template.Node):
def __init__(self, item_var, items_iterable, new_list_name, nodelist):
self.item_var = item_var
self.items_iterable = items_iterable
self.new_list_name = new_list_name
self.nodelist = nodelist
def render(self, context):
new_list = []
for item in self.items_iterable.resolve(context, ignore_failures=True):
context[self.item_var] = item
new_list.append(self.nodelist.render(context))
context[self.new_list_name] = new_list
return ''
@register.tag('map')
def do_map(parser, token):
"""
This tag is meant to emulate the python ``map`` function, accepting a list of X items, rendering them
one at a time using the template fragment within the map block, and saving the result into a template
variable, which can then be passed on to another template tag or template.
For example, suppose we have a list of users and want to wrap each item in the list in HTML. We might
do this in python as follows before passing it into template context::
wrapped_users = map(lambda user: f"<div><b>{user.name}</b> from {user.city} ({user.age})</div>", users)
Or we could do it directly inside a Django template as follows::
{% map user in users as wrapped_users %}
<div><b>{{ user.name }}</b> from {{ user.city }} ({{ user.age }})</div>
{% endmap %}
Either way, ``wrapped_users`` will now contain one HTML formatted item for each user in ``users``.
The syntax here is similar to that of a for loop, but unlike the for loop, which renders output in place,
an "as" clause is required here to indicate the name of the context variable in which the resulting list
should be stored. This variable will then be injected into context for later use.
"""
error = False
try:
_, item_var, in_kw, items_iterable, as_kw, new_list_name = token.split_contents()
if in_kw != 'in' or as_kw != 'as':
error = True
except:
error = True
if error:
raise TemplateSyntaxError("'map' tags take the form {%% map ITEM_VAR in ITEMS_ITERABLE"
" as NEW_LIST_NAME %%}; you entered {%% %s %%}" % token.contents)
nodelist = parser.parse(('endmap',))
parser.delete_first_token()
items_iterable = parser.compile_filter(items_iterable)
return MapNode(item_var, items_iterable, new_list_name, nodelist)
Code of Conduct
Feature Description
In python, we can use the
mapfunction to perform an operation on each member of a list (or list-like object) and then return the results in a new list. For example, assuming a queryset of users:It would be helpful to be able to do this directly from within a template, so that, given a
usersqueryset, we could deriveformatted_users_list, which could then be passed on to another template tag or fed into another template for further processing. I would propose the following format:In both of the above cases,
formatted_users_listnow holds formatted HTML for each user record. This list could then be passed to another template as follows:Problem
If I have a list-like object, each of whose members need to be formatted in a particular way, and I want to display results directly on the page, a simple for loop will do the trick. If I want to capture the output as a string, a "simple_block_tag" will work. But if I want to format members and then pass results as a list to another template that requires a list rather than a string, then I need a template tag that operates on an iterable in template context and then injects the resulting list back into template context in a manner that is visible to subsequent tags / includes. At present, there is no straightforward way to do this.
Request or proposal
proposal
Additional Details
I've already built it. See below.
Implementation Suggestions
The following code is tested and working.