Add a new `map` template tag that formats a list of items and returns them in a new list
- Dominant language
- No language data
- Stars
- 188
- Forks
- 7
- PR merge metrics
- No merged PRs in 30d
Description
### Code of Conduct
- [x] I agree to follow Django's 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'
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 %}
{% 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"
Or we could do it directly inside a Django template as follows::
{% map user in users as wrapped_users %}
{% 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)
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by locating Django's template tag registration and Node rendering APIs, then compare the proposed MapNode and do_map entry points with existing block tags. No tests or target files are named; done would require an agreed design, implementation, and coverage for the proposed map syntax and resulting context list.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100