How to reset cycle counter? - django

I have in template this:
{% for item in items %}
{% ifchanged item.st2 %}
<div class="clear"></div>
<div class="item-title">{{ item.get_st2 }}</div>
{% endifchanged %}
<div class="cell {% cycle 'clear tco1' 'tco' 'tco' 'tco2' 'clear tce1' 'tce' 'tce' 'tce2' %}">{{ item.name }}</div>
{% endfor %}
and it shows things like this:
------------------ st2 name 1 -------------------
+-----------------------------------------------+
| grey item | grey item | grey item | grey item |
+-----------------------------------------------+
| dark item | dark item | dark item | dark item |
+-----------------------------------------------+
| grey item | grey item | grey item | grey item |
+-----------------------------------------------+
| dark item | dark item | dark item | dark item |
+-----------------------------------------------+
| grey item | grey item | grey item | grey item |
+-----------------------------------------------+
------------------ st2 name 2 -------------------
+-----------------------------------------------+
| grey item | grey item | grey item | grey item |
+-----------------------------------------------+
| dark item | dark item | dark item | dark item |
+-----------------------------------------------+
and its good.
But if items on st2 name is less than 4 in one line:
------------------ st2 name 1 -------------------
+-----------------------------------------------+
| grey item | grey item | grey item | grey item |
+-----------------------------------------------+
| dark item | dark item | dark item |
+-----------------------------------+
then next looks like this:
------------------ st2 name 1 -------------------
+-----------------------------------+
| grey item | grey item | grey item |
+-----------------------------------------------+
| dark item | dark item | dark item | dark item |
+-----------------------------------------------+
because cycle coutner is still on the fourth element.
How to reset cycle tag?

You can use cycle tag with silent keyword to declare the cycle but not produce the first value. By giving the cycle tag a name, using "as" you can insert the current value of the cycle wherever you’d like. Another cycle tag with name of the variable will move cycle to the next value independently. And divisibleby tag can be used to check for last in line item:
{% cycle 'clear tco1' 'tco' 'tco' 'tco2' 'clear tce1' 'tce' 'tce' 'tce2' as cellcls silent %}
{% for item in items %}
{% ifchanged item.st2 %}
<div class="clear"></div>
<div class="item-title">{{ item.get_st2 }}</div>
{% if not forloop.counter1|divisibleby:"4" %} {# im not tested it #}
{% cycle cellcls %}
{% endif %}
{% endifchanged %}
<div class="cell {{cellcls}}">{{ item.name }}</div>
{% cycle cellcls %}
{% endfor %}

Ugly, but it works:
{% cycle '1' '2' '3' '4' as cellcount silent %}
{% cycle 'tco1' 'tco' 'tco' 'tco2' 'tce1' 'tce' 'tce' 'tce2' as cellcls silent %}
{% for item in items %}
{% ifchanged item.st2 %}
{% if not forloop.first %}
{% if cellcount|add:0 == 2 %}
{% cycle cellcls %}{% cycle cellcls %}{% cycle cellcls %}
{% cycle cellcount %}{% cycle cellcount %}{% cycle cellcount %}
{% endif %}
{% if cellcount|add:0 == 3 %}
{% cycle cellcls %}{% cycle cellcls %}
{% cycle cellcount %}{% cycle cellcount %}
{% endif %}
{% if cellcount|add:0 == 4 %}
{% cycle cellcls %}
{% cycle cellcount %}
{% endif %}
{% endif %}
<div class="clear"></div>
<div class="item-title">{{ item.get_st2 }}</div>
{% endifchanged %}
<div class="cell {{ cellcls }}" style="{{ cellcount }}">{{ item.name }}</div>
{% cycle cellcls %}
{% cycle cellcount %}
{% endfor %}

I came across same problem and write small custom template tag (works in django 1.8):
from django import template
import itertools
register = template.Library()
class ResetCycleNode(template.Node):
def __init__(self, variable_name, cycle_node):
self.variable_name = variable_name
self.cycle_node = cycle_node
def render(self, context):
cycle_iter = itertools.cycle(self.cycle_node.cyclevars)
context.render_context[self.cycle_node] = cycle_iter
context[self.variable_name] = next(cycle_iter).resolve(context)
return ''
#register.tag
def reset_cycle(parser, token, escape=False):
args = token.split_contents()
if len(args) != 2:
raise TemplateSyntaxError("'reset_cycle' tag requires exactly one argument")
name = args[1]
if not hasattr(parser, '_namedCycleNodes'):
raise TemplateSyntaxError("No named cycles in template. '%s' is not defined" % name)
if name not in parser._namedCycleNodes:
raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
return ResetCycleNode(name, parser._namedCycleNodes[name])
And then use it in template like this:
{% cycle 'clear tco1' 'tco' 'tco' 'tco2' 'clear tce1' 'tce' 'tce' 'tce2' as cellclass silent %}
{% for item in items %}
{% ifchanged item.st2 %}
<div class="clear"></div>
<div class="item-title">{{ item.get_st2 }}</div>
{% reset_cycle cellclass %}
{% endifchanged %}
<div class="cell {{ cellclass }}{% cycle cellclass %}">{{ item.name }}</div>
{% endfor %}

This is an old question, but as of Django 1.11 there is a built-in tag, resetcycle that appears to do what OP wants.

Related

How do i implement a working if Statement in twig? mine isnt working

im trying to do a simple IF statement while templating with twig. Appears that it gets ignored and the following code is read everytime. Should be Pretty simple but does just not work.
{% extends "_layout" %}
{% block content %}
<div class="box">
{% set x = 1 %}
{% for entry in craft.entries.section('news') %}
{% if x % 3 == 0 %}
<div class="row">
</div>
{% endif %}
{% set x =+ 1 %}
<div class="beitrag col l4 m12 s12">
<h3 class="beitrag_titel">{{ entry.title }}</h3> <p>{{ entry.textnews }}</p>
</div>
{% endfor %}
</div>
{% endblock %}
i want the code to do a new "row" div every third entry. All he does is do a new one every time.
thx for the answers
Your assignment of x is wrong. there is no postincrement in twig.
At this point your just setting x to +1 every time. You should be using {% set x = x + 1 %}
Some notes, it not necessary to keep track of an extra counter in the for loop as you could use loop.index or loop.index0, starting at 1 and 0 respectively.
Also you could use the test is divisible by for more readability
{% for i in 0..10 %}
{% if loop.index is divisible by(3) %}
{{ loop.index }} is divisible by 3
{% endif %}
{% endfor %}
demo
To solve html issues with rows you could also use batch
<div class="box">
{% for row in data|batch(3) %}
{% for value in row %}
<div class="beitrag col l4 m12 s12">
{{ value }}
</div>
{% endfor %}
<div class="row"></div>
{% endfor %}
</div>
demo
From your (deleted) comment, if you wanted the content to go inside the row, u 'd need to adapt the code as following
<div class="box">
{% set x = 1 %}
{% for entry in entries %}
{% if loop.first %}
<div class="row">
{% endif %}
<div class="col l4 m12 s12">
<h3 class="beitrag_titel">{{ entry.title }}</h3>
<p>{{ entry.textnews }}</p>
</div>
{% if x is divisible by(3) and not loop.last %}
</div>
<div class="row">
{% endif %}
{% set x = x + 1 %}
{% if loop.last and entries|length is not divisible by(3) %}
</div>
{% endif %}
{% endfor %}
</div>
demo
But then you are def better of with batch
<div class="box">
{% for row in entries|batch(3) %}
<div class="row">
{% for value in row %}
<div class="beitrag col l4 m12 s12">
<h3>{{ value.title }}</h3>
<p>{{ value.textnews }}</p>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
demo

mathematical operations on the variable product.has_default_option

I need to perform mathematical operations on the variable product.has_default_option.
how can I do?
this is my snippet:
{% for product in products %}
<li class="{{ product.css_class }}">
<a href="{{ product.url }}">
<img alt="Image of {{ product.name | escape }}" src="{{ product.image | product_image_url | constrain: '900' }}">
<b style="font-family: 'Bevan', cursive;text-transform: uppercase;font-size: large;">{{ product.name }}</b>
<i style="font-family: 'Bevan', cursive;text-transform: uppercase;font-size: medium;">{{ product.default_price | money: theme.money_format }}</i>
<p> {{ product.default_price * 2 | money: theme.money_format }}</p>
{% case product.status %}
{% when 'active' %}
{% if product.on_sale %}<em>On Sale</em>{% endif %}
{% when 'sold-out' %}
<em>Sold Out</em>
{% when 'coming-soon' %}
<em>Coming Soon</em>
{% endcase %}
</a>
</li>
{% endfor %}
You can use the times filter, so to multiply that variable by 2 you'd do the following:
{{ product.default_price | times: 2 }}

Django template iterating a queryset with a for loop counter

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 %}

