模块代码复用

在模板中,可能会遇到以下情况:

  • 多个模板具有完全相同的顶部和底部内容
  • 多个模板中具有相同的模板代码内容,但是内容中部分值不一样
  • 多个模板中具有完全相同的 html 代码块内容

像遇到这种情况,可以使用 JinJa2 模板中的 宏、继承、包含来进行实现

对宏(macro)的理解:

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

使用

定义宏:

{# 宏(macro) #}
{% macro input(name,value='',type='text') %}
<input type="{{type}}" name="{{name}}"
value="{{value}}" class="form-control">
{% endmacro %}

调用宏:

{{ input('name' value='lst')}}

这样会输出:

<input type="text" name="name"
value="lst" class="form-control">

把宏单独抽出来,封装成html文件,其他模块中导入使用,文件名可以自定义macro.html:

{# 单独抽出来封装成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() }}

实际练习代码:

在使用宏之前的代码:

<form>
<label>用户名:</label><input type="text" name="username"><br/>
<label>身份证号:</label><input type="text" name="idcard"><br/>
<label>密码:</label><input type="password" name="password"><br/>
<label>确认密码:</label><input type="password" name="password2"><br/>
<input type="submit" value="注册">
</form>

定义宏macro.html:

{#定义宏,相当于定义一个函数,在使用的时候直接调用该宏,传入不同的参数就可以了#}
{% macro input(label="", type="text", name="", value="") %}
{# 细心观察,就是将需要替换的值变成一个变量,然而这个值是自己传入 #}
<label>{{ label }}</label><input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}

使用宏:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板</title>
</head>
<body>
{# 因为macro在其他文件中,因此需要先导入,在使用 #}
{% import 'macro.html' as func %} <form>
{{ func.input("用户名:", name="username") }}<br/>
{{ func.input("身份证号:", name="idcard") }}<br/>
{{ func.input("密码:", type="password", name="password") }}<br/>
{{ func.input("确认密码:", type="password", name="password2") }}<br/>
{{ func.input(type="submit", value="注册") }}
</form>
</body>
</html>

模板继承

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

标签定义的内容:

{% block top %} 

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

父模板:(base.html)

{% block top %}
顶部菜单
{% endblock top %} {% block content %}
{% endblock content %} {% block bottom %}
底部
{% endblock bottom %}

子模板:

extends指令声明这个模板继承自哪:

{% extends 'base.html' %}
{% block content %}
需要填充的内容
{% endblock content %}

模板继承使用时注意点:

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

包含

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

include的使用:

{% include 'hello.html' %}

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

include 的使用加上关键字ignore missing:

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

小结:

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

模板中的特有变量和函数


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

config:

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

{{config.SQLALCHEMY_DATABASE_URI}}

sqlite:///database.db

request:

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

{{request.url}}

http://127.0.0.1

session:

为flask的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=)}}

/post/
get_flashed_messages():

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

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

Web表单

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激活的时候,该设置会根据设置的密匙生成加密令牌。

代码验证:

使用html自带的表单来验证:

1、创建模板文件login.html,在其中直接写form表单:

<!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 %}
</body>
</html>

2、在视图中的逻辑处理:

from flask import Flask,render_template,request,flash

app = Flask(__name__)
#需要先配置参数SECRET_KEY 不然访问会出问题
app.config["SECRET_KEY"] = "lishuntaolovemyself"
@app.route('/')
def hello_world():
#渲染模板
return render_template("temp_demo1.html")
@app.route("/demo1",methods=["get","post"])
def demo1():
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("login.html") if __name__ == '__main__':
app.run(port=,debug=True)

使用Flask-WTF

配置参数,关闭CSRF校验:

# 配置参数,关闭CSRF校验
app.config["WTF_CSRF_ENABLED"] = False

