基础表单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>****
</head>
<body>
<form method="post" action="">
<label for="username">Username</label>
<input type="text" name="username" placeholder="Hector Rivera"><br>
<label for="password">Password</label>
<input type="password" name="password" placeholder="19001130"><br>
<input id="remember" name="remember" type="checkbox" checked>
<label for="remember"><small>Remember me</small></label><br>
<input type="submit" name="submit" value="Log in">
</form>
</body>
</html>
使用Flask-WTF处理表单
pip install flask-wtf
Flask-WTF默认为每个表单启动CSRF保护,会自动生成和验证CSRF令牌
app.secret_key = 'secret key'
定义表单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div align="center">
<h2>User Management</h2>
{% if message %} {{message}} {% endif %}
<form method="POST">
username:{{ form.username }}
<br>
password:{{ form.password }}
<br>
<!-- <input type="submit" value="Submit"> -->
<br>
{{ form.submit }}
<input type="reset" value="reset">
</form>
</div>
</body>
</html>
---------------------------------------------------
---------------------------------------------------
from flask_wtf import FlaskForm
from wtforms import Form
# 使用WTForms创建表单
class LoginForm(Form):
# 使用Flask-WTF创建表单
class LoginForm(FlaskForm):
username = StringField('username', validators=[DataRequired()])
password = PasswordField('password', validators=[DataRequired(),Length(8,128)])
remember = BooleanField('remember me')
submit = SubmitField('Log in')
@app.route('/login_base',methods=["GET","POST"])
def login_base():
login_form = LoginForm()
if request.method == 'POST':
return render_template('flash.html')
return render_template('login.html',form = login_form)
常用的WTForm字段
| 字段类 | 说明 | 对应的HTML表示 |
|---|---|---|
| BooleanField | 复选框,值会被处理为True或False | <input type="checkbox"> |
| DateField | 文本字段,值会被处理为datetime.data对象 | <input type="text"> |
| DateTimeField | 文本字段,值会被处理为datetime.datetime对象 | <input type="text"> |
| FileField | 文件上传字段 | <input type="file"> |
| FloatField | 浮点数字段,值会被处理为浮点型 | <input type="text"> |
| IntegerField | 整数字段,值会被处理为整型 | <input type="text"> |
| RadioField | 一组单选按钮 | <input type="radio"> |
| SelectField | 下拉列表 | <select><option></option></select> |
| SelectMultipleField | 多选下拉列表 | <select multiple><option></option></select> |
| SubmitField | 提交按钮 | <input type="submit"> |
| StringField | 文本字段 | <input type="text"> |
| HiddenField | 隐藏文本字段 | <input type="hidden"> |
| PasswordField | 密码文本字段 | <input type="password"> |
| TextAreaField | 多行文本字段 | <textarea></textarea> |
username = StringField('username', validators=[])
实例化字段常用参数
| 参数 | 说明 | |
|---|---|---|
| label | 字段标签 <label> 的值, | 也就是渲染后显示在输入字段前的文字 |
| render_kw | 一个字典,用来设置对应的HTML<input> 标签的属性,比如传入 {'placeholder':'Your Name'},渲染后HTML代码会将<input>标签的placeholder属性设置为Your Name | |
| validators | 一个列表, 包含一系列验证器,会在表单提交后被逐一调用验证表单数据 | |
| default | 字符串或可调用对象,用来为表单字段设置默认值 |
常用的WTFforms验证器(wtforms.validators模块导入)
| 验证器 | 说明 |
|---|---|
| DataRequired(message=None) | 验证数据是否有效 |
| Email(message=None) | 验证Email地址 |
| EqualTo(fieldname, message=None) | 验证两个字段值是否相同 |
| InputRequired(message = None) | 验证两个字段值是否相同 |
| InputRequired(message=None) | 验证是否有数据 |
| Length(min=1, max=-1, message=None) | 验证输入值长度是否在给定范围内 |
| NumberRange(min=None, max=None,message=None) | 验证输入数字是否在给定范围内 |
| Optional(strip_whitespace = True) | 允许输入值为空,并跳过其他验证 |
| Regexp(regex, flags=0, message=None) | 使用正则表达式验证输入值 |
| URL(require_tld=True, message=None) | 验证URL |
| AnyOf(values.message=None, values_formatter=None) | 确保输入值在可选值列表中 |
| NoneOf(values, message=None, values_formatter=None) | 确保输入值不在可选值列表中 |
message、re 参数用来传入自定义错误信息,如果没传则使用默认的错误信息(英文)。
实例化字段参数
- 使用
render_kw属性render_kw={"placeholder":"You username"}
username = StringField('Username', render_kw={"placeholder":"You username"})
- 调用字段时传入
form.username(style='width:200px;',class='bar')
u'<input class="bar" id="username" name="username" style="width: 200px;" type="text">'
在模板中渲染表单
templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div align="center">
<h2>User Management</h2>
{% if message %} {{message}} {% endif %}
<form method="POST">
**{{form.username.label}}**:**{{ form.username }}**
<br>
{{form.password.label}}:{{ form.password }}
<br>
<!-- <input type="submit" value="Submit"> -->
<br>
{{form.remember.label}}:{{ form.remember }}
{{ form.submit }}
**{{form.csrf_token}} # 包含了自动生成的scrf令牌值,在提交表单后悔自动被验证。**
</form>
</div>
</body>
</html>
===========================================
app.py
from wtforms import StringField, Form, PasswordField,BooleanField,SubmitField
from wtforms.validators import DataRequired, Length
from flask_wtf import FlaskForm
class LoginForm(FlaskForm):
username = StringField('username', validators=[DataRequired()])
password = PasswordField('password', validators=[DataRequired(),Length(8,128)])
remember = BooleanField('remember me')
submit = SubmitField('Log in')
@app.route('/login_base',methods=["GET","POST"])
def login_base():
login_form = LoginForm()
if request.method == 'POST':
return render_template('flash.html')
return render_template('login.html',form = login_form)
# 在浏览器中请求 "http://127.0.0.1:5000/login_base" 就可以看到表单页面
处理表单数据:(流程)
表单数据的处理涉及很多内容,除去表单提交不说,从获取数据到保存数据大致会经历以下步骤: 1)解析请求,获取表单数据。 2)对数据进行必要的转换,比如将勾选框的值转换成Python的布尔值。 3)验证数据是否符合要求,同时验证CSRF令牌。 4)如果验证未通过则需要生成错误消息,并在模板中显示错误消息。 5)如果通过验证,就把数据保存到数据库或做进一步处理。 除非是简单的程序,否则手动处理不太现实,使用Flask-WTF和WTForms可以极大地简化这些步骤。
| HTML中表单控制提交的行为 | 属性 | 默认值 | 说明 |
|---|---|---|---|
| action | 当前URL,即页面对应的URL | 表单提交时发送请求的目标URL | |
| method | get | 提交表单的HTTP请求方法,目前仅仅支持使用GET和POST方法 | |
| enctype | application/x-www-form-urlencoded | 表单数据的编码类型,当表单中包含文件上传字段时,需要设为multipart/form-data, 还可以设为纯文本类型text/plain |
GET请求:
http://localhost:5000/basic?username=greyli&password=12345
POST请求:
POST /login HTTP/1.0
Content-Type: application/x-www-from-urlencoded
Content-Length: 30
username=wuhan&password=12345
Flask为路由设置默认监听方法为GET,要是接收提交表单的POST接口的话,就需要进行指定。
@app.route(methods=['GET','POST'])
表单验证机制
客户端验证
{{ form.username(required='') }} #验证是否输入可以使用这种方式
服务端验证
- WTForms验证机制
- 在实例化表单类时传入表单数据,然后对其调用
validate()方法进行验证。(返回布尔值)
- 在实例化表单类时传入表单数据,然后对其调用
>>> from wtforms import Form, StringField, PasswordField, BooleanField
>>> from wtforms.validators import DataRequired, Length
>>> class LoginForm(Form):
... username = StringField('Username', **validators=[DataRequired()]**)
... password = PasswordField('Password', validators=[DataRequired()
, Length(8, 128)])
>>> form = LoginForm(username='', password='123')
>>> form.data # 表单数据字典
{'username': '', 'password': '123'}
>>> **form.validate()**
False
>>> **form.errors ** # 错误消息字典
{'username': [u'This field is required.'], 'password': [u'Field must be
between 8 and 128 characters long.']}
>>> form2 = LoginForm(username='greyli', password='12345678')
>>> form2.data
{'username': 'greyli', 'password': '12345678'}
>>> form2.validate()
True
>>> form2.errors
{}
在视图函数中验证表单
- if request.method == 'POST' and login_form.validate(): 提交表单和验证分开
@app.route('/login_base',methods=["GET","POST"])
def login_base():
login_form = LoginForm()
if request.method == 'POST' **and login_form.validate()**:
return render_template('flash.html')
return render_template('login.html',form = login_form)
validate_on_submit():提交表单和验证合并
@app.route('/login_base',methods=["GET","POST"])
def login_base():
login_form = LoginForm()
**if ****form.validate_on_submit():**
return render_template('flash.html')
return render_template('login.html',form = login_form)
表单验证与获取数据
获取数据方式: from.字段名.data
from flask import Flask, render_template, redirect, url_for, flash
...
@app.route('/basic', methods=['GET', 'POST'])
def basic():
form = LoginForm()
if form.validate_on_submit(): # 表单验证
username = form.username.data # 获取数据
flash('Welcome home, %s!' % username)
return redirect(url_for('index'))
return render_template('basic.html', form=form)
在浏览器中,当单击F5刷新/重载时的默认行为是发送上一个请求。如果上一个请求是POST请求,那么就会弹出一个确认窗口,询问用户是否再次提交表单。为了避免出现这个容易让人产生困惑的提示,我们尽量不要让提交表单的POST请求作为最后一个请求。这就是为什么我们在处理表单后返回一个重定向响应,这会让浏览器重新发送一个新的GET请求到重定向的目标URL。最终,最后一个请求就变成了GET请求。这种用来防止重复提交表单的技术称为PRG(Post/Redirect/Get)模式,即通过对提交表单的POST请求返回重定向响应将最后一个请求转换为GET请求。
在模板中渲染错误消息
使用form.validate_on_submit() 返回Fasle 之后,验证没通过,此时WTForms会吧错误消息添加到errors中、然后我们在模版中就可以取出这个errors列表(遍历)form.字段名.errors
<form method="post">
{{ form.csrf_token }}
{{ form.username.label }}<br>
{{ form.username }}<br>
**{% for message in form.username.errors %} # 因为特性原因所以验证的时候可以输入 空格 来验证报错**
<small class="error">{{ message }}</small><br>
{% endfor %}
{{ form.password.label }}<br>
<form method="post">
{{ form.csrf_token }}
{{ form.username.label }}<br>
{{ form.username }}<br>
{% for message in form.username.errors %}
<small class="error">{{ message }}</small><br>
{% endfor %}
{{ form.password.label }}<br>
使用内置错误消息为中文
# 设置内置的错误消息语言为Z洪文
app.config['WTF_I18N_ENABLED'] = False
class MyBaseForm(FlaskForm):
class Meta:
locales = ['zh']
class HelloForm(MyBaseForm):
name = StringField("Name", validators=[DataRequired()])
submit = SubmitField()
===========================
也可以在实例化表单时通过meta关键字传入locales值
form = MyForm(meta={'locales':['zh','zh_TW']})
使用 宏 来渲染表单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
{% macro qux(amount=1) %}
{% if amount == 1%}
I am qux
{% elif amount > 1 %}
We are quxs.
{% endif %}
{% endmacro %}
# 渲染表单的宏
{% macro get_field(field)%}
{{ field.label }}
{{ field(**kwargs) }}
{% if field.errors %}
{% for error in field.errors %}
<small class="error">{{ error }}</small>
{% endfor %}
{% endif %}
{% endmacro %}
</body>
</html>
在html文件中使用
{% from 'macro.html' import get_field %}
{{ get_field(form.username) }}<br/>
{{ get_field(form.password) }}<br/>
{{ form.submit(class='btn btn-primary') }}
自定义验证器
- 使用
validate_字段属性名形式命名的方法时(自动调用),提交表单的时候会自动进行验证。
from wtforms import StringField, Form, PasswordField, \
BooleanField,SubmitField,IntegerField
from wtforms.validators import DataRequired, Length, \
ValidationError
from flask_wtf import FlaskForm
class FortyTwoForm(FlaskForm):
answer = IntegerField('The Number')
submit = SubmitField()
**def validate_answer(form, field): # (行内验证器[in-line validator])
# validate_answer 这个名字需要与上面定义的字段名一致
if field.data != 42: # field.data 获取字段数据
raise ValidationError("must be 42.")**
@app.route('/vali_login', methods= ['GET','POST'])
def vali_login():
forty = FortyTwoForm()
if forty.validate_on_submit():
return render_template('flash.html')
return render_template('vali_login.html', form = forty)
============================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form method="POST">
{% from 'macro.html' import get_field %}
{{form.csrf_token}}
{{ get_field(form.answer) }}<br/>
{{ form.submit(class='btn btn-primary') }}
</form>
</body>
</html>
- 全局验证器
from wtforms.validators import ValidationError
def is_42(form, field):
if field.data != 42:
raise ValidationError('Must be 42.')
class FortyTwoForm(FlaskForm):
answer = IntegerField('The Number',validators = [is_42])
submit = SubmitField()
- 工厂函数形式的全局验证器
from wtforms.validators import ValidationError
def is_42(message=None):
if message is None:
message = 'Must be 42.'
def _is_42(form, field):
if field.data != 42:
raise ValidationError(message)
return _is_42
class FortyTwoForm(FlaskForm):
answer = IntegerField('The Number', validators=[is_42()])
submit = SubmitField()
文件上传
定义上传表单
FileFIeld
# 上传文件
from flask_wtf.file import FileField, FileRequired, FileAllowed
class UploadForm(FlaskForm):
photo = **FileField("Upload File", validators=[FileRequired(), FileAllowed(['jpg','jpeg','png','gif'])])**
submit = SubmitField()
#**TODO** 增加一个验证文件类型
@app.route("/file_test", methods=['GET','POST'])
def file_test():
f_test = UploadForm()
if f_test.validate_on_submit():
return render_template('flash.html')
return render_template('upload.html', form = f_test)
=========================
<form method="post" **enctype="multipart/form-data"**>
{{form.csrf_token}}
{{ form.photo }}
{{ form.submit }}
</form>
Flask-WTF提供的上传文件验证器
| 验证器 | 说明 |
|---|---|
| FileRequired(message=None) | 验证是否包含文件对象 |
| FileAllowed(upload_set, message=None) | 用来验证文件类型,upload_set 参数用来传入允许的文件后缀名列表 |
- 设置文件的最大长度(单位为字节(byte))
app.config['MAX_CONTENT_LENGTH'] = 3 * 1024 * 1024 # 大小限制为3M
处理上传的文件
from flask_wtf.file import FileField, FileRequired, FileAllowed
class UploadForm(FlaskForm):
photo = FileField("Upload File", validators=[FileRequired(), FileAllowed(['jpg','jpeg','png','gif'])])
submit = SubmitField()
import uuid
def random_filename(filename):
ext = os.path.splitext(filename)[1]
new_filename = uuid.uuid4().hex + ext
return new_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
if form.validate_on_submit():
f = form.photo.data
print("===>", f) # ===> <FileStorage: 'test.png' ('image/png')>
# filename = random_filename(f.filename)
filename = f.filename # 原文件名
f.save(os.path.join(app.config['UPLOAD_PATH'], filename))
flash('Upload success.')
session['filenames'] = [filename]
return redirect(url_for('upload'))
return render_template('upload.html', form=form)
定义 判断上传文件类型的函数
app.config['ALLOWED_EXTENSIONS'] = ['png','jpg','jpeg','gif']
def allowed_file(filename):
return '.' in filename and filename.rsplit('.',1 )[1].lower() in app.config['ALLOWED_EXTENSIONS']
上传文件
# 上传文件
import os
app.config['UPLOAD_PATH'] = os.path.join(app.root_path, 'static/upload')
import uuid
def random_filename(filename):
ext = os.path.splitext(filename)[1]
new_filename = uuid.uuid4().hex + ext
return new_filename
from flask_wtf.csrf import validate_csrf
@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
if request.method == 'POST':
filenames = []
# 验证CSRF有没有进行验证
try:
validate_csrf(form.csrf_token.data)
except ValidationError:
flash('CSRF token error.')
return redirect(url_for('upload'))
# 验证文件是否存在
if 'photo' not in request.files:
flash("This field is required")
return redirect(url_for('upload'))
# 验证文件类型
for f in request.files.getlist('photo'):
if f and allowed_file(f.filename):
f1 = form.photo.data
print("===>", f1) # ===> <FileStorage: 'test.png' ('image/png')>
filename = random_filename(f.filename)
f1.save(os.path.join(app.config['UPLOAD_PATH'], filename))
filenames.append(filename)
else:
flash("Invalid file type.")
return redirect(url_for('upload'))
flash('Upload success.')
session['filenames'] = [filename]
return redirect(url_for('upload'))
return render_template('upload.html', form=form)
- 获取对应文件对象
request.files.get('photo')
- 获取上传文件的FileStorage对象
form.photo.data
- 处理文件名
- 使用原文件名
filename = f.filename
- 使用过滤后的文件名
from werkzeug.utils import secure_filename
secure_filename('avator@&(^&(##^&(#&.jpg')
-> avator.jpg
- 统一重命名
def random_filename(filename):
ext = os.path.splitext(filaname)[1]
new_filename = uuid.uuid4().hex + ext
return new_filename
- 规定下载文件路径
app.config['UPLOAD_PATH'] = os.path.join(app.root_path, 'uploads')
- 保存下载文件
f.save(os.path.join(app.config['UPLOAD_PATH'], filename))
- 获取上传后的文件
send_from_directory():用来获取文件,传入文件得路径和文件名
@app.route('/upload/<path:filename>')
def get_file(filename):
return **send_from_directory****(**app.config['UPLOAD_PATH'], filename)
- 多文件上传
# 多文件上传
from wtforms import MultipleFileField
class MultiUploadForm(FlaskForm):
photo = MultipleFileField('Upload image', validators=[DataRequired()])
submit = SubmitField()
@app.route('/multi-upload', methods=['GET','POST'])
def multi_upload():
form = MultiUploadForm()
if request.method == 'POST':
filenames = []
# 验证CSRF令牌
try:
validate_csrf(form.csrf_token.data)
except:
flash('CSRF token error.')
return redirect(url_for('multi-upload'))
# 验证文件是否存在
if 'photo' not in request.files:
flash('This filed is required.')
return redirect(url_for('multi_upload'))
# 验证文件类型是否正确
for f in request.files.getlist('photo'):
if f and allowed_file(f.filename):
filename = random_filename(f.filename)
f.save(os.path.join(app.config['UPLOAD_PATH'], filename))
filenames.append(filename)
else:
flash('Invalid file type.')
return redirect(url_for('multi_upload'))
flash("Upload Success.")
session['filenames'] = filenames
return redirect(url_for('multi_upload'))
return render_template('multi-upload.html', form=form)
获取图片路径
from flask import send_from_directory
@app.route('/upload/<path:filename>')
def get_file(filename):
return send_from_directory(app.config['UPLOAD_PATH'], filename)
展示图片用
@app.route('/uploaded-images')
def show_images():
return render_template('uploaded.html')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form method="post" action="" enctype="multipart/form-data">
**{% if session.filenames %}
{% for filename in session.filenames %}
<a href="{{ url_for('get_file', filename=filename) }}" target="_blank"> # 使用get_file来拼接路径
<img src="{{ url_for('get_file', filename=filename) }}">
</a>
{% endfor %}
{% endif %}**
</body>
</html>
在请求方法为POST时,我们对上传数据进行手动验证,主要包含下面几步: 1)手动调用flask_wtf.csrf.validate_csrf验证CSRF令牌,传入表单中csrf_token隐藏字段的值。如果抛出wtforms.ValidationError异常则表明验证未通过。 2)其中if'photo'not in request.files用来确保字段中包含文件数据(相当于FileRequired验证器),如果用户没有选择文件就提交表单则request.files将为空。 3)if f用来确保文件对象存在,这里也可以检查f是否是FileStorage实例。 4)allowed_file(f.filename)调用了allowed_file()函数,传入文件名。这个函数相当于FileAllowed验证器,用来验证文件类型,返回布尔值,如代码清单4-17所示。
- 验证文件类型
app.config['ALLOWED_EXTENSIONS'] = ['png','jpg','jpeg','gif']
def allowed_file(filename):
return '.' in filename and filename.rsplit('.',1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
使用FLask-CKEditor集成富文本编辑器
pip install flask-ckeditor
from flask_ckeditor import CKEditor
ckeditor = CKEditor(app)
Flask-CKEditor常用配置
| 配置键 | 默认值 | 说明 |
|---|---|---|
| CKEDITOR_SERVE_LOCAL | False | 设为True会使用内置的本地资源 |
| CKEDITOR_PKG_TYPE | 'standard' | CKEditor包类型,可选值为basic,standard和full |
| CKEDITOR_LANGUAGE | '' | 界面语言,传入 ISO 639格式的语言吗 |
| CKEDITOR_HEIGHT | '' | 编辑器高度 |
| CKEDITOR_WIDTH | '' | 编辑器宽度 |
re:app.config['CKEDITOR_SERVE_LOCAL'] = True
# 富文本编辑器
from flask_ckeditor import CKEditor, CKEditorField
app = Flask(__name__)
app.config['CKEDITOR_SERVE_LOCAL'] = True
app.config['CKEDITOR_HEIGHT'] = 400
app.secret_key = 'secret string'
**ckeditor = CKEditor(app)**
class PostForm(FlaskForm):
title = StringField('Title')
**body = CKEditorField('Body', validators=[DataRequired()])**
submit = SubmitField('Submit')
@app.route('/index1', methods=['GET', 'POST'])
def index1():
form = PostForm()
if form.validate_on_submit():
title = form.title.data
body = form.body.data
# WARNING: use bleach or something similar to clean the data (escape JavaScript code)
# You may need to store the data in database here
return render_template('post.html', title=title, body=body)
return render_template('index.html', form=form)
{% extends 'base.html' %}
{% from 'macro.html' import get_field %}
{% block content %}
<h1>Integrate CKEditor with Flask-CKEditor</h1>
<form method="post">
{{ form.csrf_token }}
{{ get_field(form.title) }}
{{ get_field(form.body) }}
{{ form.submit }}
</form>
{% endblock %}
{% block scripts %}
**{{ super() }}
{{ ckeditor.load() }}**
{% endblock %}
单个表单多个提交按钮
场景:文章用户编辑完成之后, 有保存草稿和发布按钮
class NewPostFrom(FlaskForm):
title = StringField("Title", validators=[DataRequired(), Length(1, 20)])
body = TextAreaField('Body', validators=[DataRequired()])
save = SubmitField('Save')
publish = SubmitField('Publish')
@app.route('/two-submits', methods = ['GET','POST'])
def two_submits():
form = NewPostForm()
if form.validate_on_submit():
if form.save.data:
# do_something
flash("这是保存按钮")
elif form.publish.data:
# do_something
flash("这是提交按钮")
return redirect(url_for('index'))
return render_template('two_submit.html')
单个页面多个表单
- 单视图处理
class SigninForm(FlaskForm):
username = StringField("Username", validates=[DataRequired(), Length(1,20)])
password = PasswordField("Password", validates = [DataRequired(), Lenght(8, 128)])
submit1 = SubmitField("Sign in")
class RegisterForm(FlaskForm):
username = StringField("Username", validates=[DataRequired(), Length(1,20)])
email = StringField("Email", validates = [DataRequired(), Email(),Lenght(8, 128)])
password = PasswordField("Password", validates = [DataRequired(), Lenght(8, 128)])
submit2 = SubmitField("Register")
@app.route('/multi-form', methods = ['GET','POST'])
def multi_form():
signin_form = SigninForm()
register_form = RegisterForm()
if signin_form.submit1.data and signin_form.validate():
username = signin_form.username.data
flash("%s , you just submit the Signin Form." % username)
return redirect(url_for('index'))
if register_form.submit2.data and register_form.validate():
username = register_form.username.data
flash("%s , you just submit the Register Form." % username)
return redirect(url_for('index'))
return render_template("2form.html", signin_form=signin_form, register_form = register_form)
<form method="POST">
{{ signin_form.csrf_token }}
{{ form_field(signin_form.username) }}
{{ form.field(signin_form.password) }}
{{ signin_form.submit1 }}
</form>
<form method="POST">
{{ register_form.csrf_token }}
{{ form_field(register_form.username) }}
{{ form.field(register_form.password) }}
{{ register_form.submit2 }}
</form>
- 多视图处理
- 把同一模板页面得表单 分成多个视图函数来处理
# 多视图处理
@app.route('/multi-form-multi-view')
def multi_form_multi_view():
"""这部分只处理GET请求"""
signin_form = SigninForm()
register_form = RegisterForm()
return render_template('multi_form.html', register_form=register_form, signin_form=signin_form)
# 这部分各自处理各自的请求
@app.route('/handle_signin', methods=['POST'])
def handle_signin():
signin_form = SigninForm()
register_form = RegisterForm()
if signin_form.validate_on_submit():
username = signin_form.username.data
flash("%s, you just submit the Signin Form." % username)
return redirect(url_for('index1'))
return render_template('multi_form.html', register_form=register_form,signin_form=signin_form)
@app.route('/handle_register', methods=['POST'])
def handle_register():
signin_form = SigninForm()
register_form = RegisterForm()
if signin_form.validate_on_submit():
username = signin_form.username.data
flash("%s, you just submit the Register Form." % username)
return redirect(url_for('index1'))
return render_template('multi_form.html', register_form=register_form,signin_form=signin_form)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
{% from 'macro.html' import get_field%}
<h3>Login Form</h3>
<form method="post" action="{{ url_for('handle_signin') }}">
{{ signin_form.csrf_token }}
{{ get_field(signin_form.username) }}
{{ get_field(signin_form.password) }}
{{ signin_form.submit1 }}
</form>
<h3>Register Form</h3>
<form method="post" action="{{ url_for('handle_register') }}">
{{ register_form.csrf_token }}
{{ get_field(register_form.username) }}
{{ get_field(register_form.email) }}
{{ get_field(register_form.password) }}
{{ register_form.submit2 }}
</form>
</body>
</html>
处理错误信息
def flash_errors(form):
for field, errors in form.errors.items():
for error in errors:
flash(u"Error in the %s field - %s" % (
getattr(form, field).label.text,
error
))
- 以下思考点
- 保存状态下 应该是可以先保存到session中然后在读取
- if session['body']: form.body.data = session['body']