jinja2: convert string to dict object? - python

I have a json string stored in the database and when this is pulled out and shown on the template it is a string. I want to convert this into a dict object so I can access the contents directly.
string = "{'a':1, 'b':3}"
{{ obj }} ---> string "{'a':1, 'b':3}"
{{ obj.b }} ---> I want to access it like an object.

obj_in_string = "{'a':1, 'b':3}"
{{ obj_in_string | tojson }}
Ref: https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.tojson

Write the following code in a temporary file and write a function back-end side:
{{ obj.b | str_to_obj }}
Put this code in your jinja filter file:
for convert str to dic object
def str_to_obj(str):
return eval(str)
And write this code in your admin file:
app.jinja_env.filters['str_to_obj'] = jinja_filters.str_to_obj

Related

Flask how to pass list of list to javascript [duplicate]

I am trying to pass data as JSON from a Flask route to a Jinja template rendering JavaScript. I want to iterate over the data using JavaScript. The browser shows SyntaxError: Unexpected token '&'. Expected a property name. when JSON.parse is called on the rendered data. How do I use rendered JSON data in JavaScript?
var obj = JSON.parse({{ data }})
for (i in obj){
document.write(obj[i].text + "<br />");
}
def get_nodes(node):
d = {}
if node == "Root":
d["text"] = node
else:
d["text"] = node.name
getchildren = get_children(node)
if getchildren:
d["nodes"] = [get_nodes(child) for child in getchildren]
return d
tree = get_nodes("Root")
return render_template("folder.html", data=tree)
If I just put {{ data }} in the HTML part, what I see looks correct.
{'text': 'Root', 'nodes': [{'text': u'Prosjekt3'}, {'text': u'Prosjekt4', 'nodes': [{'text': u'mappe8'}]}]}
Flask's Jinja environment automatically escapes data rendered in HTML templates. This is to avoid security issues if the dev tries to render untrusted user input.
Since you are passing a Python object to be treated as JSON, Flask provides the tojson filter which automatically dumps the data to JSON and marks it safe.
return render_template('tree.html', tree=tree)
var tree = {{ tree|tojson }};
When you just look at the data rendered in HTML, it looks correct because the browser displays the escaped symbols as the real symbols (although in this case you're seeing the string representation of a Python dict, not JSON, so there's still some issues like u markers).
Previous versions of Flask didn't mark the dumped data safe, so you might come across examples like {{ tree|tojson|safe }}, which isn't required anymore.
If you're not rendering JSON (or you already dumped the JSON to a string), you can tell Jinja that data is safe to render without escaping by using the safe filter.
# already dumped to json, so tojson would double-encode it
return render_template('tree.html', tree=json.dumps(tree))
var tree = {{ tree|safe }};
You can also wrap the string in Markup before rendering it, it's equivalent to the safe filter.
# already dumped and marked safe
return render_template('tree.html', tree=Markup(json.dumps(tree)))
var tree = {{ tree }};
If you're not passing this data to JavaScript, but using it in Jinja instead, you don't need JSON. Pass the actual Python data, don't call tojson on it, and use it as you would any other data in the template.
return render_template('tree.html', tree=tree)
{% for item in tree %}
<li>{{ item }}</li>
{% endfor %}
I could archive it using the following code sample.
<script>
console.log(JSON.parse({{json|safe}}))
</script>
The problem is that your server returns not JSON, but rendered HTML, which escapes some of the symbols with & notation.
Instead of using
return render_template("folder.html", data=tree)
try
return flask.jsonify(**tree)

Passing in variable template inputs for render_template() (Flask) [duplicate]

