Flask学习笔记 - 表单

表单处理

  1. 基本表单处理:使用 request.form 获取表单数据。
  2. 使用 Flask-WTF:结合 WTForms 进行表单处理和验证,简化表单操作。
  3. 表单验证:使用验证器确保表单数据的有效性。
  4. 文件上传:处理文件上传和保存文件。
  5. CSRF 保护:确保表单免受跨站请求伪造攻击。

基本表单处理

form.html

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>Form Example</title>
</head>
<body>
    <form action="/submit" method="post">
        <label for="name">Name:</label>
        <input type="text" id="name" name="name">
        <br>
        <label for="email">Email:</label>
        <input type="email" id="email" name="email">
        <br>
        <input type="submit" value="Submit">
    </form>
</body>
</html>

app.py

python 复制代码
@app.route('/form')
def form():
    return render_template('form.html')

@app.route('/submit', methods=['POST'])
def submit():
    name = request.form.get('name')
    email = request.form.get('email')
    return f'Name: {name}, Email: {email}'

request.form.get('变量名'),变量名是input中的name字段的值

使用 Flask-WTF 扩展

Flask-WTF 是一个封装了 WTForms 的扩展,提供了表单处理和验证的功能,使得表单处理更加简洁和强大。

安装 Flask-WTF

shell 复制代码
pip install flask-wtf
# 我的设备上有Python2.7和Python3,给Python3安装所以用以下的命令
# pip3 install flask-wtf

app.py

python 复制代码
from flask import Flask, render_template, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, EmailField, SubmitField
from wtforms.validators import DataRequired, Email

app = Flask(__name__)
app.secret_key = '6a31ad17a15b0d887049fcfd478aedeb8da2d40049f1413ca4add757379d4237'  # Required for form protection

class MyForm(FlaskForm):
    name = StringField('Name', validators=[DataRequired()])
    email = EmailField('Email', validators=[DataRequired(), Email()])
    submit = SubmitField('Submit')

@app.route('/form', methods=['GET', 'POST'])
def form():
    form = MyForm()
    if form.validate_on_submit(): # 如果校验通过则显示姓名与邮箱地址
        name = form.name.data
        email = form.email.data
        return f'Name: {name}, Email: {email}'
    return render_template('form.html', form=form) # 否则显示form.html页面

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

generate_secrets.py

python 复制代码
import secrets
print(secrets.token_hex())
# 6a31ad17a15b0d887049fcfd478aedeb8da2d40049f1413ca4add757379d4237
# 64位的token

表单处理和验证的功能

python 复制代码
name = StringField('Name', validators=[DataRequired()])
email = EmailField('Email', validators=[DataRequired(), Email()])

要求name和email非空,且email要符合格式

验证name和email

不输入name

不输入email

输入错误的email

输入name和正确格式的email

sh 复制代码
Exception: Install 'email_validator' for email validation support.

报错比较明显是还缺少emial_validator模块,通过命令行进行安装

sh 复制代码
pip3 install email_validator

继续请求,报错消失,但界面没有出现下面的效果

python 复制代码
return f'Name: {name}, Email: {email}'

一开始以为是html中的name和email是小写,导致提交时找不到元素,然而还是不对。

IDE里的注释 # Call validate only if the form is submitted. This is a shortcut for form.is_submitted() and form.validate(). 所以加了打印看时哪个函数报错,结果是form.validate()校验过程有问题,搜索相关资料,看怎么排查这个问题

  • 请求方法错误 -- POST请求,不是这个问题
  • 表单数据错误 -- 加上print(form.errors) 打印具体具体的错误 => {'csrf_token': ['The CSRF tokens do not match.']} ...

是csrf_token未匹配的原因,对比了其它几种写法,未发现有什么特殊操作,其中提到清理浏览器缓存的操作,我一直都是使用Trae IDE自带的浏览器,抱着试一试的心态。用Chrome测试了下,是正常的!

我又问了Trae里的AI,看能不能告诉我怎么清理自带的浏览器,然后它的回答,始终讲不到点上。

