I am passing an object to my Jinja2 template to be used as form values.
If a property doesn't exist, it is printing None as a string, where I would like it to just print an empty string (aka, nothing).
As there are a lot of properties for the object, I wish to avoid string coercion at the Controller level.
My current Jinja code looks like:
value="{{ my_object.my_property }}"
Try:
{{ my_object.my_property if my_object.my_property != None }}
I was able to utilize the following, which is not too long:
{{ my_object.my_property or '' }}
Related
I have the following string in my Django app: "Hello, {{ name }}. I will arrive at {{ time }}". My goal is to come up with a list of variables that is used in that string:
def my_func():
# What should be done here ?
my_str = "Hello, {{ name }}. I will arrive at {{ time }}"
print(my_func(my_str)) # Desired output ["name", "time"]
The desired output here should be ["name", "time"].
Currently, I am going to implement that invoking a regexp, but I feel like there must be some in-built way to achieve that. Any hints ?
You can use jinja2schema.infer to get the list of variables used in template:
import jinja2schema
def my_func(my_str):
return list(jinja2schema.infer(my_str).keys())
my_str = "Hello, {{ name }}. I will arrive at {{ time }}"
variables = my_func(my_str)
variables should look like:
['time', 'name']
I'm trying to construct a template to support configuration lines for a specific console platform.
The running-configuration is in this following format:
Example 1:
/settings/network_connections/ETH0/ipv4_mode=static
/settings/network_connections/ETH0/pv4_address=10.0.0.10
/settings/network_connections/ETH0/ipv4_bitmask=24
/settings/network_connections/ETH0/ipv4_gateway=10.0.0.1
If you want to configure the following above lines, it's prepended by the word "set" like this:
Example 2:
set /settings/network_connections/ETH0/ipv4_mode=static
set /settings/network_connections/ETH0/pv4_address=10.0.0.10
set /settings/network_connections/ETH0/ipv4_bitmask=24
set /settings/network_connections/ETH0/ipv4_gateway=10.0.0.1
In my jinja2 template, I have something like this:
Example 3
{{ set| remediation }} /settings/network_connections/ETH0/ipv4_mode=static
{{ set| remediation }} /settings/network_connections/ETH0/pv4_address=10.0.0.10
{{ set| remediation }} /settings/network_connections/ETH0/ipv4_bitmask=24
{{ set| remediation }} /settings/network_connections/ETH0/ipv4_gateway=10.0.0.1
I want to be able to render the template and be able to output what I have in Example 2 (with 'set') as well as the ability to output as Example 1 (without 'set') using a boolean variable (with_remediation). If True, include 'set' - else exclude 'set'. In Example 3, 'remediation' being a embedded custom filter.
import jinja2
loader = jinja2.FileSystemLoader('/tmp')
env = jinja2.Environment(autoescape=True, loader=loader)
def remediation(input,with_remediation):
"""Custom filter"""
if(with_remediation):
return input
else:
return ""
env.filters['remediation'] = remediation
temp = env.get_template('template.jinja2')
temp.render(set="set")
But I'm unsure of how to pass the variable 'with_remediation' into the function remediate.
I have tried following the example Embed custom filter definition into jinja2 template? provided in the answer, but not sure if it will help in what I'm trying to achieve.
Also, how can I code it so 'set' can be any 'string' I want it to be? Must I include every string I want to use in the temp.render(set="set") line? For example; temp.render(set="set", delete="delete",rename="rename") or if there a more efficient way of tackling this?
I was able to get resolve it by doing this; output 'set' by setting with_remediation to True; exlude output 'set' by setting with_remediation to False:
import jinja2
loader = jinja2.FileSystemLoader('/tmp')
env = jinja2.Environment(autoescape=True, loader=loader)
with_remediation = True
def remediation(input):
"""Custom filter"""
if(with_remediation):
return input
else:
return ''
env.filters['remediation'] = remediation
temp = env.get_template('template.jinja2')
temp.render(set='set')
Template:
{{ set | remediation }}/settings/network_connections/ETH0/ipv4_mode=static
{{ set | remediation }}/settings/network_connections/ETH0/pv4_address=10.0.0.10
{{ set | remediation }}/settings/network_connections/ETH0/ipv4_bitmask=24
{{ set | remediation }}/settings/network_connections/ETH0/ipv4_gateway=10.0.0.1
I'm using Jinja2 to create email notification messages, mostly error notifications.
Sometimes I get invalid or incomplete data about the error details. After all, it is an error condition when this happens. E.g. a dict item might be missing or value type could be incorrect. In such case the rendering fails, but the error notification has to be sent.
I can do following in the exception handler:
To send the template as-is. At least the type of error event will be known to the recipient, but important data will be missing
To send a backup template with just raw data. That would mean to maintain two versions of each message.
What I really want is to render the template on a "best-effort" basis skipping all errors. Is there a way to do that in Jinja2?
Use defaults everywhere it's necessary. Also handles undefined variables:
default(value, default_value=u'', boolean=False):
If the value is undefined it will return the passed default value, otherwise the value of the variable:
{{ my_variable|default('my_variable is not defined') }}
This will output the value of my_variable if the variable was defined, otherwise 'my_variable is not defined'. If you want to use default with variables that evaluate to false you have to set the second parameter to true:
{{ ''|default('the string was empty', true) }}
Or you could use none if you're creating more complex structures based on the data:
{% if p is not none %}
{{ p.User['first_name'] }}
{% else %}
NONE
{%endif %}
In django's template language, if a variable called a.x, then django will try in this order to get the right value:
a['x'], dictionary lookup
a.x or a.x(), attribute or method lookup
a[x], list indexing
The result is: any variable that support var['str'] becomes a time bomb. Imagine a is a dict, with a key 'items' whoes value is 'hello world', then a.items will result in 'hello world', not ditc.items(). Here is the code:
from django.template import Engine, Context
template = """
{% for k, v in a.items %}
{{ k }} = {{ v }}
{% endfor %}
"""
e = Engine()
t = e.from_string(template)
a = {'items': 'hello world'}
print(t.render(Context({'a': a})))
While I expect items = hello world as the output, the real output is:
h =
e =
l =
...
For a dict variable, if it has a key with the same name of a dict method, then you will never be able to call that method in the template. It becomes a time bomb because you never know if in the future you will add a key called 'items' into a dict variable.
Such risk is not only for dict but any type which accept var['key'].
So my question is, why the order is designed like this, and how to make it safe.
There is a simple workaround (besides using a better name for your keys):
from django.template import Engine, Context
template = """
{% for k, v in items_func %}
{{ k }} = {{ v }}
{% endfor %}
"""
e = Engine()
t = e.from_string(template)
a = {'items': 'hello world'}
print(t.render(Context({'a': a, 'items_func': a.items})))
>> items = hello world
why the order is designed like this
It's designed like this because the template syntax is supposed to be simple and doing so encourages you to put more logic into the view rather than in the template.
How to make it safe?
Give your keys a descriptive name
"items" doesn't do anything to actually describe what should be inside of the key, so not only will it give django a headache, it will give any other developer that has to debug an issue with this dictionary a headache too.
By using the . for three different lookups, there's always the potential for a problem like this. If the order was changed to try a.x first, then it would cause problems for other users that wanted to access a['x'].
I don't think there's a general way to protect against this behaviour. Once you're burned by it once, you'll remember not to put an items key in your dictionaries in future.
As a last resort, you could switch to Jinja, which supports subscript syntax, {{ foo['bar'] }}. That means that {{ foo.bar }} checks for the attribute foo.bar first. See the Jinja docs for more information.
In my web app, the user can make blog posts. When I display the blog post, newlines aren't shown because I didn't replace the new lines with <br> tags. The problem is that I've turned autoescaping on in Jinja, so <br> tags are escaped. I don't want to temporarily disable autoescaping, I want to specifically allow <br> tags. How would I do this?
I have another answer that I think is the best. Initially I was just displaying my variable post.content as-is, and the newlines weren't being preserved. None of the solutions here worked (well), and my pre solution was just a quick fix and had major issues. This is the real solution:
{% for line in post.content.splitlines() %}
{{line}}<br>
{% endfor %}
You can use the |safe filter, or use the autoescape blocks:
{% autoescape false %}
{{ content goes here }}
{% autoescape %}
You could also set autoescaping in the environment to False.
In your model object, add a function like this:
class Post(db.Model):
# ...
def html_content(self):
# Escape, then convert newlines to br tags, then wrap with Markup object
# so that the <br> tags don't get escaped.
def escape(s):
# unicode() forces the conversion to happen immediately,
# instead of at substitution time (else <br> would get escaped too)
return unicode(jinja2.escape(s))
return jinja2.Markup(escape(self.content).replace('\n', '<br>'))
Then in your template, just call that:
<p>{{ post.html_content() }}</p>
You can create a jinja2 filter:
import re
from jinja2 import evalcontextfilter, Markup, escape
_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
#evalcontextfilter
def nl2br(eval_ctx, value):
result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', '<br>\n')
for p in _paragraph_re.split(escape(value)))
if eval_ctx.autoescape:
result = Markup(result)
return result
You need to add the filter to your jinja2 Environment before you can use it:
JINJA2_ENV.filters['nl2br'] = jinja2_filters.nl2br
In your template you can use that filter:
{{post.content|nl2br}}
Here's a filter wrote by myself:
import jinja2
#jinja2.evalcontextfilter
def nl2br(eval_ctx, value):
result = jinja2.escape(value).unescape().replace('\n', '<br>')
if eval_ctx.autoescape:
result = jinja2.Markup(result)
return result
And add the filter to the jinja2.Environment() by calling:
jinja_env.filters['nl2br'] = nl2br
Note that i have autoescape on by default, so I don't check it in this function, but this is what I'm using
def nl2br(value):
split = value.split('\n')
return jinja2.Markup('<br>').join(split)
then of course,
jinja_env.filters['nl2br'] = nl2br
The solution was to put <pre></pre> tags around the area where I had the content.
The easiest way to do this is to escape the field yourself, then add line breaks. When you pass it in in jinja, mark it as safe so it's not autoescaped.