Jekyll - Create an index of lists ordered by tag

i would like to create an index of lists in jekyll of all tags and the corresponding posts.
My first approach, for-loops for every tag:
<h4>FRUITY</h4>
{% for post in site.tags.fruity %}
<ul class="posts">
<li>
{{ post.title }}
</li>
</ul>
{% endfor %}
<h4>SWEET</h4>
{% for post in site.tags.sweet %}
<ul class="posts">
<li>
{{ post.title }}
</li>
</ul>
{% endfor %}
...
This WORKS, but i have to manually create a new for-loop for every new tag.
My second approach, one loop to automatize this step so i dont have to worry about new or removed tags:
{% for tag in site.tags %}
<div class="medium-6 large-3 columns list-view-all">
<h4>{{ tag | first | upcase}}</h4>
{% for post in site.posts contains tag %}
<ul class="posts">
<li>
{{ post.title }}
</li>
</ul>
{% endfor %}
</div>
{% endfor %}
The first part works, every tag becomes a header.
in the inner for-loop ALL posts containing any tag get listed. So the lists for every tag are equal.
The Question:
How can i make a list for every tag with only the posts corresponding to the respective tag?
Thanks for your help!
{% for tag in site.tags %}
<div class="medium-6 large-3 columns list-view-all">
{% assign currentTag = tag | first %}
<h4>{{ currentTag | upcase}}</h4>
{% for post in site.posts %}
{% if post.tags contains currentTag %}
<ul class="posts">
<li>
{{ post.title }}
</li>
</ul>
{% endif %}
{% endfor %}
</div>
{% endfor %}

How to add a class to a menu item that has children (nested pages) in Django-CMS

I'm working on a project where there's some specific styling on a menu item which has nested children. The menu structure looks something like this
Home
|
About
|
Services
|_ web design
|_ social marketing
|_ traditional marketing
I'm using {% show_menu 0 100 100 100 "menu.html" %} in my template and I have the following inside my menu.html:
{% load menu_tags %}
{% for child in children %}
<li class="{% if child.selected %}active{% endif %}{% if child.sibling %}dropdown{% endif %}">
{{ child.get_menu_title }}
{% if child.children %}
<ul>
{% show_menu from_level to_level extra_inactive extra_active template "" "" child %}
</ul>
{% endif %}
</li>
{% endfor %}
I put {% if child.sibling %}dropdown{% endif %} in there to illustrate where I want the class to be added, but targeting it as a child.sibling is not the right way of doing it. Is there a way to target the specific dropdown like this {% if child.has_children %}dropdown{% endif %}?
Thanks
{% if child.children %}...{% endif %}