Templating With Jinja2 in Flask: Advanced

In the first part of this three-part tutorial series, we saw how to lay out the template structure in a Flask-based application using Jinja2. We also saw how blocks can be used to leverage the inheritance in templates. In this part, we will cover how to write a custom filter, a custom context processor, and a macro.

Getting Started

I will build upon the catalog application we created in the first part of this series. First I will add a custom Jinja2 context processor to show a descriptive name for each product. Then I will create a custom Jinja2 filter to do the same job as the custom context processor. Then I will demonstrate how to create a custom Jinja2 macro for regular form fields.

Creating a Custom Jinja2 Context Processor

Sometimes, we might want to calculate or process a value directly in the templates. Jinja2 maintains a notion that the processing of logic should be handled in views and not in templates, and thus, it keeps the templates clean. A context processor becomes a handy tool in this case. We can pass our values to a method; this will then be processed in a Python method, and our resultant value will be returned. Therefore, we are essentially just adding a function to the template context (thanks to Python for allowing us to pass around functions just like any other object).

So, let's say we want to add a descriptive name for each product in the format Category / Product-name. For this, a method needs to be added, which has to be decorated with @app.context_processor.

Technically, a context is just a Python dictionary that can be modified to add and remove values. Any method with the specified decorator should return a dictionary that would update the current application context.

To use this context processor, just add the following Jinja2 tag in the template.

If we add this to that flask_app/templates/product.html of our application, it would look like:

The resulting product page would now look like:

A single product page example

Creating a Custom Jinja2 Filter

After looking at the above example, experienced developers might think that it was stupid to use a context processor for the purpose. One can simply write a filter to get the same result; this will make things much cleaner. A filter can be written to display the descriptive name of the product as shown below.

This filter can be used just like a normal filter, i.e, by adding a | (pipe) symbol and then the filter name.

The above filter would yield the same result as the context processor demonstrated a while back.

To take things to a higher level, let's create a filter which will format the currency based on the current browser's local language. For this, first we need to install a Python package named ccy.

Now we need to add a method for the currency filter.

To use this filter, we need to add the following in our template:

Now the product page would look like:

Formatting currency on the single product page

Creating a Custom Jinja2 Macro for Forms

Macros allow us to write reusable pieces of HTML blocks. They are analogous to functions in regular programming languages. We can pass arguments to macros as we do to functions in Python and then use them to process the HTML block. Macros can be called any number of times, and the output will vary as per the logic inside them. Working with macros in Jinja2 is a very common topic and has a lot of use cases. Here, we will just see how a macro can be created and then used after importing.

One of the most redundant pieces of code in HTML is defining input fields in forms. Most of the fields have similar code with some modifications of style and so on. The following is a macro that creates input fields when called. The best practice is to create the macro in a separate file for better reusability, for example, _helpers.html:

Now, this macro should be imported in the file to be used:

Then, it can simply be called using the following:

It is always a good practice to define macros in a different file so as to keep the code clean and increase code readability. If a private macro that cannot be accessed out of the current file is needed, then name the macro with an underscore preceding the name.

Conclusion

In this tutorial, we have seen how to write a custom filter, a custom context processor and a custom macro for forms. In the next part of this series, we will see how to implement advanced date and time formatting at template level in Jinja2 using moment.js.

Tags:

Comments

Related Articles