Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looping over a tuple in jinja2

Tags:

python

jinja2

I have a list of date in format ['DD', 'MM', 'YYYY'], and saved it to a tuples called listdates [['DD', 'MM', 'YYYY'], ['DD', 'MM', 'YYYY']]

I wanted to make a html like this

<li class="year">
    <a href="#">2013</a>
    <ul>
    <li class="month">
        <a href="#">11</a>
        <ul>
            <li class="day">01</li>
            <li class="day">02</li>
            <li class="day">03</li>
            ...
        </ul>
    </li>
    <li class="month">
        <a href="#">12</a>
        <ul>
            <li class="day">01</li>
            <li class="day">02</li>
            ...
        </ul>
     </li>
     </ul>
</li>

I've tried this for a day but hasn't found a way. Is there an easy way to do this ? or should I change the data structure ?

like image 868
user1695285 Avatar asked Jan 12 '23 19:01

user1695285


2 Answers

You should change the data structure. Complex data processing like this belongs in Python not in templates. You'll find there probably are ways to hack it in Jinja 2 (though probably not in Django's templates). But you should not do that.

Instead create a nested data structure

dates = [[d1, m1, y1], ..., [dn, mn, yn]]
datedict = {}
for d, m, y in dates:
    yeardict = datedict.setdefault(y, {})
    monthset = yeardict.setdefault(m, set())
    monthset.add(d)

nested_dates = [(y, list((m, sorted(days))
                         for m, days in sorted(yeardict.items())))
                for y, yeardict in sorted(datedict.items())]

so if dates starts out as

dates = [[1, 2, 2013], [5, 2, 2013], [1, 3, 2013]]

nested_dates will end up as

[(2013, [(2, [1, 5]), (3, [1])])]

so you can do

{% for year in nested_dates %}
    <li class="year">
        <a href="#">{{year.0}}</a>
        <ul>
        {% for month in year.1 %}
            <li class="month">
                <a href="#">{{month.0}}</a>
                <ul>
                {% for day in month.1 %}
                    <li class="day">{{day}}</li>
                {% endfor %}
                </ul>
            </li>
        {% endfor %}
        </ul>
    </li>
{% endfor %}

NB: That list comprehension is pushing the limits of what you should do in a list comprehension, if you want your code to make any sense later on, or to another programmer. So you could write it more clearly as:

nested_dates = []
for y, yeardict in sorted(datedict.items()):
    yearlist = []
    for m, days in sorted(yeardict.items()):
        yearlist.append((m, sorted(days)))
    nested_dates.append((y, yearlist))

In general the answer to any question that begins "How do I get my templating system to output data in this structure", is "Give it data in that structure".

like image 92
Ian Avatar answered Jan 21 '23 10:01

Ian


Why not just use pythons built-in date data type?

from datetime import date

# list your dates    
l = [date(2013, 12, 1), date(2013, 8, 28), ]
l.sort()

template = env.get_template('mytemplate.html')
print template.render(dates=l)

Template:

{% for year_group in dates|groupby('year') %}
{% for by_year in year_group.list %}
<li class="year">
    <a href="#">by_year.year</a>
    <ul>
    {% for month_group in by_year|groupby('month') %}
    {% for by_month in month_group.list %}
    <li class="month">
        <a href="#">by_month.month</a>
        <ul>
        {% for day_group in by_month|groupby('day') %}
        {% for by_day in day_group.list %}
              <li class="day">by_day.day</li>
        {% endfor %}
        {% endfor %}
        </ul>
    </li>
    {% endfor %}
    {% endfor %}
    </ul>
</li>
{% endfor %}
{% endfor %}
like image 37
bcorso Avatar answered Jan 21 '23 09:01

bcorso