Python101: 21. Web Development Jinja2 Template Engine

post thumb
Python
by Admin/ on 19 Jul 2021

Python101: 21. Web Development Jinja2 Template Engine

In the previous article, we briefly introduced Flask, a Python web development framework, and learned how to write a Hello World, but we are still a long way from developing a real project with Flask.

The role of templates


What is a template used for? Templates are used to generate the corresponding Html text more efficiently, without templates, you can write it by hand, such as the hello world example before, write a paragraph of html code:

<h1>Hello world!</h1>

It’s okay for simple exercises, but for large scale, highly dynamic projects, it’s a bit of a stretch to write this way, i.e., not conducive to project and productization. So what are the benefits of templates.

  • Can make presentation logic and business logic Presentation logic, i.e. UI, is what is used to show and operate to users, and business logic is business rules, such as what conditions can be registered and what permissions can be tested. The template encapsulates the presentation logic and the business logic is written in the view function.
  • Makes the project easier to maintain Due to the separation of presentation logic and business logic, they can be maintained by different developers and there will be no code conflicts
  • Makes the project more secure In doing interactive development, there is a principle: Never trust user input because malicious users may inject through input (we can talk about injection separately when we have a chance later), while templates will be anti-injection to some extent, for example, if a user enters a bit of html code as input, by default the template will replace it with web-safe characters to prevent malicious injection.
  • Can improve development efficiency With a template, it is equivalent to a function that displays logic, so it can be reused, and can be used in different view functions, and in different projects

Think about. The presentation logic and business logic mentioned above, why not just say frontend and backend? If you have answers and ideas, feel free to leave a comment to discuss.

Jinja2 Template Engine

Jinja2 is the default template engine supported by the Flask framework, not the only and not the best (varies from person to person, no best) template engine, different Web frameworks, such as Django, Nodejs, etc. have their own template engine, and even some programmers implement their own template engine (I have done so), but the general idea is the same, is to replace data or This technology is not new, in the previous printing templates, such as crystal reports, there is nothing more than the markup and syntax is different, so we need to learn from each other.

Introduce rendering functions

Like other functions, to use the template engine, first introduce the

from flask import render_template

Note: To place the template file in the templates folder under the root of the project (the path shown in print(__file__))

For example, the template file hello.html would be.

{% raw %}
<h1>Hello {{ name }} </h1>
{% endraw %}

The view function can be written as :

@app.route('/user/<name>')
def index(name):
    return render_template('hello.html', name=name)

The render_template function provided by Flask integrates the Jinja2 template engine into the application. The first argument to the render_template function is the filename of the template, and the subsequent arguments are key-value pairs that represent the corresponding real values of the variables in the template.

Variables

A template file is just a normal text file, and then the part to be replaced is marked with double curly brackets ( {{ }} ) in which the variable name to be replaced is indicated, and this variable supports basic data types, as well as lists, dictionaries, objects, and tuples. As in the template template.html:

{% raw %}

<p> A value form a string: {{ name }}. </p>
<p> A value form a int: {{ myindex }}. </p> <p> A value form a list: {{ myindex }}.
<p> A value form a list: {{ mylist[3]] }}. </p> <p> A value form a list: {{ mylist[3] }}.
<p> A value form a list, with a variable index: {{ mylist[myindex] }}. </p> <p
<p> A value form a dictionary: {{ mydict['key'] }}. </p> <p> A value form a dictionary: {{ mydict['key'] }}.
<p> A value form a tuple: {{ mytuple }}. </p> <p> A value form a tuple: {{ mytuple }}.
<p> A value form a tuple by index: {{ mytuple[myindex] }}. </p>

{% endraw %}

View function code:

@app.route('/template/')
def template():
    name = 'Jinja2 template engine'
    myindex = 1
    mylist = [1,2,3,4]
    mydict = {
       key: 'age',
       value: '25'
    }
    mytuple = (1,2,3,4)
    return render_template('template.html', name=name, myindex=myindex, mylist=mylist, mydict=mydict, mytuple=mytuple)

Filter

There are times when you need to do something special with the values you want to replace in the template, such as capitalizing the first letter, removing spaces before and after, etc. One option is to use a filter.

Description

In the Jinjia2 template engine, filters are similar to pipes in Linux commands, such as capitalizing the first letter of a string variable

{% raw %}

<h1>{{ name | capitalize}}</h1>

{% endraw %}

Filters can be spliced, as can the linux pipeline command, e.g., to capitalize values and remove whitespace before and after.

{% raw %}

<h1>{{ name | upper | trim }}</h1>

{% endraw %}

As in the code above, the filter and the variable are connected by the pipe symbol |, which is equivalent to further processing of the variable value.

Some common filters

filter description
safe rendering is not escaped
capitalize initial capitalization
lower all letters lowercase
upper all letters uppercase
title Capitalize the first letter of each word in the value
trim removes the first blank character
striptags removes all HTML tags from the value when rendering

Note: safe filter, by default, Jinja2 will escape all variables for security reasons, for example, if a variable has the value <h1>Hello</h1>, Jinja2 will render it as &lt;h1&gt;Hello&lt;/&gt;, the browser will display the original value, but will not interpret it. If you want the browser to interpret it, you can use the safe filter For example, the template file html.html is:

{% raw %}

<h1>{{ html | safe }}</h1>

{% endraw %}

The view function is.

@app.route('/html')
def html():
    return render_template('html.html', html='<b>bob</b>')

**Note: **Never use safe filters on untrustworthy values, such as the text the user enters on a form.

