title: jinja2模板 date: 2019-12-9 12:21:02
tags:

Jinja2模板

模板介绍

  • 在渲染模板的时候,默认会从项目的根目录下templates目录下查找模板
  • 如果不想把模板文件放在templatest目录下,那么就在Flask初始化的时候使用templates_folder`来指定模板路径
  1. app = Flask(__name__, template_folder=r'D:\workspaces\Pythonspace\flask_learn\templates')
  2. @app.route('/posts/')
  3. def posts():
  4. return render_template('posts/post_demo.html')

post_demo.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>帖子</title>
  6. </head>
  7. <body>
  8. 这是帖子页面
  9. </body>
  10. </html>

模板传参

def param():
    context = {
        'username' :'tim',
        'age':'18',
        'sex':'female'
    }
    return render_template('template/template_param.html', **context)

template_param.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板传参</title>
</head>
<body>
    模板传参:<br/>
    <table  width="200" border="1" cellspacing="0" cellpadding="0">
        <tr>
            <td>username</td>
            <td>age</td>
            <td>sex</td>
        </tr>
        <tr>
            <td>{{username}}</td>
            <td>{{age}}</td>
            <td>{{sex}}</td>
        </tr>
    </table>
</body>
</html>

模板URL_FOR

@app.route('/')
def index():
    return render_template('template/template_url_for.html')

@app.route('/login/', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        return 'login succes!'

template_url_for.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>url_for跳转</title>
</head>
<body>
    <p><a href=" {{url_for('login')}} "> 登陆 </a></p>
</body>
</html>

过滤器

http://docs.jinkan.org/docs/jinja2/templates.html#builtin-filters

过滤器是通过管道符号( | )进行使用的,例如:{{ name | length}},将返回name的长度。过滤器相当于一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后将结果渲染到页面中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。

  • abs(value):返回一个数的绝对值。 例如:-1|abs

  • default(value, default_value, boolean=false):如果当前的变量没有值,则使用参数中的值来代替。 name|default('ivy') — 如果name不存在,则使用ivy来代替。boolean=false默认是在这个变量为undefined的时候才会使用的default的值。当这个变量又被定义,但是这个变量为Flase(None , False, 及其各种数据类型的空类型)时,会返回相应的值,这时候只要将boolean=True,就会渲染默认的值。

  • or{{ value1 or value2 }} 和python自带的or一样。

  • escape(value)e:转义字符,会将 < 、> 等符号转义成html中的符号 content|escape 或者 content|e

  • safe(value):关闭自动转义 content|safe

  • first(value):返回一个序列的第一个元素。 name|first

  • format(value, *args, **kwargs) : 格式化字符串:

  • {{ "%s"_"%s"|format('hello', 'world') }} 输出 : hello_world
    
  • last(value): 返回一个序列的最后一个元素 name|last

  • length(value):返回一个序列或者字典的长度name|length

  • join(value, format_key):跟python的join一样

  • int(value): 转换为int整型

  • float(value):转换为float类型

  • lower(value):转换为小写

  • upper(upper): 转换为大写

  • replace(value, old, new):字符串替换

  • truncate(value, length=255, killwords=False): 截取length长度的字符串

  • striptags(value):删除字符串中的所有html标签,如果出现空格,将替换成一个空格

  • trim: 截取字符串前面和后面的空白字符串

自定义过滤器

过滤器本质上就是一个函数,如果在模板中调用这个过滤器,那么就会将这个变量的值作为第一个参数传给过滤器函数,然后函数的返回值会作为这个过滤器的返回值。

@app.template_filter('cut')
def cut(value):
    value = value.replace('hello','')
    return value

if / for 语句

在渲染过程中使用实际值替换占位符,只是Jinja2在模板文件中支持的诸多强大操作之一。

< -模板也支持在`{%...%}`块内使用控制语句 ->
<html>
    <head>
        {% if title %}
        <title>{{ title }} - Microblog</title>
        {% else %}
        <title>Welcome to Microblog!</title>
        {% endif %}
    </head>
    <body>
        <h1>Hello, {{ user.username }}!</h1>
    </body>
</html>
@app.route('/index')
def index():
    user = {'username': 'Miguel'}
    posts = [
        {
            'author': {'username': 'John'},
            'body': 'Beautiful day in Portland!'
        },
        {
            'author': {'username': 'Susan'},
            'body': 'The Avengers movie was so cool!'
        }
    ]
    return render_template('index.html', title='Home', user=user, posts=posts)

我使用了一个列表来表示用户动态,其中每个元素是一个具有authorbody字段的字典。 未来设计用户和其动态时,我将尽可能地保留这些字段名称,以便在使用真实用户和其动态的时候不会出现问题。

在模板方面,我必须解决一个新问题。 用户动态列表拥有的元素数量由视图函数决定。 那么模板不能对有多少个用户动态进行任何假设,因此需要准备好以通用方式渲染任意数量的用户动态。

Jinja2提供了for控制结构来应对这类问题:

<html>
    <head>
        {% if title %}
        <title>{{ title }} - Microblog</title>
        {% else %}
        <title>Welcome to Microblog</title>
        {% endif %}
    </head>
    <body>
        <h1>Hi, {{ user.username }}!</h1>
        {% for post in posts %}
        <div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div>
        {% endfor %}
    </body>
</html>

宏和import语句

模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将一些经常用到的代码片段放到宏中,然后把一些不固定的值抽取出来当成一个变量,以下将用一个例子来进行解释:

{% macro input(name, value='', type='text') %}        
    <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}">    
{% endmacro %}

以上例子可以抽取出了一个input标签,指定了一些默认参数。那么我们以后创建input标签的时候,可以通过他快速的创建:

<p>{{ input('username') }}</p>    
<p>{{ input('password', type='password') }}</p>

在真实的开发中,会将一些常用的宏单独放在一个文件中,在需要使用的时候,再从这个文件中进行导入。import语句的用法跟python中的import类似,可以直接import...as...,也可以from...import...或者from...import...as...,假设现在有一个文件,叫做forms.html,里面有两个宏分别为inputtextarea,如下:

forms.html:    
    {% macro input(name, value='', type='text') %}        
        <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">    
    {% endmacro %}     
    {% macro textarea(name, value='', rows=10, cols=40) %}        
        <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">{{ value|e }}</textarea>    
    {% endmacro %}

  1. import...as...形式:
    {% import 'forms.html' as forms %}
     <dl>  
         <dt>Username</dt>  
            <dd>{{ forms.input('username') }}</dd>  
         <dt>Password</dt>  
         <dd>{{ forms.input('password', type='password') }}</dd>
     </dl>
     <p>{{forms.textarea('comment') }}</p>
    
  1. from...import...as.../from...import...形式:
    {% from 'forms.html' import input as input_field, textarea %}
     <dl>  
         <dt>Username</dt>  
         <dd>{{ input_field('username') }}</dd>  
         <dt>Password</dt>  
         <dd>{{ input_field('password', type='password') }}</dd>
     </dl>
     <p>{{ textarea('comment') }}</p>
    

另外需要注意的是,导入模板并不会把当前上下文中的变量添加到被导入的模板中,如果你想要导入一个需要访问当前上下文变量的宏,有两种可能的方法:

  • 显式地传入请求或请求对象的属性作为宏的参数。
  • 与上下文一起(with context)导入宏。

与上下文中一起(with context)导入的方式如下:

{% from '_helpers.html' import my_macro with context %}

set/with语句

  • set语句
    在模板中可以使用set语句来定义变量:
    {% set username='tim'%}
    <p> name:{{ username }} <p>
    
  • with语句
    with语句定义的变量,只能在with代码块中使用
    {% with uasername='tim' %}
      <p> name:{{ username }} <p>
    {% endwith%}
    

加载静态文件

静态文件主要包括cssJavaScript等脚本文件、图片等,在加载文件的时候主要是用url_for方法。

  • 语法
{{ url_for('文件夹', filename='调用文件路径及扩展名') }}

模板继承

绝大多数Web应用程序在页面的顶部都有一个导航栏,其中带有一些常用的链接,例如编辑配置文件,登录,注销等。我可以轻松地用HTML标记语言将导航栏添加到index.html模板上,但随着应用程序的增长,我将需要在其他页面重复同样的工作。尽量不要编写重复的代码,这是一个良好的编程习惯,毕竟我真的不想在诸多HTML模板上保留同样的代码。

Jinja2有一个模板继承特性,专门解决这个问题。从本质上来讲,就是将所有模板中相同的部分转移到一个基础模板中,然后再从它继承过来。

所以我现在要做的是定义一个名为base.html的基本模板,其中包含一个简单的导航栏,以及我之前实现的标题逻辑。 您需要在模板文件app/templates/base.html中编写代码如下:

<html>
    <head>
      {% if title %}
      <title>{{ title }} - Microblog</title>
      {% else %}
      <title>Welcome to Microblog</title>
      {% endif %}
    </head>
    <body>
        <div>Microblog: <a href="/index">Home</a></div>
        <hr>
        {% block content %}{% endblock %}
    </body>
</html>

在这个模板中,我使用block控制语句来定义派生模板可以插入代码的位置。 block被赋予一个唯一的名称,派生的模板可以在提供其内容时进行引用。

通过从基础模板base.html继承HTML元素,我现在可以简化模板index.html了:

{% extends "base.html" %}

{% block content %}
    <h1>Hi, {{ user.username }}!</h1>
    {% for post in posts %}
    <div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div>
    {% endfor %}
{% endblock %}

自从基础模板base.html接手页面的布局之后,我就可以从index.html中删除所有这方面的元素,只留下内容部分。 extends语句用来建立了两个模板之间的继承关系,这样Jinja2才知道当要求呈现index.html时,需要将其嵌入到base.html中。 而两个模板中匹配的block语句和其名称content,让Jinja2知道如何将这两个模板合并成在一起。 现在,扩展应用程序的页面就变得极其方便了,我可以创建从同一个基础模板base.html继承的派生模板,这就是我让应用程序的所有页面拥有统一外观布局而不用重复编写代码的秘诀。