Login

Recursive template tag for Django with arguments

Author:
hyperair
Posted:
December 17, 2012
Language:
Python
Version:
1.4
Score:
1 (after 1 ratings)

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

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

Please login first before commenting.