Flask修炼-模板-02!



内容概述:
过滤器,
自定义过滤器,
控制代码块,
模板代码复用,
特有变量和函数,
Flask-WTF 表单,
CSRF


过滤器

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

过滤器的使用方式为:变量名 | 过滤器。 {{ 变量名 | 过滤器 }}

如果没有任何参数传给过滤器,则可以把括号省略掉

在 jinja2 中,过滤器是支持链式调用的

常见内建过滤器

字符串操作
  • **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>



自定义过滤器

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

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

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

# 自定义过滤器
# 方式1: 装饰器的形式
# @app.template_filter('lireverse')
def do_lireverse(li):
    temp = list(li)
    temp.reverse()
    return temp


# 方式2: 直接添加过滤器
app.add_template_filter(do_lireverse, 'lireverse')




控制代码块

控制代码块主要包含两个:

- if/else if /else / endif
- for / endfor
{% for item in my_list if item.id != 5 %}
    {% if loop.index == 1 %}
    <li style="background-color: orange">{{ item.value }}</li>
    {% elif loop.index == 2 %}
    <li style="background-color: green">{{ item.value }}</li>
    {% elif loop.index == 3 %}
    <li style="background-color: gray">{{ item.value }}</li>
    {% else %}
    <li style="background-color: red">{{ item.value }}</li>
    {% endif %}
{% endfor %}



模板代码复用
  • 宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。
  • **继承(Block)**的本质是代码替换,一般用来实现多个页面中重复不变的区域。
  • **宏(Macro)**的功能类似函数,可以传入参数,需要定义、调用。
  • **包含(include)**是直接将目标模板文件整个渲染出来。

对宏(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() %}

模板继承

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

  • 标签定义的内容
{% block top %} {% endblock %}
  • 相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。
  • 子模板使用 extends 指令声明这个模板继承自哪个模板
  • 父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()

在子模板中使用 extends 指令声明这个模板继承自哪

模板继承使用时注意点:

  • 不支持多继承
  • 为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行。
  • 不能在一个模板文件中定义多个相同名字的block标签。
  • 当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。
{% extends 'base.html' %}

{% block contentBlock %}
    {{ super() }}
我是子类的内容<br/>
{% endblock %}

包含

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

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

{% include 'includeaaa.html' ignore missing %}<br/>
{% include 'include.html' %}<br/>



特有变量和函数

config: 可以从模板中直接访问 Flask 当前的 config 对象

**request:**就是 flask 中代表当前请求的 request 对象

session: 为 flask 的 session 对象

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

url_for():url_for会根据传入的路由器函数名,返回该路由对应的URL,在模板中始终使用url_for()就可以安全的修改路由绑定的URL,则不比担心模板中渲染出错的链接;如果我们定义的路由URL是带有参数的,则可以把它们作为关键字参数传入url_for(),Flask会把他们填充进最终生成的URL中

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

