zoukankan      html  css  js  c++  java
  • Flask 入门(第一篇)

    1. 认识 Flask

    Flask 是一个微型 Web 框架,依赖于 jinjia2 模板系统和 Werkzeug WSGI(本质为 Socket 服务端) 服务,默认情况不支持数据库抽象层、表单验证,如果要使用可以进行拓展。

    Flask 常用扩展包:

    • Flask-SQLalchemy:操作数据库;
    • Flask-migrate:管理迁移数据库;
    • Flask-Mail:邮件;
    • Flask-WTF:表单;
    • Flask-script:插入脚本;
    • Flask-Login:认证用户状态;
    • Flask-RESTful:开发REST API的工具;
    • Flask-Bootstrap:集成前端Twitter Bootstrap框架;
    • Flask-Moment:本地化日期和时间;

    安装:

    pip3 install flask
    

    Werkzeug:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from werkzeug.wrappers import Request, Response
    
    @Request.application
    def hello(request):
        return Response('Hello World!')
    
    if __name__ == '__main__':
        from werkzeug.serving import run_simple
        run_simple('localhost', 4000, hello)
    

    尝鲜:

    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route("/")
    def index():
        return "Index Page"
    
    
    if __name__ == "__main__":
        app.run()
    

    官网地址:

    2. 路由系统

    2.1 五种常用路由

    Flask 常用五种路由:

    # 前四种可以接收变量
    @app.route('/user/<username>')      # /user/rose
    @app.route('/post/<int:post_id>')   # /post/1
    @app.route('/post/<float:post_id>') # /post/1.2
    @app.route('/post/<path:subpath>')     # /post/static/css/xx.css
    @app.route('/login', methods=['GET', 'POST'])
    

    示例:

    @app.route('/user/<username>')
    def show_user_profile(username):
        
        return 'User %s' % username
    

    以上五种路由基于以下对应关系:

    DEFAULT_CONVERTERS = {
        'default':          UnicodeConverter,
        'string':           UnicodeConverter,
        'any':              AnyConverter,
        'path':             PathConverter,
        'int':              IntegerConverter,
        'float':            FloatConverter,
        'uuid':             UUIDConverter,
    }
    

    让 Flask 支持正则:https://segmentfault.com/q/1010000000125259

    2.2 反解 URL

    url_for() 方法可以根据视图函数名,反解出 url,类似于 Django 的 reverse() 方法:

    # 接收视图函数名字,返回所对应的绝对 URL
    url_for('index', _external=True)
    
    from flask import Flask, redirect, url_for
    
    app = Flask(__name__)
    
    
    @app.route("/")
    def index():
        return "Index Page"
    
    @app.route("/hello")
    def hello():
        return "Hello World!"
    
    
    @app.route('/user/<username>')
    def profile(username):
        return username
    
    with app.test_request_context():
        print(url_for('hello'))
        print(url_for('profile', username='rose'))
        print(url_for('index'))
    
    if __name__ == "__main__":
        app.run()
    

    test_request_context() 告诉 Flask,即使我们使用Python shell,它也会像处理请求一样行事。

    运行结果如下:

    /hello
    /user/rose
    /
    
    

    2.3 正则匹配

    from flask import Flask
    from werkzeug.routing import BaseConverter
    
    class Regex_url(BaseConverter):
        def __init__(self,url_map,*args):
            super(Regex_url,self).__init__(url_map)
            self.regex = args[0]
    
    app = Flask(__name__)
    app.url_map.converters['re'] = Regex_url
    
    @app.route('/user/<re("[a-z]{3}"):id>')
    def hello_itcast(id):
        return 'hello %s' % id
    
    
    if __name__ == '__main__':
        app.run()
    
    

    3. 模板系统

    3.1 静态文件和模板路径设置

    from flask import Flask, redirect, url_for, render_template
    
    app = Flask(__name__)       # 静态文件和模板依赖这句
    
    @app.route('/hello/')
    def hello():
        return render_template('t1.html')
    
    
    if __name__ == '__main__':
        app.run()
    
    

    静态文件语法:

    url_for('static', filename='style.css')
    
    

    静态文件目录 static 在项目根目录,与程序同级(需要自己创建),Flask 默认回会去 static 中查找,你可以在里面新建子文件夹:

    <img src="{{ url_for('static', filename='imgs/1.png') }}">
    
    

    模板路径设置

    Flask 会在 templates 文件夹里寻找模板。所以,如果你的应用是个模块,这个文件夹应该与模块同级;如果它是一个包,那么这个文件夹作为包的子目录:

    情况 1: 模块:

    /application.py
    /templates
        /hello.html
    
    

    情况 2: 包:

    /application
        /__init__.py
        /templates
            /hello.html
    
    

    3.2 模板语言及自定义模板

    Flask 使用的是 Jinja2模板,与 Django 一样,因此语法也没什么差别。

    自定义模板

    在模板中使用 Python 函数:

    app.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from flask import Flask, render_template
    
    app = Flask(__name__)
    
    
    def func():
    
        return '<h1>Flask 自定义模板</h1>'
    
    
    @app.route('/index', methods=['GET', 'POST'])
    def index():
        return render_template('index.html', func=func)
    
    if __name__ == '__main__':
        app.run()
    
    

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        {{ func() | safe }}			<!-- safe 不转义 -->
    </body>
    </html>
    
    

    3.3 过滤器

    语法:

    变量名 | 过滤器
    
    

    字符串过滤器:

    # 禁用转义 safe
    <p>{{ '<em>hello</em>' | safe }}</p>
    
    
    # capitalize:把变量值的首字母转成大写,其余字母转小写;
    <p>{{ 'hello' | capitalize }}</p>
    
    # lower:把值转成小写;
    <p>{{ 'HELLO' | lower }}</p>
    
    # upper:把值转成大写;
    <p>{{ 'hello' | upper }}</p>
    
    # title:把值中的每个单词的首字母都转成大写;
    <p>{{ 'hello' | title }}</p>
    
    #trim:把值的首尾空格去掉;
    <p>{{ ' hello world ' | trim }}</p>
    
    # reverse:字符串反转;
    <p>{{ 'olleh' | reverse }}</p>
    
    # format:格式化输出;
    <p>{{ '%s is %d' | format('name',17) }}</p>
    
    # striptags:渲染之前把值中所有的HTML标签都删掉;
    <p>{{ '<em>hello</em>' | striptags }}</p>
    
    

    列表操作:

    # first:取第一个元素
    <p>{{ [1,2,3,4,5,6] | first }}</p>
    
    # last:取最后一个元素
    <p>{{ [1,2,3,4,5,6] | last }}</p>
    
    # length:获取列表长度
    <p>{{ [1,2,3,4,5,6] | length }}</p>
    
    # sum:列表求和
    <p>{{ [1,2,3,4,5,6] | sum }}</p>
    
    # sort:列表排序
    <p>{{ [6,2,3,1,5,4] | sort }}</p>
    
    

    自定义过滤器

    过滤器的本质是函数,两种实现方式:一种是通过Flask应用对象的add_template_filter方法。还可以通过装饰器来实现自定义过滤器,与内置过滤器重名则覆盖:

    1、add_template_filter,第一个参数是函数名,第二个参数是自定义的过滤器名称

    def filter_double_sort(ls):
        return ls[::2]
    app.add_template_filter(filter_double_sort,'double_2')
    
    

    2、装饰器来实现自定义过滤器,参数为过滤器名字:

    @app.template_filter('db3')
    def filter_double_sort(ls):
        return ls[::-3]
    
    

    app.py

    from flask import Flask, render_template
    
    app = Flask(__name__)
    
    
    def filter_double_sort(ls):
        return ls[::2]
    app.add_template_filter(filter_double_sort, 'double_2')
    
    @app.route('/')
    def index():
        data_list = [1, 2, 5, 8]
        return render_template('index4.html', data_list=data_list)
    
    if __name__ == "__main__":
        app.run()
    
    

    index4.html

    <p>{{ data_list | double_2}}</p>
    
    

    3.5 宏

    类似于 python 中的函数,宏的作用就是在模板中重复利用代码,避免代码冗余。

    定义不带参数的宏:

    <!-- 定义宏 -->
    {% macro input() %}
      <input type="text" name="username" value="" size="30"/>
    {% endmacro %}
    
    <!-- 调用宏 -->
    {{ input() }}
    
    

    定义带参数的宏:

    {% macro input(name,value='',type='text',size=20) %}
        <input type="{{ type }}"
               name="{{ name }}"
               value="{{ value }}"
               size="{{ size }}"/>
    {% endmacro %}
    
    <!-- 调用宏,并传递参数 -->
    {{ input(value='name',type='password',size=40)}}
    
    

    还可以把宏单独抽取出来,封装成 html 文件,其它模板中导入使用文件名可以自定义 macro.html

    {% macro function() %}
    
        <input type="text" name="username" placeholde="Username">
        <input type="password" name="password" placeholde="Password">
        <input type="submit">
    {% endmacro %}
    
    

    在其它模板文件中先导入,再调用:

    {% import 'macro.html' as func %}
    {% func.function() %}
    
    

    宏、继承(extend)和包含(include)区别:

    • 宏(Macro)、继承(Block)、包含(include)均能实现代码的复用
    • 继承(Block):本质是代码替换,一般用来实现多个页面中重复不变的区域
    • 宏(Macro):功能类似函数,可以传入参数,需要定义、调用
    • 包含(include):是直接将目标模板文件整个渲染出来

    3.4 特殊变量和方法

    • config 对象: Flask的config对象,也就是 app.config 对象,{{ config.SQLALCHEMY_DATABASE_URI }}
    • get_flashed_messages方法:应用在模板中一般用于获取错误信息,返回之前在Flask中通过 flash() 传入的信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages() 方法取出。
    {% for message in get_flashed_messages() %}
        {{ message }}
    {% endfor %}
    
    

    4. 公共组件

    4.1 请求和响应

    1、请求 request

    浏览器发送过来的 HTTP 请求,被 flask 封装在 request中(werkzeug.wrappers.BaseRequest) 中,提供了一下字段或方法以供使用:

    request.method			# 请求方法
    request.args			# 获取GET请求参数
    request.form			# 获取POST请求参数
    request.values
    request.files			# 获取上传文件
    request.cookies			# cookies
    request.headers			# 请求头
    request.path
    request.full_path
    request.script_root
    request.url				# 请求 URL 
    request.base_url		# 获取请求路径
    request.url_root
    request.host_url
    request.host			# 获取ip和端口
    
    

    2、响应 response

    1、模板渲染、重定向

    模板渲染:render_template、重定向:redirecturl_for

    from flask import Flask, render_template, request, redirect, url_for
    
    app = Flask(__name__)
    
    @app.route('/index/', method=['GET', 'POST'])
    def index():
        return render_template('index.html')
        # redirect(url_for('login'))		# 重定向
    
    

    2、错误信息

    from flask import Flask, abort, render_template
    app = Flask(__name__)
    
    
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        abort(404, 'Page Not Found!')		# 返回一个  Page Not Found! 的页面
    
    if __name__ == '__main__':
        app.run()
    
    

    3、make_reponse

    make_response 类似于 Django 的 HTTPResponse,用于返回字符串,下面是几个比较常见的用法:

    返回内容、页面:

    from flask import Flask, abort, render_template, make_response
    
    app = Flask(__name__)
    
    def func():
        return '<h1>Flask 自定义模板</h1>'
    
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        # return make_response('make response')			# 返回内容
        response = make_response(render_template('index.html', func=func))		# 返回页面
        # return response, 200,			# 返回内容和状态码
    
    

    responseflask.wrappers.Response 类型,同时 make_resposne 还可以用来设置响应头、设置 cookie 等:

    # response.delete_cookie
    # response.set_cookie
    # response.headers['X-Something'] = 'A value'
    
    
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        response = make_response(render_template('index.html', func=func))
        response.headers['hahaha'] = 'hello world'
        return response
    
    

    4.2 自定义错误页面

    如果在浏览器中输入不可用的路由,那么就会显示一个 404 状态的错误页面,这个页面太过简陋、平庸,并不是我们所希望的。好在 Flask 允许我们基于模板自定义错误页面,需要用到模块:flask-bootstrap

    安装:

    pip3 install flask-bootstrap
    
    

    下面来示范两个最常见的错误:404 和 500:

    1、app.py

    初始化 bootstrap,定义两个函数,用于处理 404 和 500 错误页面:

    from flask import Flask, abort, render_template
    from flask_bootstrap import Bootstrap
    
    
    app = Flask(__name__)
    bootstrap = Bootstrap(app)		# 初始化,千万别忘记
    
    @app.route('/index/', methods=['GET', 'POST'])
    def index():
        return "OK"
    
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('404.html'),404
    
    @app.errorhandler(500)
    def internal_server_error(e):
        return render_template('500.html'),500
    
    if __name__ == '__main__':
        app.run()
    
    

    2、在模板中定义一个母版 templates/errors/base.html,编辑如下:

    {%extends "bootstrap/base.html"%}
    
    {%block title %}Flask{% endblock %}
    
    {%block navbar %}
    <div class="navbar navbar-inverse" role="navigation">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle"
                data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="/">Flasky</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a href="/">Home</a></li>
                </ul>
            </div>
        </div>
    </div>
    
    {% endblock %}
    {% block content %}
    <div class="container">
        <div class="page-header">
            {% block page_content %}{% endblock %}
        </div>
    </div>
    {% endblock %}
    
    

    3、下面再编写 404.html500.html

    404.html

    {% extends "base.html" %}
    {% block title %}Flasky - Page Not Found{% endblock %}
    {% block page_content %}
    <div class="page-header">
        <h1>Not Found</h1>
    </div>
    {% endblock %}
    
    

    500.html

    {% extends "base.html" %}
    {% block title %}Flasky - internal server error{% endblock %}
    {% block page_content %}
    <div class="page-header">
        <h1>internal server error</h1>
    </div>
    {% endblock %}
    
    

    现在我们来访问一个不存在的页面看看:http://127.0.0.1:5000/main/

    似乎好看了点,当然你还可以定制更为高端一点。

    4.3 Session

    Session 依赖于 Cookies ,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。

    • 设置:session['username'] = 'xxx'
    • 删除:session.pop('username', None)
    • 获取:session.get('username')

    设置获取 Cookie

    from flask import Flask,make_response
    @app.route('/cookie')
    def set_cookie():
        resp = make_response('this is to set cookie')
        resp.set_cookie('username', 'itcast')
        return resp
    
    from flask import Flask,request
    #获取cookie
    @app.route('/request')
    def resp_cookie():
        resp = request.cookies.get('username')
        return resp
    
    

    示例:利用 Session 验证用户登录

    from flask import Flask, session, redirect, url_for, escape, request
    
    app = Flask(__name__)
    
    
    @app.route('/')
    def index():
        if 'username' in session:
            return '欢迎回来: %s' % escape(session['username'])
        return '你没有登录'		# 也可以重定向到登录页面
    
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if request.method == 'POST':
            session['username'] = request.form['username']
            return redirect(url_for('index'))
        return '''
            <form action="" method="post">
                <p><input type=text name=username>
                <p><input type=submit value=Login>
            </form>
        '''
    
    
    @app.route('/logout')
    def logout():
        # 从 session 移除 username
        session.pop('username', None)
        return redirect(url_for('index'))
    
    
    # 设置密钥
    app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
    
    
    if __name__ == '__main__':
        app.run()
    
    

    4.3 message

    message 是一个基于 Session 实现的用于保存数据的集合,其特点是:使用一次就删除

    app.py

    from flask import Flask, flash, redirect, render_template, request
    
    app = Flask(__name__)
    app.secret_key = 'some_secret'
    
    
    @app.route('/')
    def index1():
        return render_template('index2.html')
    
    
    @app.route('/set')
    def index2():
        v = request.args.get('p')
        flash(v)        # 将消息给下一个请求,并将其删除,模板必须调用 get_flashed_messages() 函数
        return 'ok'
    
    
    if __name__ == "__main__":
        app.run()
    
    

    index2.html

    模板中必须使用 get_flashed_messages() 才能获取到 message 传递的信息:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>messages</title>
    </head>
    <body>
        {% with messages = get_flashed_messages() %}		<!-- with xxx:取别名 -->
            {% if messages %}
            <ul class=flashes>
                {% for message in messages %}
                <li>{{ message }}</li>
                {% endfor %}
            </ul>
            {% endif %}
        {% endwith %}
    </body>
    </html>
    
    

    首先请求:http://127.0.0.1:5000/set?p=123index() 函数获取 p 的值,并将其传递给下一个请求(传递后即将其删除)。

    第二个请求访问:http://127.0.0.1:5000/,从而获得上一次请求传递的信息:

    4.4 请求钩子

    所谓请求钩子即是用来处理请求前、后,一些特定事情的功能实现。如:请求前连接数据库、请求后指定数据交互格式等。Flask 中请求钩子以装饰器形式实现:

    Flask 中请求钩子:

    • before_first_request:在处理第一个请求前运行,比如:数据库的连接,初始化操作
    • before_request:在每次请求前运行。
    • after_request:如果没有未处理的异常抛出,在每次请求后运行。
    • teardown_request:在每次请求后运行,即使有未处理的异常抛出。

    示例:基于 before_request 实现用户登录认证

    相比较装饰器而已有如下优势:

    • 装饰器实现用户登录认证,需要给每个需要认证的视图函数添加装饰器
    • before_request,只需一个即可。
    from flask import Flask, render_template, request, redirect, session, url_for
    
    app = Flask(__name__)
    
    app.secret_key = "123$456"
    
    # 基于flask里请求扩展来做
    @app.before_request
    def process_request(*args, **kwargs):
        """不管访问哪个视图函数,都会实现执行这个函数,也就意味着每次都会检查是否登录"""
        # 访问/login的时候还没有登录,就会一直重定向到登录页,所以就要设置个白名单,如果请求地址是/login,就返回None
        if request.path == "/login":
            return None
    
        # 1.登录验证功能
        user = session.get('user_info')
        # 2.如果登录信息正常,什么都不做,程序继续其他执行
        if user:
            return None
        # 3.如果登录验证不通过,就重定向到登录页面
        return redirect("/login")
    
    
    @app.route("/index", methods=['GET'])
    def index():
        name = session.get('user_info', None)
        return 'Welcome, %s' % name
    
    
    @app.route("/login", methods=['GET', 'POST'])
    def login():
        if request.method == "GET":
            return render_template("login.html")
        else:
            user = request.form.get("username")
            pwd = request.form.get("password")
            if user == "rose" and pwd == "123456":
                session['user_info'] = user
                return redirect("/index")
    
            return render_template("login.html", **{"error": "用户名和密码错误"})
    
    
    if __name__ == "__main__":
        app.run()
    
    

    也可以有多个请求钩子,但是要注意的是当有多个请求中间件时,执行顺序是不同的:

    • before_request:从上到下按照顺序执行
    • after_request:从下到上的顺序执行
    • 当有多个 before_request 时,若第一个有 return 语句,那么后面的 before_request 将被拦截不被执行,但是 after_request 会继续执行

    4.5 中间件

    Flask 程序在运行过程中会事先加载 wsgi_app,基于此我们可以拓展做些别的事情:

    from flask import Flask
    app = Flask(__name__)
    
    @app.route("/login", methods=['GET', 'POST'])
    def index():
        pass
    
    class Md(object):
        def __init__(self, old_wsgi_app):
            self.old_wsgi_app = old_wsgi_app
    
        def __call__(self, environ, start_response):
            print("开始之前")
            ret = self.old_wsgi_app(environ, start_response)
            print("结束之后")
            return ret
    
    if __name__ =="__main__":
        app.wsgi_app = Md(app.wsgi_app) # 相当于把wsgi_app给更新了
        app.run()
    
    

    5. 表单

    5.1 表单拓展 WTForms

    表单由三个部分组成:表单标签、表单域、表单按钮。Flask 通过Flask-WTF 实现表单功能。它封装了WTForms,可以生成表单、以及验证表单数据。

    安装:

    pip3 install Flask-WTF
    pip3 install flask-moment
    
    

    WTForms 支持的 HTML 标准字段:

    StringField	    # 文本字段
    TextAreaField	# 多行文本字段
    PasswordField	# 密码文本字段
    HiddenField	    # 隐藏文本字段
    DateField	    # 文本字段,值为datetime.date格式
    DateTimeField	# 文本字段,值为datetime.datetime格式
    IntegerField	# 文本字段,值为整数
    DecimalField	# 文本字段,值为decimal.Decimal
    FloatField	    # 文本字段,值为浮点数
    BooleanField	# 复选框,值为True和False
    RadioField	    # 一组单选框
    SelectField	    # 下拉列表
    SelectMultipleField	    # 下拉列表,可选择多个值
    FileField	    # 文本上传字段
    SubmitField	    # 表单提交按钮
    FormField	    # 把表单作为字段嵌入另一个表单
    FieldList	    # 一组指定类型的字段
    
    

    WTForms 常用验证函数:

    验证函数 说明
    DataRequired 确保字段中有数据
    AEqualTo 比较两个字段的值,常用于比较两次密码输入
    Length 验证输入的字符串长度
    NumberRange 验证输入的值在数字范围内
    URL 验证URL
    AnyOf 验证输入值在可选列表中
    NoneOf 验证输入值不在可选列表中

    跨站请求伪造保护 csrf

    设置一个密匙,生成加密令牌,再用令牌验证请求表单中数据真伪:

    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'hard to guess string'  # 密匙随便取 
    
    # 如果你设置的模板中存在表单,你只需要在表单中添加如下
    <form method="post" action="/">
        {{ form.csrf_token }}
    </form>
    
    # 如果没有模板中没有表单,你仍然需要一个 CSRF 令牌
    <form method="post" action="/">
        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
    </form>
    
    

    简单示例

    1、定义一个表单类:

    from flask_wtf import FlaskForm
    #导入自定义表单需要的字段
    from wtforms import SubmitField, StringField, PasswordField
    #导入wtf扩展提供的表单验证器
    from wtforms.validators import DataRequired, EqualTo
    
    # validators 不能为空
    class NameForm(FlaskForm):
        name = StringField(label='用户名', validators=[Required()])
        submit = SubmitField('提交')
    
    

    2、在模板中使用表单:

    <h1>Hello, {% if name %}{{ name }} {% else %} Stranger {% endif %}!</h1>
    
    <form method='post'>
        {{ form.hidden_tag() }}
        {{ form.name.label }} {{form.name() }}
        {{ form.submit() }}
    
        # 传入 HTML 属性
        {{ form.name.label }} {{form.name(id='id_name') }}
    </form>
    
    

    总是自己传属性太麻烦,而且表单样式过于丑陋。可以用 flask-bootstrap,它是对 bootstrap 进行了简单封装,安装好之后可以直接调用:

    <!--上面表单可用下面方式一次渲染-->
    {% import 'bootstrap/wtf.html' as wtf %}
    {{ wtf.quick_form(form) }}
    
    
    from flask_bootstrap import Bootstrap
    app = Flask(__name__)
    
    bootstrap = Bootstrap(app)
    
    

    wtf.quick_form(form) 函数参数为 Flask-WTF 表单对象

    <!-- 条件结果为 True,渲染 if else 中文字,否则渲染 else endif 中语句 -->
    <h1>Hello, {% if name %}{{ name }} {% else %} Stranger {% endif %}!</h1>
    
    

    5.2 基于 WTForms 实现用户登录验证

    实现方式:继承+WTForms +Flask-Bootstrap

    app.py

    from flask import Flask, render_template, redirect, url_for, session, request,flash
    from flask_bootstrap import Bootstrap
    from flask_moment import Moment
    
    #导入wtf扩展的表单类
    from flask_wtf import FlaskForm
    #导入自定义表单需要的字段
    from wtforms import SubmitField, StringField, PasswordField
    #导入wtf扩展提供的表单验证器
    from wtforms.validators import DataRequired, EqualTo
    app = Flask(__name__)
    app.config['SECRET_KEY'] = '1'
    
    bootstrap = Bootstrap(app)  # 使用 bootstrap 渲染表单
    moment = Moment(app)    # 本地化时间
    
    #自定义表单类,文本字段、密码字段、提交按钮
    class Login(FlaskForm):
        user = StringField(label=u'用户:', validators=[DataRequired()])
        pwd1 = PasswordField(label=u'密码', validators=[DataRequired()])
        pwd2 = PasswordField(label=u'确认密码', validators=[DataRequired()])
        submit = SubmitField(u'提交')
    
    @app.route('/', methods=['GET','POST'])
    def index():
        return render_template('index.html')
    
    #定义根路由视图函数,生成表单对象,获取表单数据,进行表单数据验证
    @app.route('/login', methods=['GET','POST'])
    def login():
        form = Login()
        if form.validate_on_submit():
            name = form.user.data
            pwd1 = form.pwd1.data
            pwd2 = form.pwd2.data
            print(name, pwd1, pwd2)
    
            if name == 'rose' and (pwd1 == pwd2 and pwd2 == '123'):
                return redirect(url_for('index'))
            else:
                flash(u'用户名或密码错误!')
    
        return render_template('login.html',  form=form)
    
    if __name__ == '__main__':
        app.run()
    
    

    include/base.html

    {% extends "bootstrap/base.html" %}
    
    {% block title %}Flask WTF{% endblock %}
    
    {% block head %}
    {{ super() }}
    <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
    <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
    {% endblock %}
    
    {% block navbar %}
    <div class="navbar navbar-inverse" role="navigation">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="/">Flask WTF</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a href="/">Home</a></li>
                </ul>
            </div>
        </div>
    </div>
    {% endblock %}
    
    {% block content %}
    <div class="container">
        <!-- get_flashed_messages() 显示错误信息 -->
    
        {% for message in get_flashed_messages() %}
        <div class="alert alert-warning">
            <button type="button" class="close" data-dismiss="alert">&times;</button>
            {{ message }}
        </div>
        {% endfor %}
    
        {% block page_content %}{% endblock %}
    </div>
    {% endblock %}
    
    {% block scripts %}
    {{ super() }}
    {{ moment.include_moment() }}
    {% endblock %}
    
    

    login.html

    继承 base.html

    {% extends "include/base.html" %}
    {% import "bootstrap/wtf.html" as wtf %}
    
    {% block title %}Flask WTF{% endblock %}
    
    {% block page_content %}
    <div class="page-header">
    
    </div>
    {{ wtf.quick_form(form) }}
    {% endblock %}
    
    

    将用户个人信息保存到会话中:

    app.py

    from flask import Flask, render_template, redirect, url_for, session, request,flash
    
    
    def login('/login', methods=['GET', 'POST']):
        ...
        # 会话中原有值
        old_name = session.get('name')
        if old_name is not None and old_name != form.name.data:
            flash('用户名错误!')
        session['name'] = form.name.data
        return redirect(url_for('index'))
     return render_template('login.html', form=form, name=session.get('name'))
    
    

    login.html

    <div class="page-header">
        <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1>
    </div>
    
    

  • 相关阅读:
    CTSC2018滚粗记
    HNOI2018游记
    NOIWC 2018游记
    PKUWC2018滚粗记
    HNOI2017 游记
    NOIP2017题解
    [HNOI2017]抛硬币
    [HNOI2017]大佬
    NOIP难题汇总
    [NOI2013]树的计数
  • 原文地址:https://www.cnblogs.com/midworld/p/11075990.html
Copyright © 2011-2022 走看看