当前位置: 首页 > 工具软件 > WTF > 使用案例 >

Flask 核心技术 - WTF表单、上下文、表单验证、蓝图

仰欣悦
2023-12-01

flask 获取请求参数

request

request 就是flask中代表当前请求的 request 对象,其中一个请求上下文变量(理解成全局变量,在视图函数中直接使用可以取到当前本次请求)

常用的属性如下:

属性说明类型
data记录请求的数据,并转换为字符串*
form记录请求中的表单数据MultiDict
args记录请求中的查询参数MultiDict
cookies记录请求中的cookie信息Dict
headers记录请求中的报文头EnvironHeaders
method记录请求使用的HTTP方法GET/POST
url记录请求的URL地址string
files记录请求上传的文件*

示例

  • 获取上传的图片并保存到本地
@app.route('/', methods=['POST'])
def index():
    pic = request.files.get('pic')
    pic.save('./static/aaa.png')
    return 'index'

flask上下文

上下文:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。

Flask中有两种上下文,请求上下文和应用上下文

请求上下文

在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:request、session

  • request
    • 封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get(‘user’),获取的是get请求的参数。
  • session
    • 用来记录请求会话中的信息,针对的是用户信息。举例:session[‘name’] = user.id,可以记录用户信息。还可以通过session.get(‘name’)获取用户信息。

应用上下文

它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。

应用上下文对象有:current_app,g

current_app

应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:

  • 应用的启动脚本是哪个文件,启动时指定了哪些参数
  • 加载了哪些配置文件,导入了哪些配置
  • 连了哪个数据库
  • 有哪些public的工具类、常量
  • 应用跑再哪个机器上,IP多少,内存多大
current_app.name
current_app.test_value='value'

g变量

g 作为 flask 程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别

g.name='abc'

注意:不同的请求,会有不同的全局变量

两者区别:

  • 请求上下文:保存了客户端和服务器交互的数据
  • 应用上下文: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激活的时候,该设置会根据设置的密匙生成加密令牌。

使用 html 自带的表单

  • 创建模板文件 login.html,在其中直接写form表单:
<form method="post">
    <label>用户名:</label><input type="text" name="username" placeholder="请输入用户名"><br/>
    <label>密码:</label><input type="password" name="password" placeholder="请输入密码"><br/>
    <input type="submit" value="登陆">
</form>

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

视图函数中获取表单数据验证登录逻辑:

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

        if not all([username, password])
            # 向前端界面弹出一条提示(闪现消息)
            flash("参数不足")
        elif password != '123456':
            flash("密码错误")
        else:
            # 假装做登陆操作
            print(username, password)
            return "success"

    return render_template('login.html')

使用 Flask-WTF 实现表单

安装

pip install flask_wtf
  • 配置参数,关闭 CSRF 校验
 app.config['WTF_CSRF_ENABLED'] = False

CSRF:跨站请求伪造,后续会讲到tdfgcbnmhblikcd,iuytstyu

模板页面:

<form method="post">
    {{ form.username.label }} {{ form.username }}<br/>
    {{ form.password.label }} {{ form.password }}<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 LoginForm(FlaskForm):
    username = StringField("用户名:", validators=[DataRequired("请输入用户名")], render_kw={"placeholder": "请输入用户名"})
    password = PasswordField("密码:", validators=[DataRequired("请输入密码")])
    submit = SubmitField("登陆")

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

    return render_template('login.html', form=login_form)

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

Restful-API

什么是 REST?

六条设计规范定义了一个 REST 系统的特点:

  • 客户端-服务器: 客户端和服务器之间隔离,服务器提供服务,客户端进行消费。
  • 无状态: 从客户端到服务器的每个请求都必须包含理解请求所必需的信息。换句话说, 服务器不会存储客户端上一次请求的信息用来给下一次使用。
  • 可缓存: 服务器必须明示客户端请求能否缓存。
  • 分层系统: 客户端和服务器之间的通信应该以一种标准的方式,就是中间层代替服务器做出响应的时候,客户端不需要做任何变动。
  • 统一的接口: 服务器和客户端的通信方法必须是统一的。
  • 按需编码: 服务器可以提供可执行代码或脚本,为客户端在它们的环境中执行。这个约束是唯一一个是可选的。

