Jinja2 compile extension after includes - python

In Jinja2, is it possible to have a Node from the AST render after all include statements have completed?
This is a key piece of a solution to a bigger puzzle.
Example code:
x.py
from jinja2 import nodes, Environment, FileSystemLoader
from jinja2.ext import Extension
class XExtension(Extension):
tags = set(['x', 'get_x'])
def __init__(self, environment):
super(XExtension, self).__init__(environment)
environment.extend(
x = 0,
)
def parse(self, parser):
tag = parser.stream.next()
return getattr(self, "_%s" % str(tag))(parser, tag)
def _get_x(self, parser, tag):
""" Return the output """
return nodes.Const(self.environment.x)
def _x(self, parser, tag):
""" Add an x """
self.environment.x += 1
return nodes.Const('<!-- Adding an X -->')
env = Environment(
loader = FileSystemLoader('.'),
extensions = [XExtension],
)
template = env.get_template('x.html')
print template.render()
x.html
Xx {% x %} Xx {% x %}
{% include "y.html" %}
Xs xes: {% get_x %}
y.html
Yx {% x %}
Ys xes: {% get_x %}
The output is
Xx <!-- Adding an X --> Xx <!-- Adding an X -->
Yx <!-- Adding an X -->
Ys xes:3
Xs xes 2
How may it be possible to have Xs xes count the X's in y.html, which is the behaviour I desire and expect?
In other words, is there a way to delay the parsing or flattening to text returned from the _get_x() in x.html?
Thank you very much for reading.
Kind regards,
Brian

Jinja2 does streaming of template data. The template is evaluated instruction for instruction into a stream of smaller strings that gets concatenated into an actual unicode string by the render() method. However you can also get hold of the stream by calling into generate() instead of render().
With some in-band signalling and postprocessing of the stream one might probably be able to implement something like that, but it's against the way of how Jinja2 was designed so it might break horribly and is totally unsupported.

Related

how to format large numbers using spaces instead fo comma?

I am using django.contrib.humanize intcomma tag to format large numbers like this $18,162,711,641
but what I want spaces instead of comma, like this $18 162 711 641
How can I achieve this? Thank you.
Thanks to Abdul Niyas P M
This is what works for me. I had to put this in app_name/templatetags directory and load it in template using {% load intspace %}
from django import template
register = template.Library()
#register.filter
def intspace(value):
import re
orig = str(value)
new = re.sub(r"^(-?\d+)(\d{3})", r"\g<1> \g<2>", orig)
if orig == new:
return new
else:
return intspace(new)
inside template you can use it like
{{ 18162711641|intspace }}

looping thru a python list and display results in flask

python function returns a python list
python module function
with open(xml_append_back) as fd1:
doc = xmltodict.parse(fd1.read())
codes = []
for p in doc['Des']['Config']:
codes.append(p['#Id'])
codes.append(pl['#name'])
print(codes)
return codes
codes = ['f2ee4681', 'Conf. no: 1', '89282c5b', 'Conf. no: 2', '3e9dd219', 'Conf. no: 3', '773044b9'] # returned from python to flask template result.html
I call this variable in my templates/flask.html like this
flask file
#app.route('/result',methods = ['POST', 'GET'])
def result():
const_ids=run_d.run_de_selected_configs() # this function returns "codes"
return render_template("result.html",result =
constraint_names_from_form,result1=constraint_ids)
result.html file
{% for key,key1 in result1 %}
<tr class="even"><td>{{ key }}</td><td>{{ key1 }}</td></tr>
should be
<tr class="even"><td>f2ee4681</td><td>Conf. no: 1</td></tr>
{% endfor %}
What am I doing wrong
To answer my own question
I used zip utility in my python code as zip is not available in flask
function returncodes()
-------
---------
return zip(codes,codeNames) # in my case
no change in the flask template
#app.route('/result',methods = ['POST', 'GET'])
def result():
const_ids=run_d.run_de_selected_configs() # this function returns "codes"
return render_template("result.html",result =
constraint_names_from_form,result1=constraint_ids)
now in my result.html
{% for keys,keys2 in result1 %}
<tr class="even"><td>{{keys}}</td><td>{{keys2}}</td></tr>
{% endfor %}
Currently your code is packing all the Id and name values into a single flat list. That doesn't work right when you later need to iterate over it, as you want two values per iteration and you're only getting one.
While there are some ways to iterate over pairs from a list (e.g. zip(*[iter(x)]*2)), I would suggest that you just build a list of tuples directly.
Try changing:
codes.append(planet['#Id'])
codes.append(planet['#name'])
To:
codes.append((planet['#Id'], planet['#name']))

How to send django model queryset to template through ajax?

