Search code examples
pythondjangodjango-templatespython-datetime

Creating and rendering structure with years and months in django


In my blogging app I need a structure (created as a variable in context processor) that will store months number and corresponding year of 5 consecutive months till current one. So if current month is december, we will have year: 2010 and months: 12,11,10,9,8. If month will be january we will have years 2010: months: 1 and years: 2009 months: 12, 11, 10, 9 . My goal is to show an archive in the following form:

- 2010
    - January
- 2009
    - December
    - November
    - October
    - September

How to create it and what structure should I use ? And then how to show it ? I think I need some nested structure but which will be possible to render in django < 1.2 ?
I've started it on my own but got completely lost at some point :

now = datetime.datetime.now()

years = []
months = []
archive = []
if now.month in range(5, 12, 1):
    months = range(now.month, now.month-5, -1)        
    if months:
        years = now.year
else:
    diff = 5 - now.month
    for i in range(1, now.month, 1):
        archive.append({
                        "month": i,
                        "year": now.year,
        })

    for i in range(0, diff, 1):
        tmpMonth = 12 - int(i)
        archive.append({
                        "month": tmpMonth,
                        "year": now.year-1,
        })

    if archive:
        years = [now.year, now.year-1]

Solution

  • How to create it and what structure should I use ?

    I'd go with a list of year-month tuples. Here is a sample implementation. You'll need the handy python-dateutil library to make this work.

    from datetime import datetime
    from dateutil.relativedelta import relativedelta
    
    def get_5_previous_year_months(a_day):
        """Returns a list of year, month tuples for the current and previous 
        5 months relative to a_day"""
        current_year, current_month = a_day.year, a_day.month
        first_of_month = datetime(current_year, current_month, 1)
        previous_months = (first_of_month - relativedelta(months = months)
                for months in range(0, 5))
        return ((pm.year, pm.month) for pm in previous_months) 
    
    def get_current_and_5_previous_months():
        return get_5_previous_year_months(datetime.today())
    

    And then how to show it ?

    Here is a very simplistic way to show it. I think you can clean it up by replacing the <ul> elements with <div> and styling it appropriately.

        <ul>
        {% for year, month in previous_year_months %}
            {% ifchanged year %}
                </ul><li>{{ year }}</li><ul>
            {% endifchanged %}
                    <li>{{ month }}</li>
        {% endfor %}
        </ul>
    

    Where previous_year_months is a context variable corresponding to the result returned by get_current_and_5_previous_months.