Jinja2 template engine (Part 2): a core tool for reuse and extension
📂 Stage: Stage 1 - Breaking the ice and setting sail (Basics) 🔗 Related chapters: Jinja2 模板引擎(上) · 静态文件管理
In the last article, we took advantage of the three key features of Jinja2: variable injection, loop control, and branch judgment, and were able to write dynamic rendering of a single page. But if you want to build a complete website with login status, footer, and sidebar, there is still one step left - reuse. If you copy and paste the navigation bar and footer to more than a dozen pages according to index.html, it will be a disaster if the copyright year is changed one day 😤.
This article will unlock the core gameplay of Reuse and Extension: template inheritance, fragment inclusion, custom filters and global functions, combined with three-layer inheritance techniques and built-in variables, which are enough to support the template architecture of a production environment.
1. Template inheritance: the most commonly used reuse solution ✨
1.1 Why use inheritance? No more copy-pasting!
Let’s first look at the comparison of the two project structures to feel the difference between “without inheritance” and “with inheritance”:
:::success ❷ Inheritance - silky mode
:::
The core of template inheritance is to extract the public skeleton of the website into a parent template, using blocks (block) marks areas where subpages can be replaced or filled.
1.2 First build the "parent template": define the shared skeleton
The parent template is usually calledbase.html, responsible for the HTML infrastructure, public navigation, footer, globally introduced CSS/JS, etc. of the entire website. we need to use{% block 块名 %}{% endblock %}Leave areas open for changes in the future.
Block names are best made semantic, e.g.title、extra_head、content、scripts, you can know its function at a glance, making it easier for sub-templates to understand and cover it.
in parent templateblockThe content inside is the default value. If the subtemplate is not overridden, the default value will be displayed; if it is overridden, the content of the subtemplate will be used.
1.3 Then write the "subtemplate": fill/cover specific blocks
Subtemplates must be used in the first line{% extends "父模板路径" %}Declare the inheritance relationship and only cover what you care aboutblock. Don't write any extra HTML outside the block, otherwise Jinja2 will complain.
If you want to append your own stuff to the original content of the parent block, you can call it at the beginning of the child block.{{ super() }}. For example, if a subtemplate wants to add a search box after the general navigation, it can overwrite the navigation block and first write{{ super() }}, and then append your own search form.
Let's write a sub-template for the article list page to feel the refreshingness of inheritance:
Now you only have to write the really different parts of the page. Navigation, footers, error messages, and other chores are all done bybase.htmlcontract.
1.4 Advanced: Three-level inheritance (the savior of complex projects)
When the website develops to have multiple modules (such as "article module" and "user module"), and each module has its own shared sidebar or sub-navigation, a singlebase.htmlIt's not enough. At this time, you can introduce middle-level subtemplate to form a three-layer inheritance structure:
The same is used for middle-level templates{% extends "base.html" %}, in one's ownblockIt is divided again into finer-grained blocks for continued coverage by the final page template.
It is recommended that the inheritance level should not exceed 3 levels, otherwise it will become a headache to track where the block comes from and who it covers.
2. include: "Inserter" for independent fragments 📎
2.1 When to use include?
{% include %}It has a complementary relationship with inheritance. It is used to insert an independent small template fragment intact, suitable for:
- Small functional components reused on multiple pages: popular article sidebar, comment area, share button;
- Pure content block that does not need to be overridden and has no inheritance relationship.
2.2 Usage of include
First extract the independent fragments, and the file name is usually underlined._prefix, indicating that this is a "part" rather than a complete page:
Then import it directly where needed:
The advantage of this is that if you change the style or logic of the popular article component, all pages that reference it will be updated simultaneously.
3. Custom filter: "Gadget" for processing data 🔧
Jinja2 has many built-in filters (such asupper、truncate、length), but in business it is often necessary to customize the data display format - for example, convert time into "year, month, day", or truncate text according to the number of Chinese characters. At this time, you need to write the filter yourself.
3.1 Register custom filter
Taking Flask as an example, just register it in the application factory or module file. Two methods are recommended:
3.2 Use in templates
Like the built-in filter, pass|For pipeline calls, add parentheses when passing parameters:
Filters are "small tools" that should maintain a single responsibility and only convert the data display layer. Don't write complex business logic in them.
4. Custom global function: "Global Assistant" that can be called at any time 🛠️
If you want to directly call a function in the template that requires dynamic calculation and accepts parameters (such as getting the number of unread messages from the current user, getting popular tags), but you don’t want to manually query and pass it in in each view function, then it’s time to customize the global function.
4.1 Register global function
Global functions are registered toapp.jinja_env.globalsIn the dictionary, the template can be called directly as an ordinary function:
4.2 Use in templates
Just call it directly in the template, no need to pass it through the view:
Be careful when using global functions:
- Don't do complex calculations or check databases in global functions to avoid performance problems, try to use cache or reasonable queries.
- Small data that can be directly provided by the view should not be made global to avoid coupling business logic at the template layer.
5. Summary and best practices
Review of core knowledge points
Avoiding Pitfalls and Best Practices
- Inheritance level ≤ 3 levels: Maintenance costs rise sharply beyond three levels;
- Block name semantics: use
content、extra_head、scriptsrather thanblock1、block2; - Independent fragments are prefixed with an underscore: On the one hand, the semantics are clear, and on the other hand, Flask does not directly serve files starting with an underscore by default, which is safer;
- Do not abuse global variables/functions: small data that the view can pass in through the context is handed over to the view, keeping the template pure;
- Prefer using filters for date and string processing: Avoid stuffing a long string of Python code into the template, which will make the readability worse.
Now you have mastered the four magic weapons of template inheritance, component inclusion, custom filters and global functions, which are enough to meet the template architecture needs of most web projects. In the next article, we will go deep into static file management and front-end resource integration to completely open up the collaboration channel between the front and back ends, so stay tuned.
🔗 Extended reading