There are also some useful filters

  • default, which provides a default value when the variable is undefined, or false, False and null (none) if you want to treat them as undefined, you need to provide a second argument of true
{% raw %}

<! -- Provides default value filter -->
<h1>Hello {{ name | default('world') }}! </h1>

<! -- Treat false, False and null (none) as undefined default filters -->
<h1>Hello {{ name | default('world', true)! }}</h1>

{% endraw %}

When the variable name is undefined, the top and bottom will be the same, and when the value is none, the top will show Hello none!, and the bottom will show Hello world!.

  • List filters min, max, get the minimum or maximum value in the list

Custom Filters

Although there are many filters, there are always times when they do not meet the needs, such as indenting the first line of text, converting the amount to Chinese uppercase, and so on. A filter is essentially a function, so first, define a filter function, and second, register it in Jinjia2’s filter.

# Define a filter function
def mylen(arg):# implement a function that can find the length
    return len(arg)
def interval(test_str, start, end): # Return the contents of the specified interval in the string
    return test_str[int(start):int(end)]

# Register filters
env = app.jinja_env
env.filters['mylen'] = mylen
env.filters['interval'] = interval

# View functions
@app.route('/myfilter')
def myfilter():
    return render_template('myfilter.html', phone='13300000000')

Template files

{% raw %}

<h1>The phone number is: {{ phone }}, length is: {{ phone | mylen }}, carrier number: {{ phone | interval(0,3) }}</h1>

{% endraw %}

The filter registration code can also be written in the initialization code __init__.py

Control structure

Many times, a smarter template rendering is needed, i.e., the ability to program the rendering, such as a style for boys and the same style for girls, and control structure instructions need to be specified with command markers, and some simple control structures are described below

Conditions

i.e. if-else control structure in the template

{% raw %}

{% if gender=='male' %}
    Hello, Mr {{ name }}
{% else %}
    Hello, Ms {{ name }}
{% endif %}

{% endraw %}

View Functions

@app.route('/hello2/<name>/<gender>')
def hello2(name, gender):
    return render_template('hello2.html', name=name, gender=gender)

In the control structure, the code syntax is the same as python

loop

Loops are useful for rendering lists, and the loop is marked with for. For example, the contents of the prize list are displayed in ul

{% raw %}

<ul>
{% for name in names %}
    <li>{{ name }} </li>
{% endfor %}
</ul>

{% endraw %}

For example, given a list of students, display it as an unordered list ul

Macros - functions in templates

A macro can be defined in the template, which is equivalent to defining a function that can be reused to make the logic clearer. First, define a macro :

mymacro.html

{% raw %}

{% macro render_name(name) %}
    <li>{{ name }}</li>
{% endmacro %}

{% endraw %}

Then use a macro, for example, in the example of the loop structure, where the name is displayed, to call the macro

{% raw %}

<ul>
    {% for name in names %}
        {{ render_name(name) }}
    {% endfor %}
</ul>

{% endraw %}

Calling a macro is the same as calling a function, but the code is written inside {{}} double curly brackets. Generally we keep the macros in a separate file for reuse, and refer to them where we need to use them

{% raw %}

{% import 'mymarco.html' as macros %}
<ul>
    {% for name in names %}
        {{ macros.render_name(name) }}
    {% endfor%}
</ul>

{% endraw %}

As mentioned above, introducing macro definition files with improt, specifying aliases via as, is the same as introducing modules with python. Specifying an alias is a good programming convention to visualize a complex thing while acting like a namespace and effectively avoiding conflicts.

include

Alternatively, multiple template fragments can be written to a single file and then included ( include ) in all templates to improve development efficiency:

{% raw %}

{% include 'common.html' %}

{% endraw %}

include into the file, which is equivalent to copying the contents of the file to the include location, so you need to consider carefully before using it

Template inheritance

If you think include is too dumb and inflexible, Jinja2 template engine has a more advanced feature - inheritance. Similar to the inheritance of classes in Python code, let’s take a look. First define a base class, base.html:

{% raw %}

<html>
<head>
    {% block head %}
    <title>{% block title %}{% endblock%} - My Application</title>
    {% endblock %}
</head> {% endblock %}
<body> {% block body %}
    {% block body %}
        <h3>This is the content of the base class</h3>
    {% endblock %}
</body>
</html>

{% endraw %}

Each blcok tag needs to specify a special name, such as head, title, etc., so that the subclass can be refactored with a specific name. In addition, the block tag needs to have an end tag endblock, similar to the curly brackets in C-like language, but of course the block tag can be nested. Next, define a subclass template hello3.html.

{% raw %}

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style></style>
{% endblock %}
{% block body %}
    {{ super() }}
    <h3>This is the content of the subclass Hello world!</h>
{% endblock %}

{% endraw %}

Use the extends tag to specify the base class to be inherited, and then use the block tag to set the subclass to replace the contents of the base class, as long as the name specified by block is the same. Alternatively, if you don’t need to replace the base class completely, you can call the super method in the subclass block to get the contents of the base class under this name, which allows more flexibility.

Summary


Today, we introduce the basic usage and features of Jinja2 template engine, and hope that through the different features, you can understand the basic usage of templates, so that you can use it faster and learn more in-depth content. In addition, I want to illustrate the basic features of templates through the Jinja2 template engine, so that you can learn other good templates by analogy and by example, and also want to show that templates can be used not only in web development, but also in automated coding, testing and many other areas.

Finally, at the beginning of this chapter, I left a question for you: why not refer to presentation logic and business logic as frontend and backend? If you have an answer, feel free to share it in the comments.


Reference

comments powered by Disqus