后端框架之Flask--模板

1、模板

在前面的示例中,视图函数的主要作用是生成请求的响应,这是最简单的请求。实际上,视图函数有两个作用:处理业务逻辑和返回响应内容。在大型应用中,把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本。本节学到的模板,它的作用即是承担视图函数的另一个作用,即返回响应内容。

  • 模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
  • 使用真实值替换变量,再返回最终得到的字符串,这个过程称为“渲染”
  • Flask是使用 Jinja2 这个模板引擎来渲染模板

使用模板的好处:

  • 视图函数只负责业务逻辑和数据处理(业务逻辑方面)
  • 而模板则取到视图函数的数据结果进行展示(视图展示方面)
  • 代码结构清晰,耦合度低

1、1     Jinja2 和 模板语言:

  • Jinja2:是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask内置的模板语言。
  • 模板语言:是一种被设计来自动生成文档的简单文本格式,在模板语言中,一般都会把一些变量传给模板,替换模板的特定位置上预先定义好的占位变量名。

1、2    渲染模版函数:

  • Flask提供的 render_template 函数封装了该模板引擎
  • render_template 函数的第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量对应的真实值。

1、3    模板语言的使用:

  • {{}} 来表示变量名,这种 {{}} 语法叫做变量代码块
<h1>{{ post.title }}</h1>
inja2 模版中的变量代码块可以是任意 Python 类型或者对象,只要它能够被 Python 的 str() 方法转换为一个字符串就可以,比如,可以通过下面的方式显示一个字典或者列表中的某个元素:
{{your_dict['key']}}
{{your_list[0]}}
  • 用 {%%} 定义的控制代码块,可以实现一些语言层次的功能,比如循环或者if语句
{% if user %}
    {{ user }}
{% else %}
    hello!
<ul>
    {% for index in indexs %}
    <li> {{ index }} </li>
    {% endfor %}