模板页面:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post">
{# 点label就用label标签渲染第一个参数,点属性就生成input表格 #}
{{ form.username.label }} {{ form.username }}<br/>
{{ form.password.label }} {{ form.password }}<br/>
{{ form.password2.label }} {{ form.password2 }}<br/>
{{ form.submit }}
</form>
</body>
</html>

视图函数:

#导入Flask,渲染模板render_template,以及推送消息flash
from flask import Flask,render_template,flash,request
#导入wtf扩展的表单类
from flask_wtf import FlaskForm
#导入自定义表单需要的字段
from wtforms import SubmitField,StringField,PasswordField
#导入wtf扩展提供的表单验证器
from wtforms.validators import DataRequired,EqualTo #实例化app 参数告诉Flask模块包在哪里
app = Flask(__name__)
#配置密钥口令牌
app.config["SECRET_KEY"] = "SECRET_KEY" #关闭CSRF验证
app.config['WTF_CSRF_ENABLED'] = False
#自定义表单类,文本字段,密码字段,提交按钮
#自定义的表单类需要继承FlaskForm
class RegisterForm(FlaskForm):
#需要输入的注册信息,因为是用户,所以是字符串字段 验证器
#第一个参数是表格名字:
username = StringField("用户名:",validators=[DataRequired("请输入用户名")],render_kw={"placeholder": "请输入用户名"})
password = PasswordField("密码:", validators=[DataRequired("请输入密码")])
#EqualTo("password", "两次密码不一致") 验证不成功,继续输入密码
password2 = PasswordField("确认密码:", validators=[DataRequired("请输入确认密码"), EqualTo("password", "两次密码不一致")])
submit = SubmitField("注册")
"""这是在HTML中的form表单渲染变量
第一个参数是表格名字:点label就用label标签渲染第一个参数
validators验证提示 并且是个列表传关键字参数
前面渲染label标签 这个渲染input标签
{{ form.username.label }} {{ form.username }}<br/>
{{ form.password.label }} {{ form.password }}<br/>
{{ form.password2.label }} {{ form.password2 }}<br/>
{{ form.submit }}
"""
#定义由路由视图函数,生成表单对象,获取表单数据,进行表单数据验证
@app.route("/",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")
# 假装做注册操作
return "success"
else:
if request.method == "POST":
flash("参数有误或者不完整") return render_template("temp_register.html",form=register_form)
if __name__ == '__main__':
app.run(debug=True,port=)

CSRF

  

  • CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造。
  • CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。
    • 包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......
  • 造成的问题:个人隐私泄露以及财产安全。

CSRF攻击示意图:

客户端访问服务器时没有同服务器做安全验证:

总结就是:用户访问其他攻击网站,攻击网站发出一个请求,获取浏览器的cookie信息,然后去访问带有cookie的网站,操作个人隐私操作,获取想要的价值信息或者钱财。

防止CSRF攻击

步骤:

  1. 在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值
  2. 在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token
  3. 在用户点击提交的时候,会带上这两个值向后台发起请求
  4. 后端接受到请求,以会以下几件事件:
    • 从 cookie中取出 csrf_token
    • 从 表单数据中取出来隐藏的 csrf_token 的值
    • 进行对比
  5. 如果比较之后两值一样,那么代表是正常的请求,如果没取到或者比较不一样,代表不是正常的请求,不执行下一步操作

在Flask项目中解决CSRF攻击

  在 Flask 中, Flask-wtf 扩展有一套完善的 csrf 防护体系,对于我们开发者来说,使用起来非常简单。

在FlaskForm中实现校验

1、设置应用程序的secret_key。用于加密生成的csrf_token的值。

app.secret_key = "#此处可以写随机字符串#"

在模板的表单中添加代码:

<form method="post">
{# 添加的csrf防护体系 #}
{{ form.csrf_token() }}
{{ form.username.label }} {{ form.username }}<br/>
{{ form.password.label }} {{ form.password }}<br/>
{{ form.password2.label }} {{ form.password2 }}<br/>
{{ form.submit }}
</form>

csrf_token会在前端渲染在form表单内。

单独使用:

1、设置应用程序的secret_key。用于加密生成的csrf_token的值。

app.secret_key = "#此处可以写随机字符串#"

2、导入 flask_wtf.csrf 中的 CSRFProtect 类,进行初始化,并在初始化的时候关联 app

from flask.ext.wtf import CSRFProtect
CSRFProtect(app)

如果模板中有表单,不需要做任何事情。与之前一样

<form method="post">
{{ form.csrf_token }}
...
</form>

如果没有表单,仍需要CSRF令牌:

<form method="post" action="/">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>

03-模板(过滤器,代码复用,表单,CSRF)的更多相关文章

  1. Django提交POST表单“CSRF verification failed. Request aborted”问题的解决

    1.环境 python 3.4 Django 1.7 Visual Studio 2015 PTVS 2.问题 提交表单,出现以下错误: CSRF verification failed. Reque ...

  2. Laravel 5 基础(十一)- 子视图和表单复用

    我们需要处理编辑文章的问题.当然我们可以手工添加新的路由,就像这样: Route::get('/articles/{id}/edit', 'ArticleController@edit'); 让我们在 ...

  3. Flask—03-bootstrap与表单

    bootstrap与表单 Bootstrap是美国Twitter公司的设计师Mark Otto和Jacob Thornton合作基于HTML.CSS.JavaScript 开发的简洁.直观.强悍的前端 ...

  4. Symfony2学习笔记之表单

    对于一个Web开发者来说,处理HTML表单是一个最为普通又具挑战的任务.Symfony2集成了一个Form组件,让处理表单变的容易起来.在这一节里,我们将从基础开始创建一个复杂的表单,学习表单类库中最 ...

  5. 一个web应用的诞生--数据表单

    下面把角色分为两种,普通用户和管理员用户,至少对于普通用户来说,直接修改DB是不可取的,要有用户注册的功能,下面就开始进行用户注册的开发. 用户表 首先要想好用户注册的时候需要提供什么信息:用户名.密 ...

  6. Flask 扩展 表单

    pip install flask-wtf 一个简单的表单 from flask_wtf import Form from wtforms import StringField from wtform ...

  7. Django表单介绍

    HTML 表单 在HTML中,表单是<form>...</form> 之间元素的集合,它们允许访问者输入文本.选择选项.操作对象和控制等等,然后将信息发送回服务器. 某些表单的 ...

  8. flask 在视图函数中验证表单

    在视图函数中验证表单 因为现在的basic_form视图同时接受两种类型的请求:GET请求和POST请求.所以我们要根据请求方法的不同执行不同的代码.具体来说,首先是实例化表单,如果是GET请求,就渲 ...

  9. Django入门与实践-第13章:表单处理(完结)

    http://127.0.0.1:8000/boards/1/ http://127.0.0.1:8000/boards/2/ http://127.0.0.1:8000/boards/3/ http ...

  10. 一个web应用的诞生(5)--数据表单

    下面把角色分为两种,普通用户和管理员用户,至少对于普通用户来说,直接修改DB是不可取的,要有用户注册的功能,下面就开始进行用户注册的开发. 用户表 首先要想好用户注册的时候需要提供什么信息:用户名.密 ...

随机推荐

  1. 12C新功能:在线移动数据文件 (Doc ID 1566797.1)

    12C New Feature : Move a Datafile Online (Doc ID 1566797.1) APPLIES TO: Oracle Database - Enterprise ...

  2. [Go] 轻量服务器框架tcp的粘包问题 封包与拆包

    tcp传输的数据是以流的形式传输的,因此就没有办法判断到哪里结束算是自己的一个消息,这样就会出现粘包问题,多个包粘在一起了 可以使用这样一个自定义的形式来解决,一个消息分为 head+body  he ...

  3. Samba共享文件

    1 安装samba yum install -y samba* 2 添加用户 useradd smbuser 3 设置共享文件用户的密码 smbpasswd -a smbuser 4 创建公共共享文件 ...

  4. ML.NET Model Builder 更新

    ML.NET是面向.NET开发人员的跨平台机器学习框架,而Model Builder是Visual Studio中的UI工具,它使用自动机器学习(AutoML)轻松地允许您训练和使用自定义ML.NET ...

  5. LeetCode刷题-最长公共前缀(简单)

    题目描述 编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 示例 1: 输入: ["flower","flow ...

  6. leetcode动态规划--基础题

    跳跃游戏 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 判断你是否能够到达最后一个位置. 思路 根据题目意思,最大跳跃距离,说明可以跳0--n ...

  7. 【问题记录】 Linux分区磁盘占满,导致ssh登陆闪退

    问题描述 今天要去后台看日志查个问题,通过ssh登陆到服务器后准备用平时非常熟悉的less命令打开日志查看,突然xshell客户端就闪退了.一时感觉很蒙,怎么回事??由于之前有同事遇到类似的问题,提醒 ...

  8. 什么是面向对象编程(OOP)?

    Java 程序员第一个要了解的基础概念就是:什么是面向对象编程(OOP)? 玩过 DOTA2 (一款推塔杀人的游戏)吗?里面有个齐天大圣的角色,欧洲战队玩的很溜,国内战队却不怎么会玩,自家人不会玩自家 ...

  9. three.js通过canvas实现球体世界平面地图

    概况如下: 1.SphereGeometry实现自转的地球: 2.THREE.CatmullRomCurve3实现球体线条地图点确定: 3.THREE.Math.degToRad,Math.sin,M ...

  10. C#深入浅出之数据类型

    基本数据类型        C#支持完整的BCL(基类库)名字,但是最好都统一使用关键字进行使用与开发,比如使用int而不是System.Int32,以及使用string类型时候应当使用string而 ...