List comprehensions in Jinja - python

I have two lists:
strainInfo, which contains a dictionary element called 'replicateID'
selectedStrainInfo, which contains a dictionary element called 'replicateID'
I'm looking to check if the replicateID of each of my strains is in a list of selected strains, in python it would be something like this:
for strain in strainInfo:
if strain.replicateID in [selectedStrain.replicateID for selectedStrain in selectedStrainInfo]
print('This strain is selected')
I'm getting the correct functionality in django, but I'm wondering if there's a way to simplify using a list comprehension:
{% for row in strainInfo %}
{% for selectedStrain in selectedStrainsInfo %}
{% if row.replicateID == selectedStrain.replicateID %}
checked
{% endif %}
{% endfor %}
{% endfor %}

List comprehensions are not supported in Jinja
You could pass the data, via Jinja, to JavaScript variables like so
var strainInfo = {{strainInfo|safe}};
var selectedStrainInfo = {{selectedStrainInfo|safe}};
and then do your clean up there.
Use Jinja's safe filter to prevent your data from being HTML-escaped.

Since v2.7 there is selectattr:
Filters a sequence of objects by applying a test to the specified attribute of each object, and only selecting the objects with the test succeeding.
If no test is specified, the attribute’s value will be evaluated as a boolean.
Example usage:
{{ users|selectattr("is_active") }}
{{ users|selectattr("email", "none") }}
Similar to a generator comprehension such as:
(u for user in users if user.is_active)
(u for user in users if test_none(user.email))
See docs.

Related

Assign multiple variables in a with statement after returning multiple values from a templatetag

