Django template loop where last item value is not Null - python

DB data:
pk,text
2|null
3|foo
4|null
5|bar
6|null
I intend to loop the given data, but I only want to iterate over items where text column value is NOT null. Also, I need to know which is the last value where text column is NOT null.
This is my intended view once the template has rendered:
3 foo
5 bar *last*
In python, such is achieved by an if condition in list comprehension, such as:
[x for x in my_loop if x != None]
Rendering this in the views.py is massively inconvenient due to how this data is also used in the rest of the template, and this would also gobble up too much unnecessary memory.
However, is this possible to do when rendering in django template?
Many thanks!

This should work.
{% for item in items %}
{% if item.text %}
<span>{{ item.text }}</span>
{% endif %}
{% endfor %}

Related

Jinja List Issue

I am getting a weird problem in Jinja, I have a list endpoints, which contains dictionary for every endpoint. In each dictionary, there is a key tags which is a list. Every item in tags is itself a dictionary where the key value gives the label of a tag. endpoint may have similar tags.
A sample abstract representation of an endpoints object can be:
[ {"tags":[{"value":"car"},{"value":"place"}]} , {"tags":[{"value":"van"},{"value":"place"}]} ]
what I want is to simple display unique tags in a div. It is simple, keeping a list of all displayed tags and upon getting a tag, checking if it is already in the list, and if not display it and add it to the list. Weirdly, it's not working.
The codes are:
{% set tagValues = [] %}
{% for endpoint in endpoints %}
{% for tag in endpoint["tags"]%}
{% set tagValue = tag["tag"]["value"] %}
{% if tagValue not in tagValues %}
{% set tagValues = tagValues + [tagValue] %}
<span >{{ tagValue }}</span></a>
{% endif %}
{% endfor %}
{% endfor %}
it is not working, for example, for the enpoints list above, I am getting the following output:
car place van place
is there any problem with the codes ?
I recommend creating a distinct list of tags in your View. e.g.
distinctTags = list(set([tag for endpoint in endpoints for tag in endpoint]))
and passing that to your template
{% for tag in distinctTags %}
<span >{{ tagValue }}</span></a>
{% endfor %}
this has the advantage of the distinct tag code being reusable and the code being less procedural.
my jinja knowledge is limited, but by adding tagValues to the output, it appears that it's reset after each iteration of the outer loop. I'd guess it's to do with scopes, but don't know.
My recommendation would be to pre-process your endpoints in regular python before passing to jinja

Django template tag comparison not working

I'm trying to compare two values in a Django template tag, but it's not working like I expect it to. Here's what my template markup looks like:
<ul>
{% for c in category %}
{% ifequal c.name|stringformat:"%s" values.project_category|stringformat:"%s" %}
<li>Values equal ({{c.name}}:{{values.project_category}})</li>
{% else %}
<li>Values differ ({{c.name}}:{{values.project_category}})</li>
{% endifequal %}
{% endfor %}
</ul>
The values item is a dictionary that gets passed to the template from the view, while the category is a QuerySet that I iterate over. The values dictionary starts out empty, but I get this output:
Values equal (One:)
Values equal (Two:)
Values equal (Three:)
As you can see from the output, the values do not equal (the values items don't show up because they don't exist). What am I doing wrong here? I assumed it must have been a typing issue, which is why I chose to use the stringformat modifier, but that doesn't seem to make a difference.
The issue is using "%s" in the stringformat filter. Leading % are dropped by the filter:
Django stringformat filter
Thus, you are just converting both strings to the literal "%s", and since "%s" == "%s" your ifequal is always true.

Return items from RSS feed in Django Templatetags

