Jinja 2 And How To Template Like A Pro

Jinja 2 is a powerful, super extensive and popular templating engine for Python web development. It gives you a simple, clean, and really extensible way to generate dynamic content in web applications. In this article, we'll look through the origins of Jinja 2 and look into some of its top features that make it a favorite among developers.

Origins of Jinja 2

Jinja 2 was created by Armin Ronacher, the same developer behind the Flask web framework. It was first released in 2008 and has since become a super useful tool for web developers working with Python based web applications. The main goal of Jinja 2 is to provide a template engine that is easy to use and also filled with a ton of features for devs who need it.

The name "Jinja" is inspired by the Japanese word 仁者 (pronounced jinja), which translates to "benevolence" or "humanity." They chose this name because it is their mission to create a tool that is considerate, humane, and friendly to developers.

Top Features of Jinja 2

1. Syntax Simplicity:

Jinja 2 provides a clean and easy syntax that is pretty close to Python's. This makes it easy for developers to learn and write templates without a steep learning curve. The template tags and expressions are intuitive, which lets developers focus on their application logic instead of spending their time understanding complex templating syntax.

2. Template Inheritance:

One of Jinja 2's standout features is template inheritance. This lets you create a base template with a common structure and elements and then extend or override specific blocks in child templates. This goes hand in hand with DRY principles and object-oriented programming commonly seen in python. Here is an example of a base template and a child that extends it.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Default Title{% endblock %}</title>
</head>
<body>
    <div id="content">
        {% block content %}{% endblock %}
    </div>
    <footer>
        {% block footer %}Default Footer{% endblock %}
    </footer>
</body>
</html>

And to use the previously defined template you would write code like this

{% extends 'base_template.html' %}

{% block title %}Child Page Title{% endblock %}

{% block content %}
    <h1>Welcome to the Child Page!</h1>
    <p>This is the content of the child page.</p>
{% endblock %}

{% block footer %}
    <p>Custom Footer for the Child Page</p>
{% endblock %}

This would end up rendering html that looks like this

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Child Page Title</title>
</head>
<body>
    <div id="content">
        <h1>Welcome to the Child Page!</h1>
        <p>This is the content of the child page.</p>
    </div>
    <footer>
        <p>Custom Footer for the Child Page</p>
    </footer>
</body>
</html>

3. Autoescaping:

Jinja 2 includes an autoescape feature that helps prevent common web vulnerabilities like Cross-Site Scripting (XSS). By default, Jinja 2 automatically escapes variables to ensure that user-generated content does not contain malicious code. Developers can control the autoescape behavior on a per-template basis.

<p>{{ user_input }}</p>

4. Filters and Tests:

Jinja 2 comes with a variety of built-in filters and tests that enhance the templating capabilities. Filters allow you to modify variables from within the template, and tests provide conditional checks. This makes it easy to format data and perform logic directly in the template. You can think about it almost exactly like conditional rendering in react but direcly in the html file.

<p>{{ user_input|default('No input') }}</p>
{% if user_is_authenticated %}
    <p>Welcome, {{ username }}!</p>
{% endif %}

5. Template Loading:

Jinja 2 supports multiple ways of loading templates, like loading from strings, files, or even remote locations. This flexibility lets you organize templates in a way that suits your project structure. Template loaders can be customized to fetch templates from a ton of sources.

from jinja2 import Environment, FileSystemLoader

env = Environment(loader=FileSystemLoader('/path/to/templates'))
template = env.get_template('index.html')

6. Custom filters:

Jinja 2 is highly extensible, it lets you create custom filters, tests, and extensions. This makes it possible to change Jinja 2 based on the specific needs of a project. A ton of third-party extensions are available, which make additional functionality like internationalization and markdown rendering. Here is an example of how I used a custom filter in my Flatiron School phase 4 project to render embeds for a post in my project.

First we have to define the custom functionality in the python code using a decorator that comes default with flask since flask comes with jinja2

@app.template_filter('format_date')
def format_date(date_str):
    # Convert the string to a datetime object
    post_datetime = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")

    # Format the datetime object as "Nov 28, 2023, 2:33 PM"
    formatted_date = post_datetime.strftime("%b %d, %Y, %I:%M %p")

    return formatted_date

This custom template takes a string and converts it to a date string, and then I used flasks built In render_template function to render a template and pass in some context, in this case the post dictionary. Now here is what the template looks like.

   <html>
    <body>
      <div class="container" onclick="window.open('http:/\/localhost:3000/home/post/{{post.id}}', '_blank')">
        <div class="post-container">
          <div class="user-pfp">
            <img
              src="http://backend.danner.repl.co/uploads/Y2x65qX.png"
              alt="danner1"
            />
          </div>
          <span class="username">{{post.username}}</span>
          <p class="post-body">{{post.content}}</p>
          <div class="info-container">
            <div class="buttons-container">
              <div><button class="like-button">&#x1F44D; {{post.likes|length}}</button></div>
              <button class="comment-button">&#x1F4AC; {{post.comments|length}}</button>
            </div>
            <div class="additional-info">
              <span class="views">{{post.views|int}} views</span>
               **This is where I use my custom formate date function**
              <span class="date">{{post.created_at|format_date}}</span>
            </div>
          </div>
        </div>
      </div>
    </body>
</html>

Conclusion

Jinja 2 has proven itself as a great and developer-friendly templating engine in the Python world. Its simplicity, template inheritance, auto escaping, and power make it an amazing tool for generating dynamic content in web applications.