Flask - 表单使用

url_for

  • 可以根据视图函数名构造对应的路由地址
  • 带参的路由也可以构造,多出来的参数会自动作为 get 参数
  • 构造完整(带主机和端口)路由需要制定参数_external=True

加载静态文件

  • 创建目录

    1
    2
    3
    4
    5
    6
    7
    8
    project/
    manage.py # 启动控制文件
    static/ # 静态文件
    favicon.ico # 收藏夹图标
    image/ # 存放图片
    css/ # 存放css文件
    js/ # 存放js文件
    templates/ # 模板文件
  • 加载收藏夹图标

    1
    2
    3
    4
    5
    {% block head %}
    {{ super() }}
    {# 加载收藏夹图标 #}
    <link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
    {% endblock %}
  • 加载图片文件

    1
    <img src="{{ url_for('static', filename='image/meinv.jpg') }}" alt="美女">
  • 加载 CSS 文件

    1
    2
    3
    4
    {% block styles %}
    {{ super() }}
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/common.css') }}">
    {% endblock %}
  • 加载 JS 文件

    1
    2
    3
    4
    {% block scripts %}
    {{ super() }}
    <script type="text/javascript" src="{{ url_for('static', filename='js/common.js') }}"></script>
    {% endblock %}

原生表单

  • 模板文件

    1
    2
    3
    4
    5
    <form method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit" value="立即登录">
    </form>
  • 添加登陆的视图函数

    1
    2
    3
    4
    5
    6
    @app.route('/login/', methods=['GET', 'POST'])
    def login():
    if request.method == 'GET':
    return render_template('login.html')
    else:
    return request.form['username']

flask-wtf

  • 说明:提供了简洁的书写形式,提供了 CSRF(跨站请求伪造保护),提供了字段的校验等。

  • 安装:pip install flask-wtf

  • 使用:

    1. 创建一个表单类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      # 导入表单基类
      from flask_wtf import FlaskForm
      # 导入需要的字段
      from wtforms import StringField, SubmitField, PasswordField

      # 设置秘钥,CSRF会使用
      app.config['SECRET_KEY'] = '123456'

      # 创建一个表单类
      class NameForm(FlaskForm):
      name = StringField('用户名:')
      password = PasswordField('密码:')
      submit = SubmitField('提交')
    2. 视图函数中,创建一个表单对象,然后在渲染时分配到模板文件中

      1
      2
      3
      4
      5
      6
      7
      @app.route('/', methods=['GET', 'POST'])
      def index():
      form = NameForm()
      if request.method == 'GET':
      return render_template('form.html', form=form)
      else:
      return form.name.data
    3. 原生渲染,需要对表单中的每个字段单独渲染处理

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <form method="post" action="">
      {# CSRF用途的隐藏字段 #}
      {{ form.hidden_tag() }}

      {# name字段 #}
      {{ form.name.label() }}{{ form.name(id='xxx', class='yyy') }}<br>
      {# password字段 #}
      {{ form.password.label() }}{{ form.password() }}<br>
      {# 登录字段 #}
      {{ form.submit() }}
      </form>
  • Bootstrap 进行渲染

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {% extends 'bootstrap/base.html' %}

    {% block title %}表单渲染{% endblock %}

    {# 导入渲染工具 #}
    {% import 'bootstrap/wtf.html' as wtf %}

    {# 渲染表单 #}
    {% block content %}
    <div class="container">
    {{ wtf.quick_form(form) }}
    </div>
    {% endblock %}
  • 验证器使用

    1. 需要导入相关的验证器类

      1
      2
      # 导入需要的验证器类
      from wtforms.validators import Length
    2. 在需要验证的字段后添加相关的验证器对象

      1
      name = StringField('用户名:', validators=[Length(6, 20, '必须在6-20个字符之间')])
    3. 表单提交的校验需要使用函数 validate_on_submit

      1
      2
      3
      4
      5
      6
      7
      8
      @app.route('/', methods=['GET', 'POST'])
      def index():
      name = None
      form = NameForm()
      # if request.method == 'POST':
      if form.validate_on_submit():
      name = form.name.data
      return render_template('form2.html', form=form, name=name)
  • 常见的字段类型

    字段类型 说明
    StringField 普通文本
    SubmitField 提交按钮
    PasswordField 密文字段
    HiddenField 隐藏字段
    TextAreaField 多行文本输入
    IntegerField 整数
    FloatField 浮点数
    BooleanField 复选框
    RadioField 单选框
    SelectField 下拉框
    FileField 上传文件
    DateField 日期
    DateTimeField 日期时间
  • 常见的验证器

    验证器 说明
    Length 规定字符长度
    DateRequired 确保字段有值
    Email 邮箱格式
    NumberRange 规定数字范围
    IPAddress ip 地址格式
    URL URL 格式
    EqualTo 验证字段相等
    Regexp 正则校验
  • 自定义验证函数

    1. 函数名称:validate_字段名
    2. 参数:self, field(表示要验证的字段)
    3. 验证失败时,需要抛出指定异常 ValidationError,验证通过返回 True
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      # 导入需要的验证器类
      from wtforms.validators import Length,ValidationError


      # 创建一个表单类
      class NameForm(FlaskForm):
      # name = StringField('用户名:', validators=[Length(6, 20, '必须在6-20个字符之间')])
      name = StringField('用户名:')
      password = PasswordField('密码:')
      submit = SubmitField('提交')

      def validate_name(self, field):
      if len(field.data) < 6:
      raise ValidationError('用户名不能少于6个字符')
      return True

      def validate_password(self, field):
      if not field.data:
      raise ValidationError('密码不能为空')
      return True

POST 重定向 GET

  • 说明:浏览器默认会记录最后的请求状态,当是 POST 请求时,用户点击刷新按钮,浏览器会提示是否重新提交表单,但是这种情况绝大多数都是没有必要的;最好的解决方案就是将 POST 请求重定向到当前的地址(GET)
  • 使用:redirect

会话技术

    • 设置

      • resp.set_cookie(key, value, max_age, expires)
      • max_age高级浏览器使用,类型为整数单位秒
      • expiresIE-8 浏览器,类型为日期或时间戳,优先级低
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        # 设置cookie
        @app.route('/set_cookie/')
        def set_cookie():
        import time
        from flask import make_response
        from datetime import datetime, timedelta
        resp = make_response('cookie已设置')
        # 设置过期时间
        # expires:兼容IE8及以下版本的浏览器,参数类型datetime或时间戳
        # max_age:高级浏览器选用此参数,类型为整数,单位为s,优先级较高
        # resp.set_cookie('name', 'dahua', max_age=10)
        # expires = time.time() + 10
        expires = datetime.utcnow() + timedelta(seconds=100)
        resp.set_cookie('name', 'dahua', expires=expires)
        return resp
    • 获取

      • request.cookies.get('key')
        1
        2
        3
        4
        # 获取cookie
        @app.route('/get_cookie/')
        def get_cookie():
        return request.cookies.get('name', '未设置')
    • 删除

      • resp.delete_cookie(key)
        1
        2
        3
        4
        5
        6
        # 删除cookie
        @app.route('/del_cookie/')
        def del_cookie():
        resp = make_response('cookie已删除')
        resp.delete_cookie('name')
        return resp
  • session

    • session[key] = value

    • 有效期

      • app.config['SESSION_PERMANENT'],设置为 False 浏览器关闭就失效,设置 True 后有效期才有意义
      • app.config['PERMANENT_SESSION_LIFETIME'],设置有效期,默认 31 天,也可以传递整数
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        # session是否永久生效,设置为False浏览器关闭后就失效
        app.config['SESSION_PERMANENT'] = True
        # session有效期,默认是31天,可以是一个整数,设置为永久生效才有意义
        # print(app.config['PERMANENT_SESSION_LIFETIME'])
        app.config['PERMANENT_SESSION_LIFETIME'] = 10

        # 设置session
        from flask import session
        @app.route('/set_session/')
        def set_session():
        session['name'] = 'ergouzi'
        return 'session已设置'
    • 获取

      • session.get(key, 默认值)
        1
        2
        3
        4
        # 获取session
        @app.route('/get_session/')
        def get_session():
        return session.get('name', '未设置')
    • 删除

      • 指定:session.pop(key, None)
      • 所有:session.clear()
        1
        2
        3
        4
        5
        6
        7
        8
        # 删除session
        @app.route('/del_session/')
        def del_session():
        # 删除指定session
        session.pop('name', None)
        # 清空session,清空所有session
        session.clear()
        return 'session已删除'