How do you parse and inject additional nodes in a Jinja extension? How do you parse and inject additional nodes in a Jinja extension? django django

How do you parse and inject additional nodes in a Jinja extension?


templatetags/wrap.py

class WrapExtension(jinja2.ext.Extension):    tags = set(['wrap'])    template = None    def parse(self, parser):        tag = parser.stream.current.value        lineno = parser.stream.next().lineno        args, kwargs = self.parse_args(parser)        body = parser.parse_statements(['name:end{}'.format(tag)], drop_needle=True)        return nodes.CallBlock(self.call_method('wrap', args, kwargs), [], [], body).set_lineno(lineno)    def parse_args(self, parser):        args = []        kwargs = []        require_comma = False        while parser.stream.current.type != 'block_end':            if require_comma:                parser.stream.expect('comma')            if parser.stream.current.type == 'name' and parser.stream.look().type == 'assign':                key = parser.stream.current.value                parser.stream.skip(2)                value = parser.parse_expression()                kwargs.append(nodes.Keyword(key, value, lineno=value.lineno))            else:                if kwargs:                    parser.fail('Invalid argument syntax for WrapExtension tag',                                parser.stream.current.lineno)                args.append(parser.parse_expression())            require_comma = True        return args, kwargs    @jinja2.contextfunction    def wrap(self, context, caller, template=None, *args, **kwargs):        return self.environment.get_template(template or self.template).render(dict(context, content=caller(), **kwargs))

base.html.j2

<h1>dsd</h1>{% wrap template='wrapper.html.j2' %}    {% for i in range(3) %}        im wrapped content {{ i }}<br>    {% endfor %}{% endwrap %}

wrapper.html.j2

Hello im wrapper<br><hr>{{ content|safe }}<hr>         

args/kwargs parsing get from here https://github.com/Suor/django-cacheops/blob/master/cacheops/jinja2.py


Additionally, the above can be extended to support additional tags with a default template specified as the wrapper:

templatetags/example.py

class ExampleExtension(WrapExtension):    tags = set(['example'])    template = 'example.html.j2'

base.html.j2

{% example otherstuff=True, somethingelse=False %}    {% for i in range(3) %}        im wrapped content {{ i }}<br>    {% endfor %}{% endexample %}


The better way to handle this is using macros. Define it with:

{% macro wrapper() -%}<div>    some ifs and stuff    {{ caller() }}    more ifs and stuff</div>{%- endmacro %}

And use later with a call tag:

{% call wrapper() %}    <img src="{{ url('image:thumbnail' ... }}">{% endcall %}

Macro can have arguments like python function and could be imported:

{% from macros import wrapper %}

See documentation for macro, call and import tags for more details.