After rendering a Jinja2 template, can I get the set values? - jinja2

I want to render a Jinja2 template, and after the rendering, read the values that were set by it:
import jinja2
template = jinja2.Template("""
{% set number = 42 %}
Hello {{name}} {{number}}!
""")
vars = {"name": "Ned"}
print(template.render(vars).strip())
print(vars)
This prints:
Hello Ned 42!
{'name': 'Ned'}
Is there something I can do after template.render that would give me the value of number? Also, I need to do it without knowing the name number beforehand. I want to discover whatever values have been set in the template.

Here's a way to do it without using functions that are documented, but say not to use them:
import jinja2
template = jinja2.Template("""
{% set number = 52 %}
{% set choice = "apple" %}
Hello {{name}} {{number}}!
{% if number > 50 %}
More than 40!
{% set also = 'foo' %}
{% endif %}
""")
ctx = template.new_context(vars = {"name": "Ned"})
template.render(ctx)
mod = template.module
template_vars = {n:getattr(mod, n) for n in dir(mod) if not n.startswith("_")}
print(template_vars)
This prints:
{'also': 'foo', 'choice': 'apple', 'number': 52}

I guess, this is a kinda hack but it seems you can do something with Context from jinja2.runtime like
$ cat show_vars.py
import jinja2
template = jinja2.Template("""
{% set number = 42 %}
Hello {{name}} {{number}}!
""")
vars = {"name": "Ned"}
print(template.render(vars).strip())
print(vars)
from jinja2 import Environment
from jinja2.runtime import Context
env = Environment()
ctx = Context(env, vars, '', template.blocks)
list(template.root_render_func(ctx))
print(ctx.vars)
print(ctx.get_all())
$ python show_vars.py
Hello Ned 42!
{'name': 'Ned'}
{'number': 42}
{'name': 'Ned', 'number': 42}
As #nedbat pointed out, one can also achieve the same with
import jinja2
template = jinja2.Template("""
{% set number = 42 %}
Hello {{name}} {{number}}!
{% if number > 50 %}
More than 40!
{% set also = 'foo' %}
{% endif %}
""")
ctx = template.new_context(vars = {"name": "Ned"})
list(template.root_render_func(ctx))
print("get_all:", ctx.get_all())
print("exported_vars:", ctx.exported_vars)
print("vars:", ctx.vars)

With the context provided:
import jinja2
import re
template = jinja2.Template("""
{% set number = 42 %}
Hello {{name}} {{number}}!
""")
vars = {"name": "Ned"}
rendered = template.render(vars).strip()
print(vars)
print(rendered)
changed_var_value = re.findall(r'Hello[\s\S]*\s{1}(.*)!', rendered)
print(f'var after the greeting: {changed_var_value}')
output:
{'name': 'Ned'}
Hello Ned 42!
var after the greeting: ['42']

Related

how to add html tag in django dictonary

I have added a strong tag in Django dict but not working
mylist = [{'hq': 'abc', 'inst': 'Total', 'trade': 'Fitter'}]
I added like this but it rendered the same not highlights
mylist = [{'hq': 'abc', 'inst': '<strong>Total</strong>', 'trade': 'Fitter'}]
Django autoescape all strings when render its in templates for security reasons.
You can disable it when render with the safe template filter or autoescape template tag:
{{ myvar|safe }}
{% autoescape off %}
{{ myvar }}
{% endautoescape %}
Or mark previously the variable as a safe string.
from django.utils.safestring import mark_safe
mylist = [{
'hq': 'abc',
'inst': mark_safe('<strong>Total</strong>'),
'trade': 'Fitter'
}]

Django how to use array in template within a forloop

