Jinja2 is treating list of dictionary as string - templates

I am using ansible to parse a jinja2 template. The jinja2 template has a piece of code which should iterate over a variable which is a list of dicts. However it is treating the list of dict as string and printing the individual characters. Please note the variable is set_fact in the ansible playbook.
Code to loop on the list of dicts in the j2 template:
{% for subscriber in subscribers %}
{% for dict_item in subscriber['filter_policy'] %}
{{dict_item.name}}
{% endfor %}
{% endfor %}
Variable as output in the debug module of ansible:
subscribers": [
{
"filter_policy": "[ { \"name\": \"Severity\", \"type\": \"CommaDelimitedList\", \"value\": \"critical\" }, { \"name\": \"Environment\", \"type\": \"CommaDelimitedList\", \"value\": \"nonprod\" }]",
"id": "blah#blah.com"
}
]
Ansible gives me an error saying:
"msg": "AnsibleUndefinedVariable: 'str object' has no attribute 'name'"
However if I use set in the jinja2 template to assign the same value to the variable and use for loop over it, it works fine.
{% set policies = [{"name": "Severity","type": "CommaDelimitedList","value": "critical"},{"name": "Environment","type": "CommaDelimitedList","value": "nonprod"}] %}
I have no clue, how to resolve it.
ansible 2.7.2
python version = 3.7.3 (default, Mar 27 2019, 09:23:39) [Clang 10.0.0 (clang-1000.11.45.5)]

Your filter_policy variable effectively contains a string which happens to be a json representation of a list of dicts. You just have to decode that json string to an effective list of dicts with the from_json filter
{% for subscriber in subscribers %}
{% for dict_item in (subscriber['filter_policy'] | from_json) %}
{{dict_item.name}}
{% endfor %}
{% endfor %}

Related

Was wondering why iterating through a dictionary using .keys in django would not work?

I know that .items would be useful to grab the value, but wanted to see why this would not work?
Data:
...
city_data = {
'city': json_data['name'],
'country': json_data['sys']['country'],
'temp': json_data['main']['temp'],
'feels_like': json_data['main']['feels_like'],
'temp_max': json_data['main']['temp_max'],
'temp_min': json_data['main']['temp_min']
}
return render(request, ..., context={'city_data':city_data})
template:
...
{% for key in city_data.keys %}
<li>{{city_data.key}}</li>
{% endfor %}
...
I think that the reason that it doesn't work that way is because django will look at test.key and try to look up a string "key" as an actual key to the dictionary. There are a couple ways that you could do this. One way is you could define a custom template filter that would allow you to do it. I don't know much about custom filters so I can't say how specifically to do it. Another way though is to use city_data.items in your template instead like this:
{% for key,value in city_data.items %}
<li>{{ value }}</li>
{% endfor %}

How to parse the following dict in django templates?

I have a following dict structure passed to my Django template, whose keys or length are unknown to me:
config_values = {
'Kafka': {
'KAFKA_BROKER_URL': 'localhost:9092',
},
'Redis': {
'REDIS_HOST': 'localhost',
'REDIS_PORT': '6379',
},
}
I want to show it in my templates as shown below
Kafka
KAFKA_BROKER_URL = localhost:9092
Redis
REDIS_HOST = localhost
REDIS_PORT = 6379
I'm relatively new to python and dicts.
You can iterate through a dict in a django template just the way you do in python.
Look at this.
{% for i,j in config_file.items %}
{{i}} // i will give you 'kafka'
{% for k,l in j.items %}
{{k}} {{l}} // k will give you 'kafka_broker_url' and l will give you localhost:9092
{% endfor %}
{% endfor %}

Access template passed variables in Django