</ul>
  • 使用 {# #} 进行注释,注释的内容不会在html中被渲染出来
{# {{ name }} #}

2、模板的使用

  • 在项目下创建 templates 文件夹,用于存放所有的模板文件,并在目录下创建一个模板html文件temp_demo1.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
我的模板html内容
</body>
</html>
  • 设置 templates 文件夹属性以便能够在代码中有智能提示
  • 设置 html 中的模板语言,以便在 html 有智能提示
  • 创建视图函数,将该模板内容进行渲染返回
@app.route('/')
def index():
    return render_template('temp_demo1.html')
  • 代码中传入字符串,列表,字典到模板中
@app.route('/')
def index():
    # 往模板中传入的数据
    my_str = 'Hello 你好,程序员'
    my_int = 10
    my_array = [3, 4, 2, 1, 7, 9]
    my_dict = {
        'name': 'xiaoming',
        'age': 18
    }
    return render_template('temp_demo1.html',
                           my_str=my_str,
                           my_int=my_int,
                           my_array=my_array,
                           my_dict=my_dict
                           )
  • 模板中代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
我的模板html内容
<br/>{{ my_str }}
<br/>{{ my_int }}
<br/>{{ my_array }}
<br/>{{ my_dict }}

</body>
</html>
  • 运行效果
<!DOCTYPE html>

我的模板html内容 
Hello 黑马程序员 
10 
[3, 4, 2, 1, 7, 9] 
{'name': 'xiaoming', 'age': 18}
  • 相关运算,取值
<br/> my_int + 10 的和为:{{ my_int + 10 }}
<br/> my_int + my_array第0个值的和为:{{ my_int + my_array[0] }}
<br/> my_array 第0个值为:{{ my_array[0] }}
<br/> my_array 第1个值为:{{ my_array.1 }}
<br/> my_dict 中 name 的值为:{{ my_dict['name'] }}
<br/> my_dict 中 age 的值为:{{ my_dict.age }}
  • 结果
my_int + 10 的和为:20 
my_int + my_array第0个值的和为:13 
my_array 第0个值为:3 
my_array 第1个值为:4 
my_dict 中 name 的值为:xiaoming 
my_dict 中 age 的值为:18


3、过滤器

过滤器的本质就是函数。有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,而在模板中是不能直接调用 Python 中的某些方法,那么这就用到了过滤器。

使用方式:

  • 过滤器的使用方式为:变量名 | 过滤器。
{{variable | filter_name(*args)}}
  • 如果没有任何参数传给过滤器,则可以把括号省略掉
{{variable | filter_name}}

链式调用,在 jinja2 中,过滤器是可以支持链式调用的,示例如下:

{{ "hello world" | reverse | upper }}

常见内建过滤器:

字符串操作

  • safe:禁用转义
<p>{{ '<em>hello</em>' | safe }}</p>
  • capitalize:把变量值的首字母转成大写,其余字母转小写
<p>{{ 'hello' | capitalize }}</p>
  • lower:把值转成小写
<p>{{ 'HELLO' | lower }}</p>
  • upper:把值转成大写
<p>{{ 'hello' | upper }}</p>
  • title:把值中的每个单词的首字母都转成大写
<p>{{ 'hello' | title }}</p>
  • reverse:字符串反转
<p>{{ 'olleh' | reverse }}</p>
  • format:格式化输出
<p>{{ '%s is %d' | format('name',17) }}</p>
  • striptags:渲染之前把值中所有的HTML标签都删掉
<p>{{ '<em>hello</em>' | striptags }}</p>
  • truncate: 字符串截断
<p>{{ 'hello every one' | truncate(9)}}</p>

列表操作

  • first:取第一个元素
<p>{{ [1,2,3,4,5,6] | first }}</p>
  • last:取最后一个元素
<p>{{ [1,2,3,4,5,6] | last }}</p>
  • length:获取列表长度
<p>{{ [1,2,3,4,5,6] | length }}</p>
  • sum:列表求和
<p>{{ [1,2,3,4,5,6] | sum }}</p>
  • sort:列表排序
<p>{{ [6,2,3,1,5,4] | sort }}</p>

语句块过滤

{% filter upper %}
    #一大堆文字#
{% endfilter %}

4、自定义过滤器

过滤器的本质是函数。当模板内置的过滤器不能满足需求,可以自定义过滤器。自定义过滤器有两种实现方式:

  • 一种是通过Flask应用对象的 add_template_filter 方法
  • 通过装饰器来实现自定义过滤器

重要:自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。

需求:添加列表反转的过滤器

方式一

通过调用应用程序实例的 add_template_filter 方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称:

def do_listreverse(li):
    # 通过原列表创建一个新列表
    temp_li = list(li)
    # 将新列表进行返转
    temp_li.reverse()
    return temp_li

app.add_template_filter(do_listreverse,'lireverse')
方式二

用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。

@app.template_filter('lireverse')
def do_listreverse(li):
    # 通过原列表创建一个新列表
    temp_li = list(li)
    # 将新列表进行返转
    temp_li.reverse()
    return temp_li

5、模板代码复用

5.1     宏

对宏(macro)的理解:

  • 把它看作 Jinja2 中的一个函数,它会返回一个模板或者 HTML 字符串
  • 为了避免反复地编写同样的模板代码,出现代码冗余,可以把他们写成函数以进行重用
  • 需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复

宏的代码:

  • 定义宏
{% macro input(name,value='',type='text') %}
    <input type="{{type}}" name="{{name}}"
        value="{{value}}" class="form-control">
{% endmacro %}
  • 调用宏
{{ input('name' value='zs')}}
  • 这会输出
<input type="text" name="name"
    value="zs" class="form-control">
  • 把宏单独抽取出来,封装成html文件,其它模板中导入使用,文件名可以自定义macro.html
{% macro function(type='text', name='', value='') %}
<input type="{{type}}" name="{{name}}"
value="{{value}}" class="form-control">

{% endmacro %}
  • 在其它模板文件中先导入,再调用
{% import 'macro.html' as func %}
{% func.function() %}

5 、2    模板继承

模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。

  • 标签定义的内容:{% block top %} {% endblock %}
  • 相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。
  • 子模板使用 extends 指令声明这个模板继承自哪个模板
  • 父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()
模板继承使用时注意点:
  • 不支持多继承
  • 为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行。
  • 不能在一个模板文件中定义多个相同名字的block标签。
  • 当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。

5、3    包含

Jinja2模板中,除了宏和继承,还支持一种代码重用的功能,叫包含(Include)。它的功能是将另一个模板整个加载到当前模板中,并直接渲染。

{% include 'hello.html' %}

包含在使用时,如果包含的模板文件不存在时,程序会抛出TemplateNotFound异常,可以加上 ignore missing 关键字。如果包含的模板文件不存在,会忽略这条include语句。

{% include 'hello.html' ignore missing %}

6、模板中特有的变量和函数

你可以在自己的模板中访问一些 Flask 默认内置的函数和对象

config:
{{config.SQLALCHEMY_DATABASE_URI}}
sqlite:///database.db
request:
{{request.url}}
http://127.0.0.1
session:
{{session.new}}
True
g变量

在视图函数中设置g变量的 name 属性的值,然后在模板中直接可以取出

{{ g.name }}
url_for()

url_for会根据传入的路由器函数名,返回该路由对应的URL,在模板中始终使用url_for()就可以安全的修改路由绑定的URL,则不比担心模板中渲染出错的链接:

{{url_for('home')}}
/

如果我们定义的路由URL是带有参数的,则可以把它们作为关键字参数传入url_for(),Flask会把他们填充进最终生成的URL中:

{{ url_for('post', post_id=1)}}
/post/1
get_flashed_messages()

这个函数会返回之前在flask中通过flask()传入的消息的列表,flash函数的作用很简单,可以把由Python字符串表示的消息加入一个消息队列中,再使用get_flashed_message()函数取出它们并消费掉:

{%for message in get_flashed_messages()%}
    {{message}}
{%endfor%}

7、使用 Flask-WTF 实现表单

  • 配置参数,关闭 CSRF 校验
app.config['WTF_CSRF_ENABLED'] = False
模板页面:
<form method="post">
    {{ form.username.label }} {{ form.username }}<br/>
    {{ form.password.label }} {{ form.password }}<br/>
    {{ form.password2.label }} {{ form.password2 }}<br/>
    {{ form.submit }}
</form>
视图函数:
from flask import Flask,render_template, flash
#导入wtf扩展的表单类
from flask_wtf import FlaskForm
#导入自定义表单需要的字段
from wtforms import SubmitField,StringField,PasswordField
#导入wtf扩展提供的表单验证器
from wtforms.validators import DataRequired,EqualTo


app = Flask(__name__)
app.config['SECRET_KEY']='SECRET_KEY'

#自定义表单类,文本字段、密码字段、提交按钮
class RegisterForm(FlaskForm):
    username = StringField("用户名:", validators=[DataRequired("请输入用户名")], render_kw={"placeholder": "请输入用户名"})
    password = PasswordField("密码:", validators=[DataRequired("请输入密码")])
    password2 = PasswordField("确认密码:", validators=[DataRequired("请输入确认密码"), EqualTo("password", "两次密码不一致")])
    submit = SubmitField("注册")

#定义根路由视图函数,生成表单对象,获取表单数据,进行表单数据验证
@app.route('/demo2', methods=["get", "post"])
def demo2():
    register_form = RegisterForm()
    # 验证表单
    if register_form.validate_on_submit():
        # 如果代码能走到这个地方,那么就代码表单中所有的数据都能验证成功
        username = request.form.get("username")
        password = request.form.get("password")
        password2 = request.form.get("password2")
        # 假装做注册操作
        print(username, password, password2)
        return "success"
    else:
        if request.method == "POST":
            flash("参数有误或者不完整")

    return render_template('temp_register.html', form=register_form)

if __name__ == '__main__':
    app.run(debug=True)




















  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值