Check if key exists in a Python dict in Jinja2 templates - python

I have a python dictionary:
settings = {
"foo" : "baz",
"hello" : "world"
}
This variable settings is then available in the Jinja2 template.
I want to check if a key myProperty exists in the settings dict within my template, and if so take some action:
{% if settings.hasKey(myProperty) %}
takeSomeAction();
{% endif %}
What is the equivalent of hasKey that I can use?

Like Mihai and karelv have noted, this works:
{% if 'blabla' in item %}
...
{% endif %}
I get a 'dict object' has no attribute 'blabla' if I use {% if item.blabla %} and item does not contain a blabla key

You can test for key definition this way:
{% if settings.property is defined %}
#...
{% endif %}

This works fine doesn't work in cases involving dictionaries. In those cases, please see the answer by tshalif.
Otherwise, with SaltStack (for example), you will get this error:
Unable to manage file: Jinja variable 'dict object' has no attribute '[attributeName]'
if you use this approach:
{% if settings.myProperty %}
note:
Will also skip, if settings.myProperty exists, but is evaluated as False (e.g. settings.myProperty = 0).

Actually, in the style of Python, if you do a simple if statement, it will work:
{% if settings.foo %}
Setting Foo: {{ settings.foo }}
{% endif %}
{% if settings.bar %}
Setting Bar: {{ settings.bar }}
{% endif %}
{% if settings.hello %}
Setting Hello: {{ settings.hello }}
{% endif %}
Output:
Setting Foo: baz
Setting Hello: world
Cheers!

Related

Python HTML template ternary operator

I've come across with different python ternary operators such as:
a if b else 0
but it didn't work when I tried to include it inside Django HTML template
{% a if b else 0 %}
Here is the exact code that I'll be using but won't work:
{% 'error' if form.title.errors else '' %}
or
{% form.title.errors ? 'error' : '' %}
I don't like to do the usual
{% if form.title.errors %}error{% endif %}
because it looks bulky in my opinion especially if I add an else statement.
Any suggestions? or should I just stick with it?
You can get more or less the behavior you want by using the yesno filter (documentation):
{{ form.title.errors|yesno:"errors," }}
In Django, the templatetags just support {% if %}{% else %}{% endif %}. If you don't want to use:
{% if form.title.errors %}error{% endif %}
You can try:
{{ error|default:'' }}
And the test in django template is bellow:
...from django.template import Context, Template
...from django.conf import settings
...settings.configure()
...t = Template("My name is: {{ name|default:'anonymous' }} ")
If default:
...t.render(Context({}))
it will return:
'My name is anonymous '
If has name:
...t.render(Context({"name": "John"}))
result:
'My name is John '
Read more: https://docs.djangoproject.com/en/1.9/ref/templates/

How to dynamically add items to jinja variable in Flask?

