"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
# 使用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">
        <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}} # 包含了自动生成的scrf令牌值,在提交表单后悔自动被验证。**
        </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)
    # 在浏览器中请求 "http://127.0.0.1:5000/login_base" 就可以看到表单页面
    

    处理表单数据:(流程)

    表单数据的处理涉及很多内容,除去表单提交不说,从获取数据到保存数据大致会经历以下步骤: 1)解析请求,获取表单数据。 2)对数据进行必要的转换,比如将勾选框的值转换成Python的布尔值。 3)验证数据是否符合要求,同时验证CSRF令牌。 4)如果验证未通过则需要生成错误消息,并在模板中显示错误消息。 5)如果通过验证,就把数据保存到数据库或做进一步处理。 除非是简单的程序,否则手动处理不太现实,使用Flask-WTF和WTForms可以极大地简化这些步骤。

    HTML中表单控制提交的行为属性默认值说明
    action当前URL,即页面对应的URL表单提交时发送请求的目标URL
    methodget提交表单的HTTP请求方法,目前仅仅支持使用GET和POST方法
    enctypeapplication/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>
    

    使用内置错误消息为中文

    # 设置内置的错误消息语言为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">
        <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):  # (行内验证器[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">
        <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() #**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">
        <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"> # 使用get_file来拼接路径
          <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_LOCALFalse设为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">
      <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']
  • 分类:
    后端
    标签: