I have been trying to automate a template for deploy by Ansible:
Inventory contents:
[splunk_license]
10.10.113.209
[splunk_master]
[splunk_search]
10.10.113.209
[splunk_indexer]
10.10.113.234
My template has logic based on whether the splunk_master group has a host defined or not.
Original code:
{% if inventory_hostname in groups['splunk_indexer'] and
groups['splunk_master']|length > 0 %}
#{% if blah blah blah blah...%}
# CUSTOMER INDEXES go to $SPLUNK_HOME/etc/master-apps/_cluster/local/indexes.conf
# on Master node
{% elif inventory_hostname in groups['splunk_master'] %}
#{% if some other blah blah blah blah...%}
# CUSTOMER INDEXES go to $SPLUNK_HOME/etc/master-apps/_cluster/local/indexes.conf
{% else %}
# CUSTOMER INDEXES
[nothing]
coldToFrozenDir = $SPLUNK_DB/frozen/nothing/frozendb
thawedPath = $SPLUNK_DB/hotwarm/nothing/thaweddb
coldPath = volume:secondary/nothing/colddb
homePath = volume:primary/nothing/db
{% endif %}
No matter what I did I could not get the bottom part after {% else %} to work. Turns out the commenting out '#' does not actually cause that line to be ignored, which I had for testing purposes as I was tired of typing stuffs out over and over.
I tried to modify my (uncommented) if statements every which way from Sunday and I would either get only the top part of template, an Ansible error complaining about unexpected 'elif' or groups not found errors.
Jinja comments are as follows {# comment #} if using single # jinja will still evaluate those lines causing errors or a bad formatted destination file, see Jinja Templating docs
I was going to ask for help here but last minute tried to remove all commented lines out and now my template finally works.
Working code (commented lines removed):
{% if inventory_hostname in groups['splunk_indexer'] and
groups['splunk_master']|length > 0 %}
# CUSTOMER INDEXES go to $SPLUNK_HOME/etc/master-apps/_cluster/local/indexes.conf
# on Master node
{% elif inventory_hostname in groups['splunk_master'] %}
# CUSTOMER INDEXES go to $SPLUNK_HOME/etc/master-apps/_cluster/local/indexes.conf
{% else %}
# CUSTOMER INDEXES
[nothing]
coldToFrozenDir = $SPLUNK_DB/frozen/nothing/frozendb
thawedPath = $SPLUNK_DB/hotwarm/nothing/thaweddb
coldPath = volume:secondary/nothing/colddb
homePath = volume:primary/nothing/db
{% endif %}
Apologies if this is Jinja's obvious behaviour.
Related
So on my front-end, I want to show some HTML only if the user belongs to one of 2 groups: 'admins' or 'clerks'. There are 3 groups of users: 'admins', 'clerks', and 'sellers'. Here is my front-end code:
{% if user.groups.all.0 == "admins" %}
<h1>Some HTML</h1>
{% elif user.groups.all.0 == "clerks" %}
<h1>Some HTML</h1>
{% endif %}
When I run this code, the HTML shows for the admins. But that of the clerks does not show. I have tried printing out the group to be sure that the spelling and the casing were the same, and they were the same. And Django does not throw an error. It only works if I rewrite the code as follows:
{% if user.groups.all.0 == "admins" %}
<h1>Some HTML</h1>
{% elif user.groups.all.0 != "sellers" %}
<h1>Some HTML</h1>
{% endif %}
But I feel like this is not good design. Please am I missing something? Thank you all in advance
First of all, is a not practical idea for too many reasons.
Think that using that method you are getting possibilities and processing that don't correspond to the templates. The templates engine philosophy is just basic and no important logic.
A way to have more control and more practical is using a flag. For example
groups_permited_for_this=["admins","clerks",...]
Permited =False
for group in user.Groups.all:
if group.name in groups_permited_for_this:
Permited =True
Pass it as context and then use a Jinja if statement.
So I resolved the issue in a slightly different way from the one above:
First, I created a folder called 'templatetags' inside the app.
Then I created 2 files in it:
An empty 'init.py' file
A 'cust_auth.py'(for custom authentication) file
Inside the 'cust_auth.py' file I wrote a function that does a similar thing as in #Paulo Aguilar's answer above:
from django import template
from django.contrib.auth.models import Group
register = template.Library()
#register.filter(name='has_group')
def has_group(user, group_name):
group = Group.objects.get(name=group_name)
return True if group in user.groups.all() else False
Then in my template, I loaded the 'cust_auth.py' file at the top:
{% load auth_extras %}
Then I did the logic I was looking for:
{% if user|has_group:"admins" %}
<h1>Some HTML</h1>
{% endif %}
I preferred this because I think over the cause of my application, I will want more custom authentications
Thank you Paulo and whoever posts
I use the Order Printer app in Shopify to print my orders. I have edited the the template to suit my needs, however I am quiet new to Liquid code.
Based on the shipping postcode of the order, I need the template to return 1 of 3 labels - Rural, Major and Outer. I have a list of postcodes in the following format (this is a small portion for example):
Rural
2648, 2715, 2717-2719, 2731-2739, 3221-3334, 3342-3349, 3351-3352, 3357-3426, 3444-3688, 3691-3749, 3812-3909, 3921-3925, 3945-3974, 3979, 3984-3999
Major
1000-1935, 2000-2079, 2085-2107, 2109-2156, 2158, 2160-2172, 2174-2229, 2232-2249, 2557-2559, 2564-2567, 2740-2744, 2747-2751, 2759-2764, 2766-2774, 2776-2777, 2890-2897
Outer
7020-7049, 7054, 7109-7150, 7155-7171, 7173-7247, 7255-7257, 7330-7799
I'm unable to work out how to use the if statement for the purpose of identifying if the shipping postcode is a rural, major or outer postcode, without typing out every postcode between 7330 and 7799 etc.
Can anyone help?
First declare your arrays:
{% assign Rural= "2648, 2715, 2717-2719, 2731-2739, 3221-3334, ...." | split: ", " %}
{% assign Major= "1000-1935, 2000-2079, 2085-2107, 2109-2156,..." | split: ", " %}
{% assign Outer= "7020-7049, 7054, 7109-7150, 7155-7171,...." | split: ", " %}
Then declare a variable that you will use for the label
{% assign relatedLabel = ""%}
Implement the if logic
{% if Rural contains Order.PosteCode %}
{% assign relatedLabel = "rural" %}
{% endif %}
{% if Major contains Order.PosteCode %}
{% assign relatedLabel = "major" %}
{% endif %}
{% if Outer contains Order.PosteCode %}
{% assign relatedLabel = "outer" %}
{% endif %}
Finally you can print it where you need it
This Poste Code belongs to {{relatedLabel}} area.
In my Django web-app, I want to let users sort model objects by different parameters, which I achieve with URL parameters which tell the view which items should be loaded. Here's the Jinja/HTML snippet from the template:
<p><b><span class="text-info">sort by:</span></b>
latest_release |
alphabetically |
soonest release</p>
If the user is already sorting by latest_release (the first link), I want the link from it removed. However, I can't seem to find a way to do this in a DRY way.
You can define dict in your view with argument name - display name mapping:
mapping = {'': 'latest_release', 'name': 'alphabetically', 'next_release': 'soonest release'}
and pass it to context:
context['mapping'] = mapping
Now in template iterate over each pair from dict and show link only if sorted_by value not equal with key:
{% for k, v in mapping.items %}
{% if request.GET.sorted_by|default:"" != k %} {{ v }} |{% endif %}
{% endfor %}
To remove | delimiter after last link you can validate forloop.last status.
Below is the jinja2 template that i wrote to use in ansible.
{% set port = 1234 %}
{% set server_ip = [] %}
{% for ip in host_ip %}
{% do server_ip.append({{ ip }}:{{ port }}) %}
{% endfor %}
{% server_ip|join(', ') %}
Below is the my desired output:
devices = 192.168.56.14:1234,192.168.56.13:1234,192.168.56.10:1234
But when i am running the ansible playbook, it is throwing the error as below:
"AnsibleError: teme templating string: Encountered unknown tag 'do'. Jinja was looking for th: 'endfor' or 'else'
Any help would be appreciated..
Try below code:
{% set port = '1234' %}
{% set server_ip = [] %}
{% for ip in host_ip %}
{{ server_ip.append( ip+":"+port ) }}
{% endfor %}
{{ server_ip|join(',') }}
You ll get:
192.168.56.14:1234,192.168.56.13:1234,192.168.56.10:1234
I didn't like any of the answers, they feel too hacky (having to worry about outputting None, or spurious whitespace using other techniques), but I think I've found a solution that works well. I took inspiration from this answer on a related question and realized that you can call set multiple times for the same variable and seemingly not incur any penalty.
It's still a tad hacky, because I don't think it's intended to work like this (then again, several design decisions in Jinja make me scratch my head, so who knows).
{% set server_ip = server_ip.append({{ ip }}:{{ port }}) %}
Interestingly, while the value is indeed appended to server_ip, the return value of that append (which we now know very well is None) isn't assigned back to server_ip on the LHS. Which led me to discover that the LHS side of the statement seems to be a no-op.
So you can also do this and the append works:
{% set tmp = server_ip.append({{ ip }}:{{ port }}) %}
Yet, if you print tmp, nothing shows up. Go figure.
That worked for me:
- set_fact:
devices: >-
{% for ip in host_ip %}{{ ip }}:1234{% if not loop.last %},{% endif %}{% endfor %}
If you still want to use do then add
jinja2_extensions = jinja2.ext.do
to your ansible config file and change
{% do server_ip.append({{ ip }}:{{ port }}) %}` to `{% do server_ip.append({ip:port}) %}`
The most voted answer will cause a lot of whitespaces in the rendered result. Beside using do extension from jinja, the alternative solution is using whitespace control from jinja. Add minus sign - inside the block
{%- for ip in host_ip -%}...{%- endfor %}
will remove the whitespace.
In order to avoid having None printed all over using {{ server_ip.append( ip+":"+port ) }} (just spent 20 min debugging this) and if you don't want to use the misleading {% set _ = server_ip.append( ip+":"+port ) %}, you can go back to Python basics and do the following:
# Remember that [1, 2] + [3] = [1, 2, 3]
{% set server_ip = server_ip + [ip+":"+port] %}
In 99.9% situations this will do the job. However in special cases where you work with very large lists there may be a small performance downside here in terms of memory usage: in the above example, [1, 2] + [3] = [1, 2, 3], both [1, 2] and [1, 2, 3] (initial and modified list) will coexist in memory for a brief moment, contrary to the append method which doesn't create additional objects in memory.
One-line solution with map() and regex:
{{ ["1.1.1.1","2.2.2.2"]|map('regex_replace', '(.+)', "\\1:1234")|join(', ') }}
map('regex_replace', '(.+)', "\\1:1234") adds :1234 to any non-empty string (.+) in the passed array ["1.1.1.1","2.2.2.2"]. Result:
1.1.1.1:1234, 2.2.2.2:1234
Seems like elementary question, and yet can't get it work
{% if iterator.next > 10 %}
Do smth
{% endif %}
Two issues. First, this code just won't work (the code in the if-condition never implemented even when the condition seems to hold true), and second issue - the ">" sign is highlighted as if it where the closing tag of the closest open tag. Any ideas how to fix the first issue and is it all right with second issues ? Maybe there's some elegant syntax that I am missing and that would remove this ambiguity for the text editor ?
iterator.next may be a string which would result in the statement being False.
Try creating a custom filter to convert it to an int. For example create "my_filters.py":
# templatetags/my_filters.py
from django import template
register = template.Library()
#register.filter()
def to_int(value):
return int(value)
Then in your template:
{% load my_filters %}
{% if iterator.next|to_int > 10 %}
Do smth
{% endif %}
More on custom tags and filters here
I wouldn't worry about the highlighting, this may just be your IDE. I recommend using PyCharm for Django development
Django's docs says that you can use > with if tag:
{% if somevar < 100 %}
This appears if variable somevar is less than 100.
{% endif %}
take a look at documentation: https://docs.djangoproject.com/en/1.9/ref/templates/builtins/
maybe you are missing something else?