Search code examples
pythonjinja2

Jinja2 : proper way to write [not so] complex template


I'm trying to implement a simple calendar webpage.

I'm quite new to Jinja2 but my understanding of templates is that writing HTML code in the Python sources should be avoided as templates are designed for this purpose.

The problem that I'm facing I that I don't see how to write clear templates for this project. I guess there is an overall architecture problem in my project...

My page template:

{% extends "base.html" %}
{% block title %}The title{% endblock %}
{% block content %}
    <h1>Hey</h1>
    <p> I'm happy </p>
    <div id="calendar">
        <table>
            <tr>
                {% for month in range(1, 13) %}
                    <td valign="top" align="center">{# html code for a single month goes here... #}</td>
                {% endfor %}
            </tr>
        </table>
    </div>
{% endblock %}

Each month template is

<table>
    <th>{{ month_name }}</th>
    {% for day_number in days %}
        <tr><td>{{ day_number }}</td><td>{{ weekday }}</td></tr>
    {% endfor %}
</table>

Finally, I have a Python class for a calendar that basically provides helper functions to calculate the days of the month:

class Calendar:
    def __init__(self, year):
        self.year = year

    def monthrange(self, month):
        nextmonth = month % 12 + 1
        nextyear = self.year + 1 if nextmonth == 1 else self.year
        firstday = datetime.date(self.year, month, 1)
        lastday = datetime.date(nextyear, nextmonth, 1)
        return (1, (lastday - firstday).days)

    def itermonthdates(self, month):
        first, last = self.monthrange(month)
        for i in range(first, last + 1):
            yield datetime.date(self.year, month, i)

    def tohtml(self):
        def month_to_html(month):
            # !!! This code generate HTML but it should not !!!
            s = '<table>\n'
            s += '<th>{}</th>'.format(MONTHS[month - 1])
            for day in self.itermonthdates(month):
                weekday = WEEKDAYS[day.weekday()]
                d = {'day': day.day, 'weekday': weekday}
                s += '<tr><td>%(day)02d</td><td>%(weekday)s</td></tr>\n' % d
            s += '</table>\n'
            return s

        template_loader = jinja2.FileSystemLoader(searchpath='templates')
        template_env = jinja2.Environment(loader=template_loader)
        template = template_env.get_template('template.html')
        print(template.render(months=[month_to_html(i) for i in range(1, 13)]))

So this code only partially work as I don't know how to use Jinja2 to render each month.

Any help would be appreciated. Ben


Solution

  • I finally think I got what you want. Don't write HTML in Python. Instead, you can create the logic to your calendar in Python then send that data to the template to be populated with Jinja. enough talk, let's code a very simple example.

    Simple Example

    here we will get the current date to be displayed in a webpage.

    logic.py

    @app.route('/')
    def index():
        import time
        current_time = time.strftime("%d/%m/%Y")
        return render_template('template.html', data = current_time)
    

    template.html

    {% if data %} # This checks if the variable "data" is set.
         <p> {{data}} </p> ## This will output the date here
    

    By applying that to your code, you can send assign any logical data ( which means it requires Python to be generated ) to variables and send them to Jinja template via adding a second argument to render_template. Hope I Understood what you wanted right this time.