Suppose I have model Abc and Tags which have many to many relation,
options = Abc.objects.all()
tagsset = []
for entry in options:
tags_arr = entry.tags_set.all()
if tags_arr:
tagsset.append(tags_arr)
data = {}
How do I format my queryset both options and tagsset in data?
You can put them in a dictionary, convert them to json, and them return the json_object
data = {}
data['options'] = options
data['tagsset'] = tagsset
json_object = json.dumps(data)
return HttpResponse(json_object)
This above code will send the json object to the calling ajax method
Simple answer:
data = {}
data['options'] = options
data['tagset'] = tagset
# NOTE: since you skip empty tag sets,
# len(tagset) <= len(options)
# so if you are going to match tagsets with options in the
# browser, they may not match length-wise
Although the question asked only about formatting the return parameters, this answer shows a different way to do the same (which is better IMO for cases where there is more data to be packed and sent. This approach also keeps related data together, i.e. options and related tags are binded together.
# payload is the data to be sent to the browser
payload = []
# get all options and associated tags
# this will be a list of tuples, where each tuple will be
# option, list[tags]
for option in Abc.objects.all():
payload.append((
option, # the option
list(option.tags_set.all()), # list of linked tags
))
# return this payload to browser
return HttpResponse(
# good practice to name your data parameters
json.dumps({ 'data': payload, }),
# always set content type -> good practice!
content_type='application/json'
)
# in the browser template, you can do something such as:
{% for option, tags in data %}
{{ option.something }}
{% for tag in tags %}
{{ tag.something }}
{% endfor %}
{% endfor %}

Print all jinja2 variables in a template [duplicate]

If I return a Jinja2 template like so:
return render_response('home.htm', **context)
How do then get a list of the variables in context from within the template?
Technically, because context is not passed as a named dictionary, a little work is required to generate a list of the context variables from inside a template. It is possible though.
Define a Jinja context function to return the jinja2.Context object, which is essentially a dictionary of the global variables/functions
Make that function available in the global namespace; i.e. a jinja2.Environment or jinja2.Template globals dictionary
Optionally, filter objects from the context; for instance, use callable() to skip Jinja's default global helper functions (range, joiner, etc.). This may be done in the context function or the template; wherever it makes the most sense.
Example:
>>> import jinja2
>>>
>>> #jinja2.contextfunction
... def get_context(c):
... return c
...
>>> tmpl = """
... {% for key, value in context().items() %}
... {% if not callable(value) %}
... {{ key }}:{{ value }}
... {% endif %}
... {% endfor %}
... """
>>>
>>> template = jinja2.Template(tmpl)
>>> template.globals['context'] = get_context
>>> template.globals['callable'] = callable
>>>
>>> context = {'a': 1, 'b': 2, 'c': 3}
>>>
>>> print(template.render(**context))
a:1
c:3
b:2
[Alternately, call render_response with ('home.htm', context=context) to make the other solution work.]
Here's how to get #crewbum's answer working from a Flask app:
import jinja2
#jinja2.contextfunction
def get_context(c):
return c
app.jinja_env.globals['context'] = get_context
app.jinja_env.globals['callable'] = callable
I found #Garrett's answer to be a bit more complex than what I wanted. I found that I can easily inspect the context by adding a clone of the context to the context itself:
contextCopy = dict(context)
context['all'] = contextCopy
And then pretty printing that into my Jinja template with <pre>{{ all|pprint }}</pre>.

How to display string which contains django template variables?

Let's say I have an string variable called *magic_string* which value is set to "This product name is {{ product.name }}" and it's avaible at django template. Is it ever possible to parse that variable to show me "This product name is Samsung GT-8500" instead (assuming that name of the product is "GT-8500" and variable {{ product.name }} is avaible at the same template) ?
I was trying to do something like this, but it doesn't work (honestly ? Im not surprised):
{{ magic_string|safe }}
Any ideas/suggestions about my problem ?
Write custom template tag and render that variable as a template.
For example look at how "ssi" tag is written.
On the other hand, can you render this string in your view? Anyway here is an untested version of that tag:
#register.tag
def render_string(parser, token):
bits = token.contents.split()
if len(bits) != 2:
raise TemplateSyntaxError("...")
return RenderStringNode(bits[1])
class RenderStringNode(Node):
def __init__(self, varname):
self.varname = varname
def render(self, context):
var = context.get(self.varname, "")
return Template(var).render(context)
Perhaps I dont understand your question but what about,
from django.template import Context, Template
>>> t = Template("This product name is {{ product.name }}")
>>> c = Context({"product.name": " Samsung GT-8500"})
>>> t.render(c)
Regards.

Categories

Resources