Is it possible to write a Jinja2 Extension that, while rendering, has access to the template context? I want to write an extension that accesses a context variable and outputs some data based on that variable. I couldn't find enough information on how to write such an extension.
Right now, I have this:
class CsrfExtension(jinja2.ext.Extension):
r""" Adds a {% csrf %} tag to Jinja. """
tags = set(['csrf'])
template = '<input type="hidden" name="csrfmiddlewaretoken" value="%s">'
def parse(self, parser):
token = next(parser.stream)
lineno = token.lineno
return self.call_method('_render_csrf', lineno=lineno)
def _render_csrf(self, value, name, *args, **kwargs):
csrf_token = somehow_get_variable('csrf_token')
return jinja2.Markup(self.template % csrf_token)
But, in foo.jinja
<!DOCTYPE html>
<html>
<body>
<h1>This is a Test</h1>
{% csrf %}
</body>
</html>
I get
SyntaxError at /
invalid syntax (foo.jinja, line 7)
I thought I'd get a NameError since somehow_get_variable()
is not defined. I need to know a) how to get the variable from the current context, and b) how to write the Extension correctly.
Also, why line 7? The {% csrf %}
tag is on line 5. Even when I trim foo.jinja
to have only one line with the {% csrf %}
tag in it, it says line 7.
Context contains the dynamic content that you want to inject in your template when it is being rendered.
Jinja, also commonly referred to as "Jinja2" to specify the newest release version, is a Python template engine used to create HTML, XML or other markup formats that are returned to the user via an HTTP response.
Synopsis. A Jinja template is simply a text file. Jinja can generate any text-based format (HTML, XML, CSV, LaTeX, etc.). A Jinja template doesn't need to have a specific extension: . html , .
Found it. Seems like Jinja generates Python code from the Jinja AST (?) and that conversion failed, therefore caused the SyntaxError. jinja2.nodes.ContextReference()
can be used to get the render Context.
class CsrfExtension(jinja2.ext.Extension):
r""" Adds a {% csrf %} tag to Jinja. """
tags = set(['csrf', 'csrf_token'])
template = u'<input type="hidden" name="csrfmiddlewaretoken" value="%s">'
def parse(self, parser):
lineno = next(parser.stream).lineno
ctx_ref = jinja2.nodes.ContextReference()
node = self.call_method('_render_csrf', [ctx_ref], lineno=lineno)
return jinja2.nodes.CallBlock(node, [], [], [], lineno=lineno)
def _render_csrf(self, context, caller):
csrf_token = context['csrf_token']
return jinja2.Markup(self.template % unicode(csrf_token))
csrf = CsrfExtension
I just ran into this problem and I found the solution to be decorating the call method with the @contextfunction
decorator. That tells the runtime to pass the active context as a first argument to the call method.
from jinja2 import nodes
from jinja2.ext import Extension
from jinja2.utils import contextfunction
class MyExtension(Extension):
"""See http://jinja.pocoo.org/docs/2.10/extensions/#module-jinja2.ext
for more information on how to create custom Jinja extensions.
"""
tags = set(['myext'])
def __init__(self, environment):
super(MyExtension, self).__init__(environment)
def parse(self, parser):
lineno = next(parser.stream).lineno
# Parse args if you need them ...
# args = [parser.parse_expression()]
node = nodes.CallBlock(self.call_method('_myext_method', args),
[], [], []).set_lineno(lineno)
return parser.parse_import_context(node, True)
@contextfunction
def _myext_method(self, context, args, caller):
# Return what you need
return "Hello I am a custom extension, rendered with: %r" % context
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With