在Trae起的web服务和主机起的web服务,应该是有差别的。都用了5000端口,所以不能同时起,两者不能相互访问。每次验证都要在关闭web再启动,为了后面遇到奇怪的问题可以用不同浏览器去对比验证。

Trae和主机传不同的port参数可以解决这个问题

python 复制代码
import sys

if __name__ == '__main__':
    port = sys.argv[1] if len(sys.argv) > 1 else 5001
    app.run(port=<port>,debug=True)

Chrome浏览器正常显示

表单验证

app.py

python 复制代码
class MyForm2(FlaskForm):
    name = StringField('Name', validators=[
        DataRequired(), Length(min=1, max=8)
    ])
    email = EmailField('Email', validators=[
        DataRequired(), Email()
    ])
    submit = SubmitField('Submit')
    
@app.route('/form2', methods=['GET', 'POST'])
def form2():
    form = MyForm2()
    if form.validate_on_submit():
        name = form.name.data
        email = form.email.data
        return redirect(url_for('form'))
    return render_template('form.html', form=form)

设置了Length,name最长只能输入8个

文件上传

Flask 还支持处理文件上传。上传的文件可以通过 request.files 访问。

templates/upload.html

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>Upload File</title>
</head>
<body>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <label for="file">File:</label>
        <input type="file" id="file" name="file">
        <br>
        <input type="submit" value="Upload">
    </form>
</body>
</html>

app.py

python 复制代码
@app.route('/upload_file', methods=['GET'])
def upload_form():
    return render_template('upload.html')

@app.route('/upload', methods=['POST'])
def upload():
    file = request.files.get('file')
    if file:
        filename = file.filename
        file.save(f'uploads/{filename}')
        return f'File uploaded successfully: {filename}'
    return 'No file uploaded'

访问/upload_file,选择文件并上传,上传成功后在app.py同级目录uploads下

CSRF 保护

Flask-WTF 自动为表单提供 CSRF 保护。你需要配置一个密钥来启用 CSRF 保护,并在模板中包含隐藏的 CSRF 令牌。

配置CSRF保护

app.py

python 复制代码
app.secret_key = '6a31ad17a15b0d887049fcfd478aedeb8da2d40049f1413ca4add757379d4237'

在模板中添加 CSRF 令牌

html 复制代码
<form method="post">
    {{ form.hidden_tag() }}
    <!-- Form fields here -->
</form>

Demo

Flask

参考

  1. Flask表单处理
  2. Form Validation with WTForms
  3. Flask WTF form.validate_on_submit()方法不起作用
  4. flask总结05(在 Flask 项目中解决 CSRF 攻击)
  5. Flask-WTF 表单令牌不匹配:The CSRF Tokens do not match
相关推荐
木木黄木木11 分钟前
css炫酷的3D水波纹文字效果实现详解
前端·css·3d
程序员一诺32 分钟前
【Flask开发】嘿马文学web完整flask项目第1篇:简介【附代码文档】
后端·python·flask·框架
郁大锤35 分钟前
Flask与 FastAPI 对比:哪个更适合你的 Web 开发?
前端·flask·fastapi
Bruce_Liuxiaowei39 分钟前
基于Flask的MBA考生成绩查询系统设计与实现
后端·python·flask
HelloRevit1 小时前
React DndKit 实现类似slack 类别、频道拖动调整位置功能
前端·javascript·react.js
赖皮猫1 小时前
PIKIE-RAG 本地部署实践
后端·python·flask
ohMyGod_1232 小时前
用React实现一个秒杀倒计时组件
前端·javascript·react.js
eternal__day2 小时前
第三期:深入理解 Spring Web MVC [特殊字符](数据传参+ 特殊字符处理 + 编码问题解析)
java·前端·spring·java-ee·mvc
醋醋2 小时前
Vue2源码记录
前端·vue.js
江耳2 小时前
从10秒到无限流:我用Vercel+NextJS实现AI流式对话遇到的超时问题及解决方案
前端