Django noob here.
I'm trying to add RSS feed items into a django template using templatetags (with classytags).
Here's my code:
from django import template
from classytags.core import Tag
import feedparser
register = template.Library()
class ExampleTag(Tag):
name = 'exampletag'
def render_tag(self, context):
raw_feed = "example.com/feed.rss"
feed = feedparser.parse(raw_feed)
entrylist = {}
for entry in feed.entries:
entrylist[entry.title]
return entrylist
register.tag(ExampleTag)
Then, in the template I can call the ExampleTag with:
{% load my_tag %}
{% exampletag %}
This results in a KeyError at / u'The First Entry In The Feed'
If I change my code to append to a list, the template renders without error and the entire structured list is output in a single string.
This is what I'd like to do:
{% load my_tag %}
{% for item in exampletag %}
<p> {{ item }} </p>
{% endfor %}
However this just fails silently (obviously I'm not passing an interable object to the template)
Any ideas? Is this even a good way to go about doing this?
Thanks in advance.
This code looks highly suspect:
for entry in feed.entries:
entrylist[entry.title]
Shouldn't it be something like this?
for entry in feed.entries:
entrylist[entry.title] = entry # or some value
As it is right now you are trying to index into an empty dictionary and are thus getting a KeyError exception.
But I'm still not sure what you are trying to do. Here are 2 ideas that come to mind that may get you started.
Idea one: it sort of looks like you should write an inclusion tag.
Something like (untested):
#register.inclusion_tag('feed_entries.html'):
def feed_entries():
feed = feedparser.parse('example.rss')
return {'items': feed}
And in feed_entries.html
{% for item in items %}
<p> {{ item }} </p>
{% endfor %}
Then, in some random template where you want the list of items displayed:
{% load feed_tags %}
...
<p>Here are the latest entries:</p>
{% feed_entries %}
...
This is assuming feed contains a list of items you want to render somehow. Thus, whenever you use {% feed_entries %} in a template, your snippet of Python is called, it takes the returned dictionary and renders the feed_entries.html template, and the resulting HTML is placed wherever you wrote {% feed_entries %}.
Idea two: If you really want your tag to return a list of items, you could use an assignment tag:
#register.assignment_tag
def feed_entries():
return feedparser.parse('example.rss')
Then in your template you have to "catch" the result of this tag (the list of items):
{% feed_entries as items %}
{% for item in items %}
<p>{{ item }}</p>
{% endfor %}
But that means you'll have to duplicate the "as" and for-loop stuff in every template. The inclusion tag may save you typing and maintenance if you use it in many templates. But if you wanted to render the list differently in each template it would be more flexible. Say you want it in a list of <p> tags in one, but in a <ul> in another.

Django template iterating over list

I have a list created in Django view:
list = [ elem1, elem2, ..., elemN ]
The list is variable length: it can contain 0-6 elements. I want to iterate over the list in the template, but I would like the loop to run always 6 times, yielding None or empty string for non-existing elements.
I tried something like this:
{% for i in "0123456" %}
{{ list.i }}
{% endfor %}
but this obviously doesn't work. I know I could do this in the view, but I would like to have this in the template. Is is possible?
You can add an if statement checking if it is your 6th time through the loop.
{% for item in someList %}
{% if forloop.counter <= 6 %}
{{ item }}
{% endif %}
{% endfor %}
http://docs.djangoproject.com/en/1.3/ref/templates/builtins/#for in the docs.
Of course, if your list is very long then this is not optimal. I would also suggest processing the list in views.py and then passing it to the template. Logic should stay in the views if possible.
This gives you control over the number of loops done. To completely solve your problem you will need some addtional logic but see my note above regarding this.
Check this snippet: Template range filter

django template question (accessing a list)

I am writing a template for my first django website.
I am passing a list of dictionaries to the template in a variable. I also need to pass a few other lists which hold boolean flags. (Note: all lists have the same length)
The template looks something like this:
<html>
<head><title>First page</title></head><body>
{% for item in data_tables %}
<table>
<tbody>
<tr><td colspan="15">
{% if level_one_flags[forloop.counter-1] %}
<tr><td>Premier League
{% endif %}
<tr><td>Junior league
<tr><td>Member count
{% if level_two_flags[forloop.counter-1] %}
<tr><td>Ashtano League
{% endif %}
</tbody>
</table>
{% endfor %}
</body>
</html>
I am getting the following error:
Template error
In template /mytemplate.html, error at
line 7 Could not parse the remainder:
'[forloop.counter-1]' from
'level_one_flags[forloop.counter-1]'
I am, not suprised I am getting this error, since I was just trying to see if would work. So far, from the documentation, I have not found out how to obtain the items in a list by index (i.e. other than by enumeration).
Does anyone know how I may access a list by index in a template?
In short, Django doesn't do what you want.
The for loop has a number of useful properties within a loop.
forloop.counter The current iteration of the loop (1-indexed)
forloop.counter0 The current iteration of the loop (0-indexed)
forloop.revcounter The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0 The number of iterations from the end of the loop (0-indexed)
forloop.first True if this is the first time through the loop
forloop.last True if this is the last time through the loop
forloop.parentloop For nested loops, this is the loop "above" the current one
You could probably use forloop.counter0 to get the zero-based indexes you want; unfortunately, the Django template language doesn't support variable array indexes (You can do {{ foo.5 }}, but you can't do {{ foo.{{bar}} }}).
What I usually do is to try and arrange the data in the view to make it easier to present in the template. As an example, for you could create an array in your view composed of dictionaries so that all you have to do is loop through the array and pull exactly what you need out of the individual dictionaries. For really complicated things, I've gone so far as to create a DataRow object that would correctly format the data for a particular row in a table.
You use the dot-operator to index the array, or, really, to do anything.
Technically, when the template system
encounters a dot, it tries the
following lookups, in this order:
* Dictionary lookup
* Attribute lookup
* Method call
* List-index lookup
I don't believe you can do math on the index. You'll have to pass in your array constructed in some other way so that you don't have to do this subtraction.
Try using "slice" to access a list by index
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#slice
Perhaps a better way is to use forloop.last. Of course, this will require that you send to the template the specific level_one_flag and level_two_flag out of the level_one_flags and level_two_flags arrays, but I think this solution keeps a better logical separation between view and template:
<html>
<head><title>First page</title></head><body>
{% for item in data_tables %}
<table>
<tbody>
<tr><td colspan="15">
{% if forloop.last and level_one_flag %}
<tr><td>Premier League
{% endif %}
<tr><td>Junior league
<tr><td>Member count
{% if forloop.last and level_two_flag %}
<tr><td>Ashtano League
{% endif %}
</tbody>
</table>
{% endfor %}
</body>
</html>

Categories