RESTful web services 概念的核心就是“资源”。 资源可以用 URI 来表示。客户端使用 HTTP 协议定义的方法来发送请求到这些 URIs,当然可能会导致这些被访问的”资源“状态的改变。

HTTP 标准的方法有如下:

==========  =====================  ==================================
HTTP 方法   行为                   示例
==========  =====================  ==================================
GET         获取资源的信息         http://example.com/api/orders
GET         获取某个特定资源的信息 http://example.com/api/orders/123
POST        创建新资源             http://example.com/api/orders
PUT         更新资源               http://example.com/api/orders/123
DELETE      删除资源               http://example.com/api/orders/123
==========  ====================== ==================================

REST 设计不需要特定的数据格式。在请求中数据可以以 JSON 形式, 或者有时候作为 url 中查询参数项。

from flask import Flask, jsonify

app = Flask(__name__)

tasks = [
    {
        'id': 1,
        'title': u'Buy groceries',
        'desc': u'Milk, Cheese, Pizza, Fruit, Tylenol',
        'done': False
    },
    {
        'id': 2,
        'title': u'Learn Python',
        'desc': u'Need to find a good Python tutorial on the web',
        'done': False
    }
]

@app.route('/todo/api/v1/tasks', methods=['GET'])
def get_tasks():
    return jsonify({'tasks': tasks})


@app.route('/todo/api/v1/tasks/', methods=['POST'])
def create_task():
    if not request.form or not 'title' in request.form:
        abort(400)
    book = {
        'id': tasks[-1]['id'] + 1,
        'title': request.form['title'],
        'desc': request.form['desc'],
        'done': request.form['done'],
    }
    tasks.append(book)
    return jsonify({'task': book}), 201
  
  
 
@app.route('/todo/api/v1/tasks/<int:id>', methods=['PUT'])
def update_book(id):
    for book in tasks:
        if book['id']==id:
            book["title"] = request.form['title']
            book["desc"] = request.form['desc']
            book["done"] = request.form['done']
        return jsonify({'tasks': tasks})
    abort(400)


@app.route('/todo/api/v1/tasks/<int:id>', methods=['DELETE'])
def delete_task(id):
    for book in tasks:
        if book['id']==id:
            tasks.remove(book)
            return jsonify({'result': True})
    abort(404)
    return jsonify({'result': True})
  
  
if __name__ == '__main__':
    app.run(debug=True)

蓝图

模块化

随着flask程序越来越复杂,我们需要对程序进行模块化的处理,之前学习过python的模块化管理,于是针对一个简单的flask程序进行模块化处理

举例来说:

我们有一个博客程序,前台界面需要的路由为:首页,列表,详情等页面

源程序app.py文件:
from flask import Flask

app=Flask(__name__)

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

@app.route('/list')
def list():
    return 'list'

@app.route('/detail')
def detail():
    return 'detail'

if __name__=='__main__':
    app.run()
Blueprint概念

简单来说,Blueprint 是一个存储操作方法的容器,这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理请求。

Flask使用Blueprint让应用实现模块化,在Flask中,Blueprint具有如下属性:

  • 一个应用可以具有多个Blueprint
  • 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/”、“/sample”或者子域名
  • 在一个应用中,一个模块可以注册多次
  • Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
  • 在一个应用初始化时,就应该要注册需要使用的Blueprint

但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。

使用蓝图可以分为三个步骤

新建admin目录包

  • 1,创建一个蓝图对象
admin=Blueprint('admin',__name__)
  • 2,在这个蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器
@admin.route('/')
def admin_home():
    return 'admin_home'
  • 3,在应用对象上注册这个蓝图对象
app.register_blueprint(admin,url_prefix='/admin')

当这个应用启动后,通过/admin/可以访问到蓝图中定义的视图函数

 类似资料: