Django template iterating a queryset with a for loop counter - django

I have 3 query sets for 3 different types of articles: Right leaning, center, left leaning. I am trying to display these in 3 columns so it would come out as:
Right Lean | Center Lean | Left Lean
Right Lean | Center Lean | Left Lean
Right Lean | Center Lean | Left Lean
So, I have these in three query sets, like this in the view:
right_articles = sline.article_set.filter(avgLean__lte=-0.75).order_by('-credibility')[:25]
left_articles = sline.article_set.filter(avgLean__gte=0.75).order_by('-credibility')[:25]
center_articles = sline.article_set.filter(avgLean__gt=-0.75).filter(avgLean__lt=0.75).order_by('-credibility')[:25]
And I added a counter for how many I want in each column as part of the context like this:
return render(request, 'storylines/detail.html', {'n_articles': range(0, 24),
'storyline': sline,
'reqUser':request.user,
'vote':ini, 'right_articles':right_articles,
'left_articles': left_articles,
'center_articles': center_articles})
Then I go to the template and loop through those like this:
<div class="row">
<div class= "col-4">
Left articles: {{ left_articles.count }}
</div>
<div class= "col-4">
Center articles: {{ center_articles.count }}
</div>
<div class= "col-4">
Right articles: {{ right_articles.count }}
</div>
</div>
{% for i in n_articles %}
<div class="row">
Trying {{ i }}<br>
{% with left_articles.i as lt_article %}
{% if lt_article %}
{% show_card lt_article %}
{% else %}
<div class="col-4">No Left article {{ i }}</div>
{% endif %}
{% endwith %}
{% with center_articles.i as cn_article %}
{% if cn_article %}
{% show_card cn_article %}
{% else %}
<div class="col-4">No Center article {{ i }}</div>
{% endif %}
{% endwith %}
{% with right_articles.i as rt_article %}
{% if rt_article %}
{% show_card rt_article %}
{% else %}
<div class="col-4">No Right article {{ i }}</div>
{% endif %}
{% endwith %}
</div>
{% endfor %}
The count statements on the first row shows that I do in fact have 1 each left/center/right. If I modify my code to replace right_articles.i with right_articles.0, I get an article. The line that says Trying {{ i }} shows that i starts at 0, so right_articles.i should return an article when i = 0. However, unfortunately, I get no articles, which means that I have something wrong with the syntax of
{% with right_articles.i as rt_article %}
Can anyone tell me the right way to say what I'm trying to say here?
Thank you!

I will not suggest anything to improve, a custom tag filter will do the trick:
from django import template
register = template.Library()
#register.filter
def select_item(queryset,i):
return queryset[i]
# Be careful of IndexError: list index out of range
So now, you will have access to the item
{% with left_articles|select_item:i as lt_article %}
{% endwith %}

Related

How to pass one django template with a 'for loop' into another one using 'include' statement?

I am working on FAQs page where questions and answers are passed to a template sections based on their categories. I would like to reduce amount of html and use section div as a template
<div id="{{id}}">
<div class="h2">{{category}}</div>
{% for q in faqs %}
{% if q.category == '{{category}}' %}
<ul class="collapsible">
<li>
<div class="collapsible-header">{{q.question}}></div>
<div class="collapsible-body"><span>{{q.answer}}</span></div>
<div class="divider"></div>
</li>
</ul>
{% endif %}
{% endfor %}
</div>
My main html contains following code:
{% with id='m_faq'%}
{% with category='Methodology'%}
{% include 'main/faqs_section.html' %}
{% endwith %}{% endwith %}
I am only able to pass variables id and category.
Is there a way to the for loop as well?
I think the solution would be to create a list for categories in views.py
cat = [ 'Category1', 'Category2', 'Category3','Category4']
pass it to context dictionary and then put additional 'for loop' around section div.
{% for c in cat %}
<div id="">
<div class="h4">{{c}}</div>
{% for q in faqs %}
{% if c == q.category %}
<ul class="collapsible">
<li>
<div class="collapsible-header">{{q.question}}</div>
<div class="collapsible-body"><span>{{q.answer}}</span></div>
<div class="divider"></div>
</li>
</ul>
{% endif %}
{% endfor %}
</div>
{% endfor %}
That will generate a template with list of faqs divided in to sections..

The best way to divide card-deck into rows in django template

