Django Template System: How do I solve this looping / grouping / counting?
-
08-07-2019 - |
Question
I have a list of articles, and each article belongs to a section.
class Section(models.Model):
name = models.CharField(max_length=200)
def __unicode__(self):
return self.name
class Article(models.Model):
section = models.ForeignKey(Section)
headline = models.CharField(max_length=200)
# ...
I want to display the articles, grouped by section.
Sponsorships, Advertising & Marketing 1. Nike To Outfit All 18 Univ. Of Memphis Athletic Teams 2. Phil Jackson Questions Harrah's Signage At New Orleans Arena 3. Puma Hires N.Y.-Based Ad Agency Droga5 To Lead Global Account 4. Pizza Patrón To Replace Pizza Hut As AAC Exclusive Provider 5. Marketplace Roundup Sports Media 6. Many Patriots Fans In New England Will Not See Tonight's Game 7. ESPN Ombudsman Says Net Should Have Clarified Holtz Situation 8. EA Sports To Debut Fitness Title For Nintendo Wii In Spring '09 9. Blog Hound: Rockets-Suns Scuffle Today's No.1 Topic 10. Media Notes Leagues & Governing Bodies 11. DEI, Chip Ganassi Racing To Merge Into Four-Car Sprint Cup Team 12. NASCAR Roundtable Part II: New Strategies, Cutbacks Discussed 13. Average Ticket Price For NFL Playoff Games To Drop By 10%
I figured out how to do most of it with Django's template system.
{% regroup articles by section as articles_by_section %}
{% for article in articles_by_section %}
<h4>{{ article.grouper }}</h4>
<ul>
{% for item in article.list %}
<li>{{ forloop.counter }}. {{ item.headline }}</li>
{% endfor %}
</ul>
{% endfor %}
I just can't figure out how to do the numbers. The code above numbers the articles in Sports Media 1-5 instead of 6-10. Any suggestions?
Solution
Following Jeb's suggeston in a comment, I created a custom template tag.
I replaced {{ forloop.counter }}
with {% counter %}
, a tag that simply prints how many times it's been called.
Here's the code for my counter tag.
class CounterNode(template.Node):
def __init__(self):
self.count = 0
def render(self, context):
self.count += 1
return self.count
@register.tag
def counter(parser, token):
return CounterNode()
OTHER TIPS
This isn't exactly neat, but may be appropriate for someone:
{% for article in articles %}
{% ifchanged article.section %}
{% if not forloop.first %}</ul>{% endif %}
<h4>{{article.section}}</h4>
<ul>
{% endifchanged %}
<li>{{forloop.counter}}. {{ article.headline }}</li>
{% if forloop.last %}</ul>{% endif %}
{% endfor %}
I think you can use forloop.parentloop.counter inside of the inner loop to achieve the numbering you're after.
You could just use an ordered list instead of unordered:
{% regroup articles by section as articles_by_section %}
<ol>
{% for article in articles_by_section %}
<h4>{{ article.grouper }}</h4>
{% for item in article.list %}
<li>{{ item.headline }}</li>
{% endfor %}
{% endfor %}
</ol>