config: {{ config.DEBUG }}<br/> {# 可以从模板中直接访问Flask当前的config对象 #}
<hr/>

请求上下文中两个变量:<br/>
当前路由: {{ request.url }}<br/>
session 取值: {{ session.name }}<br/>
<hr/>

应用上下文中 1 个变量<br/>
g 变量: {{ g.name }}<br/>
<hr/>

两个可以直接使用的函数:<br/>
<a href="{{ url_for('index') }}">回到首页</a><br/>
<hr/>
获取闪现消息:<br/>

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



Flask-WTF 表单

Web 表单是 Web 应用程序的基本功能。

它是HTML页面中负责数据采集的部件。表单有三个部分组成:表单标签、表单域、表单按钮。表单允许用户输入数据,负责HTML页面数据采集,通过表单将用户输入的数据提交给服务器。

在Flask中,为了处理web表单,我们可以使用 Flask-WTF 扩展,它封装了 WTForms,并且它有验证表单数据的功能


WTForms 支持的 HTML 标准字段
字段对象说明
StringField文本字段
TextAreaField多行文本字段
PasswordField密码文本字段
HiddenField隐藏文件字段
DateField文本字段,值为 datetime.date 文本格式
DateTimeField文本字段,值为 datetime.datetime 文本格式
IntegerField文本字段,值为整数
DecimalField文本字段,值为decimal.Decimal
FloatField文本字段,值为浮点数
BooleanField复选框,值为True 和 False
RadioField一组单选框
SelectField下拉列表
SelectMutipleField下拉列表,可选择多个值
FileField文件上传字段
SubmitField表单提交按钮
FormField把表单作为字段嵌入另一个表单
FieldList一组指定类型的字段

WTForms 常用验证函数
验证函数说明
DataRequired确保字段中有数据
EqualTo比较两个字段的值,常用于比较两次密码输入
Length验证输入的字符串长度
NumberRange验证输入的值在数字范围内
URL验证URL
AnyOf验证输入值在可选列表中
NoneOf验证输入值不在可选列表中

使用 Flask-WTF 需要配置参数 SECRET_KEY。

CSRF_ENABLED是为了CSRF(跨站请求伪造)保护。 SECRET_KEY用来生成加密令牌,当CSRF激活的时候,该设置会根据设置的密匙生成加密令牌。

服务器代码

from flask import Flask, render_template, flash, request
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import InputRequired, EqualTo, DataRequired

app = Flask(__name__)
# 关闭 csrf 验证
app.config['WTF_CSRF_ENABLED'] = True
app.secret_key = 'asdfasdf'


# 自定义注册表单
class RegisterForm(FlaskForm):
    username = StringField('用户名:', validators=[InputRequired('请输入用户名')], render_kw={'placeholder': '我是占位文字'})
    password = PasswordField('密码:', validators=[InputRequired('请输入密码')])
    password2 = PasswordField('确认密码:', validators=[InputRequired('请输入确认密码'), EqualTo('password', '两次密码要一致')], )
    submit = SubmitField('注册:')


@app.route('/')
def index():
    return 'Hello World!'


@app.route('/register_wtf', methods=['GET', 'POST'])
def register_wtf():
    register_form = RegisterForm()

    # 使用 wtf 表单帮我们做验证
    if register_form.validate_on_submit():
        # 执行注册逻辑
        # 取到表单中提交上来的三个参数
        username = request.form.get("username")
        password = request.form.get("password")
        password2 = request.form.get("password2")

        # 取值方式 2
        # username = register_form.username.data

        # 假装做注册操作
        print(username, password, password2)
        return "success"
    else:
        if request.method == 'POST':
            flash('参数错误')

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


@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == "POST":
        # 取到表单中提交上来的三个参数
        username = request.form.get("username")
        password = request.form.get("password")
        password2 = request.form.get("password2")

        if not all([username, password, password2]):
            # 向前端界面弹出一条提示(闪现消息)
            flash("参数不足")
        elif password != password2:
            flash("两次密码不一致")
        else:
            # 假装做注册操作
            print(username, password, password2)
            return "success"

    return render_template('temp5_wtf.html')


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

模板代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form method="post">
    <label>用户名:</label><input type="text" name="username" placeholder="请输入用户名"><br/>
    <label>密码:</label><input type="password" name="password" placeholder="请输入密码"><br/>
    <label>确认密码:</label><input type="password" name="password2" placeholder="请输入确认密码"><br/>
    <input type="submit" value="注册">
</form>

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

<hr/><br/>
以下是使用 Flask_wtf 实现 <br/><br/><br/><br/>

<form method="post">
    {{ form.csrf_token() }}
    {{ form.username.label }}{{ form.username }}<br/>
    {{ form.password.label }}{{ form.password }}<br/>
    {{ form.password2.label }}{{ form.password2 }}<br/>
    {{ form.submit }}<br/>
</form>
</body>
</html>



CSRF

CSRF 全拼为 Cross Site Request Forgery,译为跨站请求伪造。

CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。

造成的问题:个人隐私泄露以及财产安全。

CSRF 流程:

  1. 客户端浏览并登录信任网站 A
  2. 验证通过,在用户处产生 A 的 cookie
  3. 用户在没有登出的情况下访问攻击网站 B
  4. B 会用一个诱惑性的按钮来让用户点击,但是这个点击会要求访问第三方网站 A ,发出一个请求
  5. 根据 B 的请求,浏览器要去访问 A 网站,访问 A 网站会默认带上之前保存的 cookie
  6. A 网站不知道这个请求是用户发出的还是 B 攻击网站发出的,但是浏览器过去的时候带上了 cookie,所以 A 会根据用户的权限处理这个请求,B 攻击网站的请求里一般会有请求,这样 B 就达到了模拟用户操作进行攻击的目的
防止 CSRF 攻击
  1. 在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值
  2. 在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token
  3. 在用户点击提交的时候,会带上这两个值向后台发起请求
  4. 后端接受到请求,以会以下几件事件:
    • 从 cookie中取出 csrf_token
    • 从 表单数据中取出来隐藏的 csrf_token 的值
    • 进行对比
  5. 如果比较之后两值一样,那么代表是正常的请求,如果没取到或者比较不一样,代表不是正常的请求,不执行下一步操作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值