Is there a way to assign multiple variables in a with statement in a django template. I'd like to assign multiple variables in a with statement after returning multiple values from a templatetag
My use case is this:
{% with a,b,c=object|get_abc %}
{{a}}
{{b}}
{{c}}
{% endwith %}
I don't think it's possible without a custom templatetag.
However if your method returns always the same length you can do it more compact like this:
{% with a=var.0 b=var.1 c=var.2 %}
...
{% endwith %}
I'm not sure that this as allowed, however from docs multiple assign is allowed.
But you can assign these 3 variables to 1 variable, which will make it tuple object, which you can easily iterate by its index.
{% with var=object|get_abc %}
{{ var.0 }}
{{ var.1 }}
{{ var.2 }}
{% endwith %}
Its not supported and its not a flaw of Django Template Language that it doesn't do that, its Philosophy as stated in the docs:
Django template system is not simply Python embedded into HTML. This
is by design: the template system is meant to express presentation,
not program logic
What you could do is prepare your data on Python side and return appropriate format which will be easy to access in template, so you could return a dictionary instead and use dotted notation with key name:
{# assuming get_abc returns a dict #}
{% with var=object|get_abc %}
{{ var.key_a }}
{{ var.key_b }}
{{ var.key_c }}
{% endwith %}

Python (Jinja2) variable inside a variable

I am trying to iterate over a dictionary in a Jinja2 template (in Ansible). One of the arrays or keys in the dictionary is 'abcd'
This {{ item.value.abcd.port }} works fine, but key 'abcd' varies in each dictionary.
I am looking to do something like below using a variable 'nginx_dir'.
{% set nginx_dir = item.value.keys().1 %}
{% set my_port = item.value.nginx_dir.port %}
Or without using a variable at all, something like this
{{ item.value.[item.value.keys().1].port }}
I had to use either of these to use a variable inside a variable.
{% set my_port = item.value.get(nginx_dir).port %}
{% set my_port = item.value[nginx_dir].port %}
I didn't wanted to hardcode my Jinja2 templates, this is exactly what I was looking for.

Django templates - find if at least one item from a list appears in other list

Let's say I have two lists of strings or integers. I'd like to check if any element from the first list appears in the second one and show something ONLY ONCE if that condition is NOT met. If I do for loop twice I won't get the desired result - item which I'd like to display will show up multiple times:
# I send this from view to template
b = [{'id':1}, {'id':2}, {'id':3}, {'id':4}, {'id':5}]
d = [{'id':5}, {'id':6}, {'id':7}, {'id':8}]
# In template
{% for a in b %}
{% for c in d %}
{% if not a.id == c.id %}
this will be displayed multiple times
{% endif %}
{% endfor %}
{% endfor %}
How can I display something only once here? Is this a practical way of checking things like this at all?
One can argue if the above belongs in a template at all, but if you can't perform this comparison in a view use a templatetag

Issues with loops in Django [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
how to create counter loop in django template?
I want to print some data based on some condition,
I want to use it like we used in other languages:
for(i=1;i<=count;i++)
print i
To do this in django I wrote
{% for i in count %}
<p>{{ i }}</p>
{% endfor %}
but it gives me an error 'int' object is not iterable.Count is coming from views.py and if I prints the count alone than it shows the output.
I wanted to print some value until count not becomes zero,So how i can do this in django.
And one more thing can we use while loop in django because i also try to use it for this task but it gives me the error of invalid block tag: 'while'
So please let me know how can I do this task ?
Thanks
Edit
in my view.py I have used like this
count=Product_attributes.objects.count()
and then pass this count to my template
Django templates are not programming language. Write all you logic in the view or models, and pass data into the template:
def view(request):
values = []
for i in range(10):
values.append(i) # your custom logic here
return render_to_response("/path/to/template", {'values': values})
in template:
{% for value in values %}
<p>{{ value }}</p>
{% endfor %}
The "for i in var" syntax works only where "var" is an iterable eg a list, tuple, set, dict...
I'd suggest the following: Instead of passing the item count to the template, pass in the iterable eg list in. If all you have is a count, you can create an iterable using range(count) in the view. In code
# Extract from view
def view(request):
# Set up values. Values is a list / tuple / set of whatever you are counting
values = Product_attributes.objects.all()
return render_to_response("/path/to/template", {'values': values})
# Extract from template
{% for value in values %}
<p>{{value}}</p>
{% endfor %}
The "while" tag is not a valid built in tag in django. A list of valid built-in tags can be seen here: https://docs.djangoproject.com/en/dev/ref/templates/builtins/
This way of doing things is not specific to templates only: it has parallels in "regular python" where the canonical way to iterate over a collection is:
for item in iterable:
# do something with the item
pass
More information on the "python way" of doing for loops can be found here: http://wiki.python.org/moin/ForLoop
If it's not appropriate to add the range to your view code (I don't like adding purely template-ty things to my views), I'd probably create a range filter:
#register.filter
def range(val):
return range(val)
Then you'd use it like this:
{% for i in count|range %}
<p>{{ i }}</p>
{% endfor %}
There's also an extremely ugly hack you can use if you don't want to bother writing any python code for this, it takes advantage of Django's center (or ljust and rjust) filters which create a string of length of the value provided. You can use it like this:
{% for x in count|center:count %}
<p>{{ forloop.counter }}</p>
{% endfor %}
I wouldn't recommend doing it this way though, I'm just demonstrating that it's possible.

How can I tell if a value is a string or a list in Django templates?

I've got a tuple of values I'm iterating in a Django template (1.4). Some of the values are strings which must just print out, others are tuples containing strings, which must be iterated themselves to print out their values. Is there a way, within the template, that I can decide if a given value, as I iterate over the master tuple, is a string or a list (tuple) ?
There's no builtin way to do so. A (somewhat dirty IMHO) workaround would be to implement a custom "is_string" filter, but the best solution would be to preprocess the values in the view to make it an uniform list of tuples (or list).
for the filter solution:
#register.filter
def is_string(val):
return isinstance(val, basestring)
and then in your templates:
<ul>
{% for whatever in something %}
<li>
{% if whatever|is_string %}
{{ whatever }}
{% else %}
<ul>
{{ whatever|unordered_list }}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
cf the excellent Django doc for more on custom filters and templatetags:
https://docs.djangoproject.com/en/stable/howto/custom-template-tags/
You can create an isinstance filter in the view or a helper module:
from django.template.defaultfilters import register
#register.filter(name="isinstance")
def isinstance_filter(val, instance_type):
return isinstance(val, eval(instance_type))
Then in the template you could do:
{% load isinstance %}
{% if some_value|isinstance:"list" %}
// iterate over list
{% else %}
// use string
{% endif %}

Categories

Resources