Im trying to use an array inside an html file within a loop
views
def noticia(request, noticia_id):
noticia = get_object_or_404(Noticia, pk=noticia_id)
user_like = False
likes = []
dislikes = []
x = 0
for comentario in noticia.comentario_set.all().order_by('-pub_data'):
likes.append(0)
dislikes.append(0)
for comentario in noticia.comentario_set.all():
for like in comentario.like_set.all():
if like.like:
likes[x] += 1
elif like.dislike:
dislikes[x] += 1
if like.user == request.user:
user_like = True
return render(request, 'Bola/Noticia.html', {'noticia': noticia, 'user_like': user_like, 'likes': likes,
'dislikes': dislikes})
Html
{% for comentario in noticia.comentario_set.all|dictsortreversed:"pub_data"%}
{% for like in comentario.like_set.all %}
<p>{{ likes.forloopcounter0 }} {{ dislikes.forloopcounter0 }}</p>
Any idea how to make this work?
Probably there is a better way of doing it by using Conditional Aggregation.
To use that, first lets update the ForeignKey relation between Comentario and Like using related_name:
class Like(models.Model):
comment = models.ForeignKey(Comentario, related_name='likes', on_delete=models.CASCADE)
Now use that related name in queryset:
from django.db.models import Count, Case, When, IntegerField
...
comments = noticia.comentario_set.all().annotate(
like_count=Count(Case(
When(likes__like=True, then=1),
output_field=IntegerField(),
)).annotate(dislike_count=Count(Case(
When(likes__like=False, then=1),
output_field=IntegerField(),
))
)
return render(request, 'Bola/Noticia.html', {'comments': comments})
Then use it in template:
{% for comentario in comments %}
{{ comentario.like_count }}
{{ comentario.dislike_count }}
{% endfor %}

Block content for passing variables from python to HTML

I'm trying to send some data from my python code to the HTML script.
This is my python code. I use lists to send the data.
def extractMetaData(request):
pdfDir = "C:/PythonPrograms/pdf/"
if pdfDir == "": pdfDir = os.getcwd() + "\\"
pdf_title = []
pdf_author = []
pdf_creationdate = []
pdf_creator = []
pdf_Keywords = []
pdf_producer = []
for pdf in os.listdir(pdfDir):
fileExtension = pdf.split(".")[-1]
if fileExtension == "pdf":
pdfFilename = pdfDir + pdf
pdf_toread = PdfFileReader(open(pdfFilename, "rb"))
pdf_title.append(pdf_toread.getDocumentInfo().title)
pdf_author.append(pdf_toread.getDocumentInfo().author)
pdf_creationdate.append(pdf_toread.getDocumentInfo()['/CreationDate'])
pdf_creator.append(pdf_toread.getDocumentInfo()['/Creator'])
pdf_Keywords.append(pdf_toread.getDocumentInfo()['/Keywords'])
pdf_producer.append(pdf_toread.getDocumentInfo().producer)
return render(request,'personal/extract.html',{'content':[str(pdf_title),str(pdf_author),str(pdf_creationdate), str(pdf_creator),str(pdf_Keywords), str(pdf_producer)]})
In HTML I use the following code.
{% block content %}
{% for c in content%}
<p>{{c}}</p>
{% endfor%}
{% endblock %}
But this prints all the items of the first list and then all items of second list and so on.. I want it to print the first item of the first list, first item of the second list and so on. Then start with second item of every list.. how can do this in jinja?
I would do it like this :
def extractMetaData(request):
pdfDir = "C:/PythonPrograms/pdf/"
if pdfDir == "":
pdfDir = os.getcwd() + "\\"
pdf_files = []
for pdf in os.listdir(pdfDir):
pdf_file = {} # Create a dictionnary to store values
fileExtension = pdf.split(".")[-1]
if fileExtension == "pdf":
pdfFilename = pdfDir + pdf
pdf_toread = PdfFileReader(open(pdfFilename, "rb"))
pdf_file['title'] = pdf_toread.getDocumentInfo().title
pdf_file['author'] = pdf_toread.getDocumentInfo().author
pdf_file['creationdate'] = pdf_toread.getDocumentInfo()['/CreationDate']
pdf_file['creator'] = pdf_toread.getDocumentInfo()['/Creator']
pdf_file['keywords'] = pdf_toread.getDocumentInfo()['/Keywords']
pdf_file['producer'] = pdf_toread.getDocumentInfo().producer
pdf_files.append(pdf_file)
return render(request,'personal/extract.html', {'pdf_files': pdf_files})
And then in the template :
{% block content %}
{% for pdf in pdf_files %}
{% for item_name, item in pdf_file.items %}
<p>{{ item_name }} : {{ item }}</p>
{% endfor %}
{% endfor%}
<hr>
{% endblock %}
I didn't test it so it might have some errors..

Setting out a list of variables in Salt and then importing into another file

Is it possible to set out a list of salt variables such as:
{% set foo = '1234' %}
{% set bar = '10.1.1.2' %}
{% set environment = salt['grains.get']('env') %}
and then import them into separate .sls file and use them like so:
foo_value = {{ foo }} # sets foo to '1234'
bar_value = {{ bar }} # sets bar to '10.1.1.2'
etc...
The best fit should be the import feature.
You can store the file with variables as described in question and then import them like:
{% from 'yourfile.jinja' import foo with context %}
{% from 'yourfile.jinja' import bar with context %}
{% from 'yourfile.jinja' import environment with context %}
Or alternatively you can add all of them to an array:
{% set vars = {
'foo': '1234',
'bar': '10.1.1.2',
'environment': salt['grains.get']('env'),
}
%}
And then import it at once:
{% from 'yourfile.jinja' import vars with context %}
Best practices on using variables (and import) are described at Salt Best Practices page.

Jekyll print custom JSON string from data

Let's say I have an array of hashes like so;
site.foo = [
{
"foo": "bar",
"baz": ["a", "b", "c"]
},
{
"foo": "baz",
"baz": ["x", "y"]
},
...
]
I want to get all values (bar, a, b, c, baz, x, y) in an array and output it as JSON in a javascript variable in a template.
How would I do this?
I assume that you know how your datas are organized.
{% assign splitMark = "###" %}
{% assign arr = "" %}
{% for el in site.foo %}
{% assign arrSize = arr | size %}
<!-- if we already have something in our string
add the splitMark at the end -->
{% if arrSize > 0 %}
{% capture arr %}{{arr}}{{splitMark}}{%endcapture%}
{% endif %}
<!-- array + foo.foo value + splitMark -->
{% capture arr %}{{arr}}{{el.foo}}{{splitMark}}{%endcapture%}
<!-- transform an array to a string
with elements delimited by splitMark -->
{% assign bazStr = el.baz | join: splitMark %}
<!-- array + transformed array -->
{% capture arr %}{{arr}}{{bazStr}}{%endcapture%}
{% endfor %}
<!-- transform our string to an array splitting at the splitMark -->
{% assign arr = arr | split: splitMark %}
<script>
var my_var = {{ arr | jsonify }};
</script>
If you don't know your exact data structure, eg:
-
foo: "bar"
baz: ["a", "b", "c", 10]
-
foo: baz
baz: ["x", "y"]
any: other
key: ['toto','titi']
You have to go the plugin way. Create a file _plugins/flatten.rb containing :
module Jekyll
module FlattenArray
def flatten( datas )
datas.flat_map(&:values).flatten
end
end
end
Liquid::Template.register_filter(Jekyll::FlattenArray)
You can now flatten your datas like this :
var my_var = {{ site.foo | flatten | jsonify }};