What I have(image)
So i need to divide these cards into 4-5 cards per row.
My solution is simple
<div class="card-deck">
{% for book in books %}
{% include 'books/book_element.html' %}
{% endfor %}
</div>
And I have tried for a while and was doing some brainstorm
{% for book in books %}
{% if forloop.counter|divisibleby:"5" or forloop.counter == 1 %}
<div class="card-deck">
</div>
{% endif %}
{% endfor %}
How can I include a template into a div.card-deck?
You need to close the previous row and open a new one in your loop. Something like this:
<div class="card-deck">
{% for book in books %}
{% include 'books/book_element.html' %}
{% if forloop.counter|divisibleby:"5" %}
{# Close the current deck and start a new one %}
</div><div class="card-deck">
{% endif %}
{% endfor %}
</div><!-- The final deck is closed here, outside the loop -->

Django template skip line every two iteration

I have the following html structure:
<div class="row>
<div class="box"></div>
<div class="box"></div>
</div>
I am using pagination feature on Django to pass on 6 items per page.
How would I go about iterating over the paginator generated object list while wrapper each two box divs with row div?
You can use the forloop.counter in the template
{% for obj in obj_list %}
{% if forloop.counter0|divisibleby:2 %}
<div class="row">
{% endif %}
<div class="box"></div>
<div class="box"></div>
{% if forloop.counter|divisibleby:2 %}
</div>
{% endif %}
{% else %}
Nothing to show
{% endfor %}
and if there are odd number of elements in the list, then it would not have a trailing div. I will let you figure out that scenario by yourself. (it is pretty simple)
Documentation for the forloop.counter0 can be found here
Documentation for divisibleby can be found here
I agree with karthikr's solution, but it doesn't print the </div> if you have 3, 5 items...
You have to add a forloop.last to handle that case:
{% for obj in obj_list %}
{% if forloop.counter0|divisibleby:2 %}
<div class="row">
{% endif %}
<div class="box"></div>
<div class="box"></div>
{% if forloop.counter|divisibleby:2 or forloop.last %}
</div>
{% endif %}
{% else %}
Nothing to show
{% endfor %}

Django create multi colums flexible best practices

I want to create a picture viewer with many colums. I don' know exactly the number.
I want to respect best practices.
I have done the following code :
{% with max=4 taille=3 %}
{% for image in image_list %}
{% if nb == 1 %}
<div class="row">
{% endif %}
<div class="col-sm-{{ taille }}">
{{ image.name }}
</div>
{% if nb == 2 %}
</div>
{% endif %}
{% nb += 1 %}
{% if nb > 4 %}
{% nb = 1 %}
{% endif %}
{% endfor %}
{% endwith %}
But the templating system don't allow me to do this line :
{% nb += 1 %}
Do you have any ideas ? Thank you.
This is a quick example on how you can do this (this is for 2 column based itteration) but you can change it to your liking (replace "2" with a integer for instance):
<div class="row">
{% for image in image_list %}
<div class="col-sm-6">
{{ image.name }}
</div>
{% if not forloop.last and forloop.counter|divisibleby:"2" %}
</div>
<div class="row">
{% endfor %}
</div>
In Django Templates you can not use += 1 operator
you can use the forloop.counter
{% if forloop.counter0|divisibleby:4 %} so every 4 iterations
starting with first iteration where
counter is 0
<div class="row">
{% endif %}
<div class="col-sm-{{ taille }}">
{{ image.name }}
</div>
{% if forloop.counter0|add:"-1"|divisibleby:"4" %} also every 4 iterations
but starting with the 2nd
iteration where counter is 1
</div>
{% endif %}

ListField is showing <ul> instead of <input> in edit/create post

I am using Flask, mongoengine for a project and I am trying to get basic stuff working from http://docs.mongodb.org/manual/tutorial/write-a-tumblelog-application-with-flask-mongoengine/
After implementing everything from above link I added a new field for "tags" in Post and when I try to create a post, my tags doesn't show a input box.
Any help is appreciated.
My code and screenshot below
class Post(db.DynamicDocument):
created_at = db.DateTimeField(default=datetime.datetime.now, required=True)
title = db.StringField(max_length=255, required=True)
slug = db.StringField(max_length=255, required=True)
comments = db.ListField(db.EmbeddedDocumentField('Comment'))
tags = db.ListField(db.StringField(max_length=30)) # New field I added
template form
{% macro render(form) -%}
<fieldset>
{% for field in form %}
{% if field.type in ['CSRFTokenField', 'HiddenField'] %}
{{ field() }}
{% else %}
<div class="clearfix {% if field.errors %}error{% endif %}">
{{ field.label }}
<div class="input">
{% if field.name == "body" %}
{{ field(rows=10, cols=40) }}
{% else %}
{{ field() }}
{% endif %}
{% if field.errors or field.help_text %}
<span class="help-inline">
{% if field.errors %}
{{ field.errors|join(' ') }}
{% else %}
{{ field.help_text }}
{% endif %}
</span>
{% endif %}
</div>
</div>
{% endif %}
{% endfor %}
</fieldset>
{% endmacro %}
rendering form code
{% extends "admin/base.html" %}
{% import "_forms.html" as forms %}
{% block content %}
<h2>
{% if create %}
Add new Post
{% else %}
Edit Post
{% endif %}
</h2>
<form action="?{{ request.query_string }}" method="post">
{{ forms.render(form) }}
<div class="actions">
<input type="submit" class="btn primary" value="save">
Cancel
</div>
</form>
{% endblock %}
From what I can gather, your problem is you're telling WTF to render the tags field, but WTForms doesn't know how to handle that information.
From looking at the Flask-MongoEngine documentation, it seems the ListField is just a FieldList as WTForms refers to it.
Currently you're not actually defining the form independently in WTForms, you're just using the magic included in Flask-MongoEngine, so my first attempt would be to add some more logic to your macro, add a {% elif field.type == 'ListField' %} and try and discover what's contained in there to iterate through to produce your form. From having a quick look at the source-code, something like the following might work.
{% elif field.type == 'ListField %}
{# render_the_group_label #}
{% for subfield in field.entries %}
{% if subfield.type == 'StringField' %}
{# render_the_subfield #}
{% endif %}
{% endfor %}
...
That code will need to be worked on, but hopefully it'll point you in the right direction. Otherwise, I'd actually define the form seperately in WTForms to give you a bit more control on the code-side. Luckily they provide a csv tag example which should help you if you need to go that route. I wrote a guide that takes a different route using #property decorators to achieve a similar effect, which again, might at least point you towards the finish line.