"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>
<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" >
<meta charset="UTF-8" >
<title>Document</title>
</head>
<div align="center" >
<h2>User Management</h2>
{% if message %} {{message}} {% endif %}
<form method="POST" >
username:{{ form.username }}
password:{{ form.password }}
<!-- <input type ="submit" value="Submit" > -->
{{ form.submit }}
<input type ="reset" value="reset" >
</form>
</body>
</html>
---------------------------------------------------
---------------------------------------------------
from flask_wtf import FlaskForm
from wtforms import Form
class LoginForm (Form ):
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" >
<meta charset="UTF-8" >
<title>Document</title>
</head>
<div align="center" >
<h2>User Management</h2>
{% if message %} {{message}} {% endif %}
<form method="POST" >
**{{form.username.label}}**:**{{ form.username }}**
{{form.password.label}}:{{ form.password }}
<!-- <input type ="submit" value="Submit" > -->
{{form.remember.label}}:{{ form.remember }}
{{ form.submit }}
**{{form.csrf_token}}
</form>
</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)
处理表单数据:(流程)
表单数据的处理涉及很多内容,除去表单提交不说,从获取数据到保存数据大致会经历以下步骤: 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()
> >> 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>
使用内置错误消息为中文
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" >
<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>
{% 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 ):
if field.data != 42 :
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" >
<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>
<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()
@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
处理上传的文件
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)
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 = []
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)
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 = []
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" >
<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>
<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" >
<img src="{{ url_for('get_file', filename=filename) }}" >
{% 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
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:
flash("这是保存按钮" )
elif form.publish.data:
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" >
<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>
{% 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']