I am trying to pass data as JSON from a Flask route to a Jinja template rendering JavaScript. I want to iterate over the data using JavaScript. The browser shows SyntaxError: Unexpected token '&'. Expected a property name. when JSON.parse is called on the rendered data. How do I use rendered JSON data in JavaScript?
var obj = JSON.parse({{ data }})
for (i in obj){
document.write(obj[i].text + "<br />");
}
def get_nodes(node):
d = {}
if node == "Root":
d["text"] = node
else:
d["text"] = node.name
getchildren = get_children(node)
if getchildren:
d["nodes"] = [get_nodes(child) for child in getchildren]
return d
tree = get_nodes("Root")
return render_template("folder.html", data=tree)
If I just put {{ data }} in the HTML part, what I see looks correct.
{'text': 'Root', 'nodes': [{'text': u'Prosjekt3'}, {'text': u'Prosjekt4', 'nodes': [{'text': u'mappe8'}]}]}
Flask's Jinja environment automatically escapes data rendered in HTML templates. This is to avoid security issues if the dev tries to render untrusted user input.
Since you are passing a Python object to be treated as JSON, Flask provides the tojson filter which automatically dumps the data to JSON and marks it safe.
return render_template('tree.html', tree=tree)
var tree = {{ tree|tojson }};
When you just look at the data rendered in HTML, it looks correct because the browser displays the escaped symbols as the real symbols (although in this case you're seeing the string representation of a Python dict, not JSON, so there's still some issues like u markers).
Previous versions of Flask didn't mark the dumped data safe, so you might come across examples like {{ tree|tojson|safe }}, which isn't required anymore.
If you're not rendering JSON (or you already dumped the JSON to a string), you can tell Jinja that data is safe to render without escaping by using the safe filter.
# already dumped to json, so tojson would double-encode it
return render_template('tree.html', tree=json.dumps(tree))
var tree = {{ tree|safe }};
You can also wrap the string in Markup before rendering it, it's equivalent to the safe filter.
# already dumped and marked safe
return render_template('tree.html', tree=Markup(json.dumps(tree)))
var tree = {{ tree }};
If you're not passing this data to JavaScript, but using it in Jinja instead, you don't need JSON. Pass the actual Python data, don't call tojson on it, and use it as you would any other data in the template.
return render_template('tree.html', tree=tree)
{% for item in tree %}
<li>{{ item }}</li>
{% endfor %}
I could archive it using the following code sample.
<script>
console.log(JSON.parse({{json|safe}}))
</script>
The problem is that your server returns not JSON, but rendered HTML, which escapes some of the symbols with & notation.
Instead of using
return render_template("folder.html", data=tree)
try
return flask.jsonify(**tree)

Issue in storing Mysql Data From Flask to jinja (HTML) variable as json [duplicate]

I am trying to pass data as JSON from a Flask route to a Jinja template rendering JavaScript. I want to iterate over the data using JavaScript. The browser shows SyntaxError: Unexpected token '&'. Expected a property name. when JSON.parse is called on the rendered data. How do I use rendered JSON data in JavaScript?
var obj = JSON.parse({{ data }})
for (i in obj){
document.write(obj[i].text + "<br />");
}
def get_nodes(node):
d = {}
if node == "Root":
d["text"] = node
else:
d["text"] = node.name
getchildren = get_children(node)
if getchildren:
d["nodes"] = [get_nodes(child) for child in getchildren]
return d
tree = get_nodes("Root")
return render_template("folder.html", data=tree)
If I just put {{ data }} in the HTML part, what I see looks correct.
{'text': 'Root', 'nodes': [{'text': u'Prosjekt3'}, {'text': u'Prosjekt4', 'nodes': [{'text': u'mappe8'}]}]}
Flask's Jinja environment automatically escapes data rendered in HTML templates. This is to avoid security issues if the dev tries to render untrusted user input.
Since you are passing a Python object to be treated as JSON, Flask provides the tojson filter which automatically dumps the data to JSON and marks it safe.
return render_template('tree.html', tree=tree)
var tree = {{ tree|tojson }};
When you just look at the data rendered in HTML, it looks correct because the browser displays the escaped symbols as the real symbols (although in this case you're seeing the string representation of a Python dict, not JSON, so there's still some issues like u markers).
Previous versions of Flask didn't mark the dumped data safe, so you might come across examples like {{ tree|tojson|safe }}, which isn't required anymore.
If you're not rendering JSON (or you already dumped the JSON to a string), you can tell Jinja that data is safe to render without escaping by using the safe filter.
# already dumped to json, so tojson would double-encode it
return render_template('tree.html', tree=json.dumps(tree))
var tree = {{ tree|safe }};
You can also wrap the string in Markup before rendering it, it's equivalent to the safe filter.
# already dumped and marked safe
return render_template('tree.html', tree=Markup(json.dumps(tree)))
var tree = {{ tree }};
If you're not passing this data to JavaScript, but using it in Jinja instead, you don't need JSON. Pass the actual Python data, don't call tojson on it, and use it as you would any other data in the template.
return render_template('tree.html', tree=tree)
{% for item in tree %}
<li>{{ item }}</li>
{% endfor %}
I could archive it using the following code sample.
<script>
console.log(JSON.parse({{json|safe}}))
</script>
The problem is that your server returns not JSON, but rendered HTML, which escapes some of the symbols with & notation.
Instead of using
return render_template("folder.html", data=tree)
try
return flask.jsonify(**tree)