I'm building a website using Flask with its jinja2 templating engine and I'm dynamically building the menu (as described here):
{%
set navigation_bar = [
('/', 'index', 'Home'),
('/aboutus/', 'aboutus', 'About Us'),
('/faq/', 'faq', 'FAQ')
]
%}
{% set active_page = active_page|default('index') -%}
<ul>
{% for href, id, title in navigation_bar %}
<li{% if id == active_page %} class="active"{% endif %}>
{{ title|e }}
</li>
{% endfor %}
</ul>
Now if a user is logged in I want to show some additional things. So at runtime I want to add items to the navigation_bar variable. I tried something like this:
{% if g.user.is_authenticated() %}
{% navigation_bar.append(('/someotherpage', 'someotherpage', 'SomeOtherPage')) -%}
{% endif %}
But unfortunately this results in the following error: TemplateSyntaxError: Encountered unknown tag 'navigation_bar'. Jinja was looking for the following tags: 'endblock'. The innermost block that needs to be closed is 'block'.
So: does anybody know how I can add additional items to a jinja2 variable at runtime? All tips are welcome!
[bonus question]
I was also wondering, what does the - do at the end of {% set active_page = active_page|default('index') -%}?
The error occurs because Jinja can't identify block. Each Jinja block should start from block name. do block from do extension meets your needs. To use it you should add
do extension to jinja extensions. You can do this like so:
app.jinja_env.add_extension('jinja2.ext.do')
And then you can use do extension. Your example should looks like this:
{% if g.user.is_authenticated() %}
{% do navigation_bar.append(('/someotherpage', 'someotherpage', 'SomeOtherPage')) %}
{% endif %}
Here's another simple example.
You will find answer to your bonus question here. In short - removes whitespaces from start or end of block (this depends on where it is located).
To complete the answer of Slava Bacherikov, if you don't have the Jinja "do extension", you can use the tag set:
{% if g.user.is_authenticated() %}
{# use a dummy variable name, we juste need the side-effect of method call #}
{% set _z = navigation_bar.append(('/someotherpage', 'someotherpage', 'SomeOtherPage')) %}
{% endif %}

Use `With` Tag to invert a boolean in Django Template?

I want to pass a value to an include tag that is the OPPOSITE of a variable passed in.
This is what I tried (basically):
{% with s_options as not disp %}
{% include "e.html" with show_options=s_options only %}
{% endwith %}
Is there any way to do what I want?
Not sure if this is the best solution, but I just made a new filter:
from django import template
register = template.Library()
#register.filter(name="not_value")
def not_value(true_value):
return not true_value
And then did:
{% load not_value %}
{% with s_options=disp|not_value %} {# WILL NOT WORK WITH "as" #}
{% include "e.html" with show_options=s_options only %}
{% endwith %}
Note that, possibly, this might work as well (though I have not tried):
{% include "e.html" with show_options=s_options|not_value only %}
This is a bit of a hack using the built in filter yesno, but it works:
{% with reversed_value=original_value|yesno:',True' %}
{# Do something #}
{% endwith %}
Note the comma before 'True'. The way it works is that yesno will return an empty string if the original value is True, which will evaluate as False if you're doing any boolean operations on it.

flask and wtf - how can I direct tag attributes for fields?

After fiddling with wtforms, fields use widgets to actually render them to html. I wrote some custom field/widget to draw html in a way that I'd more like to. But here's a question:
suppose I want to render them with pre-defined css class or give actual details myself.
How can I achieve this? and on what phase of handling requests(at Form class declaration? or when setting attributes to give Form some Fields? or when I'm actually calling them in jinja2 templates) I should do that?
I use a Jinja macro something like this:
{% macro field_with_errors(field) %}
{% set css_class=kwargs.pop('class', '') %}
{% if field.type in ('DateField', 'DateTimeField') %}
{{ field(class='date ' + css_class, **kwargs) }}
{% elif field.type == 'IntegerField' %}
{{ field(class='number ' + css_class, **kwargs) }}
{% else %}
{{ field(class=css_class, **kwargs) }}
{% endif %}
{% if field.errors %}
<ul class="errors">{% for error in field.errors %}<li>{{ error|e }}</li>{% endfor %}</ul>
{% endif %}
{% endmacro %}
usage is something like:
{{ field_with_errors(form.foo, placeholder='bar') }}
This lets me avoid boilerplate, but also lets me keep the display decisions in the template space.
Have a look at the rendering fields section.
Alternatively, you can add attributes to be rendered in the Jinja2 (etc.) template:
<div class="input-prepend">
{{ form.address(placeholder="example.com", id="address", autofocus="autofocus", required="required") }}
</div>
There's nothing to prevent you from using a variable for the ID value above, instead of address, then rendering the template with a keyword argument to populate it.

Jinja2 and Json

I have for example a JSON File
{
"Google":{
"Web":"www.web.de",
"Apps":{
"Drive": "DriveLink",
"Dropbox": "DropboxLink"
},
"Google Main":"http://mail.google.com",
"G+":"http://plus.google.com"
},
"Social":{
"Facebook":"http://www.facebook.de",
"G+":"https://plus.google.com",
"Xing":"http://www.xing.de",
"LinkedIn":"http://www.linkedin.com",
"Tumblr":"http://www.tumblr.com"
},
"Fun":{
"Reddit":"http://www.reddit.com"
}
}
As you can see I have under the section Google a Nested Section named Apps
With CherryPy I hand over this JSON Object as following with the name linksList:
#cherrypy.expose
def index(self):
linksFile = open('links.json', 'r')
linksList = json.load(linksFile)
template = jinjaEnv.get_template('index.html')
return template.render(linksList=linksList)
What I want is to render following:
Google
Web (as a link)
Google Main
G+
Apps
Drive
Dropbox
Social
Facebook
G+
Xing
and so on
What I don't understand is to do is to render this nested Objects like "Apps" recursively
The documentation reads:
It is possible to use loops recursively. This is useful if you are
dealing with recursive data such as sitemaps. To use loops recursively
you basically have to add the recursive modifier to the loop
definition and call the loop variable with the new iterable where you
want to recurse.
In your case this would be accomplished with the following:
<ul>
{% for key, value in linksList.items() recursive %}
<li>
{% if value is string %}
{{ key }}
{% else %}
{{ key }}
<ul>{{ loop(value.items()) }}</ul>
{% endif %}
</li>
{% endfor %}
</ul>
My 2 cents, just if someone comes here looking for rendering a JSON using Jinja, and complementing the response from #Ryon Sherman :)
Since JSON may have int values as well as strings, you can use if value is mapping (and flip the if-condition)
If your output must feel like a JSON you can use {{key|indent(width)}} + loop.depth
In my case, the template was for an email and key|indent() didn't work as expected so I ended up using an auxiliar {% for %} loop:
<div>
<code>{
{% for key, value in dic.items() recursive %}
{% if value is mapping %}
<p>
{% for it in range(loop.depth) %} {% endfor %}{{key}}: {
</p>
{{ loop(value.items()) }}
<p>
{% for it in range(loop.depth) %} {% endfor %}}
</p>
{% else %}
<p>
{% for it in range(loop.depth) %} {% endfor %}{{key}}: {{value}},
</p>
{% endif %}
{% endfor %}
}</code>
</div>

Categories