I pass to the template:
testruns, which is "get_list_or_404(TestRun)"
and
dict, which is smth like this:
for testrun in testruns:
dict[testrun.id] = {
'passed' : bla bla,
'failed' : bla bla 2
}
Practically a map between testrun.id and some other info from a set from TestRun Model
In the template I want to do this:
{% for testrun in testruns %}
console.log("{{ dict.testrun.id }}");
{% endfor %}
But doesn't output anything
console.log("{{ testrun.id }}"); will output a specific id ("37" for example)
console.log("{{ dict.37 }}"); will output the corresponding value from the dict
So, why doesn't this output anything?
console.log("{{ dict.testrun.id }}");
How should I get the data from 'passed' and 'failed' from the dict:
Also, this:
console.log("{{ dict[testrun.id] }}");
Will output this error:
TemplateSyntaxError at /path/dashboard
Could not parse the remainder: '[testrun.id]' from 'dict[testrun.id]'
The dot will be considered as a trigger for attribute lookup by template engine, so dict.testrun.id will be parsed as "try to find id attribute from testrun attribute from dict". Instead, if you want to show the whole dict content, you might just iterate through dictionary:
{% for key, value in dict.items %}
Testcase: {{ key }}
Passed: {{ value.passed }}
Failed: {{ value.failed }}
{% endfor %}
Or, if you are looking for dict lookup by variable's value, you will have to make custom template tag, like it was described here - Django template how to look up a dictionary value with a variable

How can I access items within a ndb query object clientside?

I'm working with appengine, django and webapp2 and am sending a query object clientside such that
{{ exercise_steps }}
returns
Query(kind='ExStep')
and
{{ exercise_steps.count }}
returns 4
I'm trying to chuck the variable exercise_steps into some javascript code and need to iterate through the items in a javascript (not a django) loop but I cannot seem to access the items.
I've tried {{ exercise_steps|0 }}, {{ exercise_steps[0] }}, {{ exercise_steps.0 }} but it returns nothing. I know I can do this with a django loop but is there a way I can access the objects within the query with a javascript loop using something like
for (var i = 0; i < {{exercise_steps.count}}; i++) {
console.log({{ exercise_steps.i.location }})
}
You can't mix client side code and template code... by the time the javascript runs, your python code is already executed. You are not sending a python object to the javascript - it's executed when the HTML is generated.
You need to recreate array in JS, or have an ajax call return an array from python.
var steps = [
{% for step in exercise_steps %}
{{ step.location }}{% if not forloop.last %},{% endif %}
{% endfor %}]; // now your python iterable is a JS array.

Django - use template tag and 'with'?

I have a custom template tag:
def uploads_for_user(user):
uploads = Uploads.objects.filter(uploaded_by=user, problem_upload=False)
num_uploads = uploads.count()
return num_uploads
and I'd like to do something like this, so I can pluralize properly:
{% with uploads_for_user leader as upload_count %}
{{ upload_count }} upload{{ upload_count|pluralize }}
{% endwith %}
However, uploads_for_user leader doesn't work in this context, because the 'with' tag expects a single value - Django returns:
TemplateSyntaxError at /upload/
u'with' expected format is 'value as name'
Any idea how I can get round this?
You could turn it into a filter:
{% with user|uploads_for as upload_count %}
While a filter would still work, the current answer to this question would be to use assignment tags, introduced in Django 1.4.
So the solution would be very similar to your original attempt:
{% uploads_for_user leader as upload_count %}
{{ upload_count }} upload{{ upload_count|pluralize }}
Update: As per the docs assignment tags are deprecated since Django 1.9 (simple_tag can now store results in a template variable and should be used instead)
In Django 1.9 django.template.Library.assignment_tag() is depricated:
simple_tag can now store results in a template variable and should be used instead.
So, now simple tag we can use like a:
It’s possible to store the tag results in a template variable rather
than directly outputting it. This is done by using the as argument
followed by the variable name. Doing so enables you to output the
content yourself where you see fit:
{% get_current_time "%Y-%m-%d %I:%M %p" as the_time %}
<p>The time is {{ the_time }}.</p>