This template tag was inspired by http://djangosnippets.org/snippets/592/, but with improvements in the syntax it is used with to be more function-like, and avoiding the problem of conditional recursion as noted in http://djangosnippets.org/comments/cr/15/592/#c2472.
The syntax for using it can be seen in the docstring of the defrecurse() function. Additionally, a magic "level" variable is used to indicate the level of recursion, starting with 0 for the outermost level.
This should theoretically allow for nested recursion, but the inner {% recurse %} call cannot call the outer {% defrecurse %} block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | ###############################################################################
# Copyright: Red Hat, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
###############################################################################
from django import template
register = template.Library()
TemplateSyntaxError = template.TemplateSyntaxError
class RecurseNode(template.Node):
def __init__(self, params):
self.params = params
def render(self, context):
try:
recurse_ctx = context['_recurse_ctx']
except KeyError:
raise TemplateSyntaxError("{0} found outside recursion context")
args = [param.resolve(context) for param in self.params]
return recurse_ctx._render(context, *args)
class RecurseDefinitionNode(template.Node):
def __init__(self, params, nodelist):
self.params = params
self.nodelist = nodelist
def _render(self, context, *args):
context.push()
context['level'] = context.get('level', -1) + 1
context['_recurse_ctx'] = self
try:
if args and len(args) != len(self.params):
raise IndexError
for i, arg in enumerate(args):
context[self.params[i]] = arg
except IndexError:
raise TemplateSyntaxError("Number of arguments passed to recurse "
"do not match defrecurse")
output = self.nodelist.render(context)
context.pop()
return output
def render(self, context):
return self._render(context)
@register.tag
def defrecurse(parser, token):
"""
Recursively render things in Django templates.
Usage:
{% defrecurse param... %}
<ul>
{% for child in param.children %}
<li>{{ child }}
{% recurse child... %}
</li>
{% endfor %}
</ul>
{% enddefrecurse %}
"""
params = token.split_contents()
tag_name = params.pop(0)
nodelist = parser.parse(('enddefrecurse',))
parser.delete_first_token()
if not nodelist.get_nodes_by_type(RecurseNode):
raise TemplateSyntaxError(
"No recursion inside {0} block".format(tag_name))
return RecurseDefinitionNode(params, nodelist)
@register.tag
def recurse(parser, token):
params = token.split_contents()
tag_name = params.pop(0)
params = [parser.compile_filter(param) for param in params]
return RecurseNode(params)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.