Jinja2: format + join the items of a list

play_hosts is a list of all machines for a play. I want to take these and use something like format() to rewrite them like rabbitmq#%s and then join them together with something like join(). So:
{{ play_hosts|format(???)|join(', ') }}
All the examples of format use piping where the input is the format string and not a list. Is there a way to use these (or something else) to accomplish what I want? The output should looks something like:
['rabbitmq#server1', 'rabbitmq#server2', rabbitmq#server3', ...]
The jinja2 doc describes format like this:
format(value, *args, **kwargs)
Apply python string formatting on an object:
{{ "%s - %s"|format("Hello?", "Foo!") }}
-> Hello? - Foo!
So it gives three kinds of input but doesn't describe those inputs in the example, which shows one in the pipe and the other two passed in via args. Is there a keyword arg to specify the string that's piped? Please help, python monks!
In ansible you can use regex_replace filter:
{{ play_hosts | map('regex_replace', '^(.*)$', 'rabbitmq#\\1') | list }}
I believe another way would be using the joiner global function, as you can read in http://jinja.pocoo.org/docs/2.9/templates/#list-of-global-functions:
A joiner is passed a string and will return that string every time it’s called, except the first time (in which case it returns an empty string). You can use this to join things
So your code would be something like:
[
{% set comma = joiner(",") %}
{% for host in play_hosts %}
{{ comma() }}
{{ "rabbitmq#%s"|format(host) }}
{% endfor %}
]
You can create custom filter
# /usr/share/ansible/plugins/filter/format_list.py (check filter_plugins path in ansible.cfg)
def format_list(list_, pattern):
return [pattern % s for s in list_]
class FilterModule(object):
def filters(self):
return {
'format_list': format_list,
}
and use it
{{ play_hosts | format_list('rabbitmq#%s') }}
You could simply join not only by , but also add the prefix together with it. Now that's not very pythonic or sophisticated but a very simple working solution:
[rabbitmq#{{ play_hosts | join(', rabbitmq#') }}]
If you want to format a string, through a list.
l: list = ["world", "stackoverflow"]
"Hello %s and %s"|format(*l)

jinja2.exceptions.UndefinedError with Python dict

I'm trying to render a jinja2 template from a python dict created from a CSV file, but jinja2 is taking exception to the dict and I don't understand why.
This is the template:
{% for key, value in hostname.iteritems() %}
interface {{ key }}
description {{ value }}
{% endfor %}
and this is the python code:
import csv
from pprint import pprint
import os
import jinja2
CSVDATA_FILENAME = 'port_descriptions.csv'
TEMPLATE_FILENAME = 'cisco.j2'
hostnames = []
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.getcwd()),
trim_blocks=True, lstrip_blocks=True)
template = env.get_template(TEMPLATE_FILENAME)
for row in csv.DictReader(open(CSVDATA_FILENAME)):
if row['hostname'] not in hostnames:
hostnames.append(row['hostname'])
for hostname in hostnames:
x = hostname
hostname = {}
for row in csv.DictReader(open(CSVDATA_FILENAME)):
if x == row['hostname']:
hostname[row['port']] = row['des']
pprint(hostname)
print template.render(hostname)
At the penultimate line (pprint(hostname)) the code will print the hostname dictionaries as desired so I know they are there, but jinja2 won't render them. Am I missing something obvious here, in the template maybe?
As far as I can see my code follows this - http://keepingitclassless.net/2014/03/network-config-templates-jinja2/ pretty closely, but I must be overlooking something?
Looking at this example in the docs, you probably need to pass your dictionary as a named keyword argument, like this: print template.render(hostname=hostname)
Note that only the part on the left of the equals sign is relevant to the template; you could just do template.render(hostname={'testy': 'testy'}) and the same template would keep working.

Categories