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.