2018-12-17 初识Flask

一、flask介绍

  Flask和Django一样,也是一个基于MVC设计模式的Web框架

  Flask流行的主要原因:

    1. 有非常齐全的官方文档,上手非常方便
        
    2. 有非常好的拓展机制和第三方的拓展环境,工作中常见的软件都有对应的拓展,自己动手实现拓展也很容易
                
    3. 微型框架的形式给了开发者更大的选择空间

二、安装flask

    pip install flask

三、安装管理库

    pip install flask-script

四、导入管理库中的Manager,用Manager管理项目

将flask对象交给Manager管理如下
manage = Manager(app)
manage.run()

  用manage启动项目

    python hello.py runserver -h 0.0.0.0 -p 8080 -d

  参数说明:

    -h 指定主机地址
    -p 指定端口号
    -d 指定用debug方式启动 
    
    路由接收参数
    @app.route('/stu/<id>/')
    def stu(id):
        # 接收的参数是字符串
        return f'stu id: {id}'
    
    @app.route('/grade/<int:id>/')
    def grade(id):
        # 指定接收的参数是int类型
        return f'grade id: {id}'

    可以指定许多接收参数类型但常用的就是int和string
@app.route('/path/<path:url>/')
def get_path(url):
    # 接收url中path后面的全部路径
    return f'url: {url}'


@app.route('/get_uuid/')
def get_uuid():
    import uuid
    # 获取一个uuid,uuid是一个唯一的字符串
    return str(uuid.uuid4())


@app.route('/uuid/<uuid:u>/')
def my_uuid(u):
    # 指定接收的参数为uuid类型的值
    return f'uuid: {u}'

  python框架主要

    Django
    Flask
    Tronado  
    Sanic    性能最优但是一般不再线上使用
    Twisted  主要是针对底层协议

  将flask项目提炼成mvc的形式

  安装蓝图(就是模块化管理路由),相当于Django中的urls.py

    pip install flask-blueprint

  使用:

    from flask import Blueprint
    # 模块化管理路由 Blueprint
    # 第一步:生成蓝图对象
    blueprint = Blueprint('first', __name__)
    # 第二步:在管理文件中注册一个蓝图,也就是将应用绑定在应用上
    app.register_blueprint(blueprint=blueprint, url_prefix='/app')
    # url_prefix: url访问前缀127.0.0.1/app/
    前缀相当于Django中的namespace

  路由跳转

@blueprint.route('/redirect/')
def my_redirect():
    # 跳转到无参方法
    # redirect: 跳转
    # url_for:反向解析出地址
    # 'first.hello_world': 蓝图第一个 参数.跳转到的函数名
    return redirect(url_for('first.hello_world'))

@blueprint.route('/redirect_id/')
def stu_redirect():
    # 有参数的跳转
    return redirect(url_for('first.stu', id=3))

五、请求与相应

  1. 请求:

@blueprint.route('/req/', methods=['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
def req():
    # methods = ['GET', 'POST']指定能接收的请求类型
    if request.method == 'GET':
        # 获取get请求中的参数
        # request.agrs.get(key)
        # 获取get请求中多个相同key的参数的值,返回列表
        # request.args.getlist(key)
        return 'hello get'

    if request.method == 'POST':
        # 获取post请求中传递的参数
        # request.form.get(key)
        # 获取post请求中相同key的参数,返回列表
        # request.form.getlist(key)

  2. 响应:

    从flask中导入响应模块
    创建响应对象
    res = make_response(响应内容,状态码)
    设置cookie
    res.set_cookie(key, value, max_age, expires)
    key: 键
    value: 值
    max_age: 过期时间,秒为单位
    expires: 过期时间,datetime为单位
    删除cookie
    方式一: res.delete_cookie(key)
    方式二: res.set_cookie(key, value, max_age=0) 过期时间为0相当于删除

六、装饰器验证登录状态

  装饰器的三个特点:

    1. 外层函数内嵌内层函数
    2. 内层函数调用外层函数的参数
    3. 外层函数返回内层函数

  自定义一个登录简单的登录验证装饰器

from flask import session, redirect, url_for

from functools import wraps

def login_status(func):
    # 使用functools装饰该内嵌函数,用于维持被装饰函数的部分属性,如__name__/__doc__/__module__
    @wraps(func)
    def check_login(*args, **kwargs):
        # 获取登录后在session中设置的user_id,由于标识用户登录状态
        user_id = session.get('user_id')
        if user_id:
            # 有user_id说明登录,则继续执行被装饰函数
            return func(*args, **kwargs)
        else:
            # 如果没有user_id说明没有登录,则跳转道登录界面
            return redirect(url_for('first.hello_world'))

    return check_login

  functools库函数简介
  1. update_wrapper
  更新一个包裹(wrapper)函数,使其看起来更像被包裹(wrapped)的函数。

  可选的参数指定了被包裹函数的哪些属性直接赋值给包裹函数的对应属性,同时包裹函数的哪些属性要更新而不是直接接受被包裹函数的对应属性,参数assigned的默认值对应于模块级常量WRAPPER_ASSIGNMENTS(默认地将被包裹函数的 namemodule,和 doc 属性赋值给包裹函数),参数updated的默认值对应于模块级常量WRAPPER_UPDATES(默认更新wrapper函数的 dict 属性)。

  这个函数的主要用途是在一个装饰器中,原函数会被装饰(包裹),装饰器函数会返回一个wrapper函数,如果装饰器返回的这个wrapper函数没有被更新,那么它的一些元数据更多的是反映wrapper函数定义的特征,无法反映wrapped函数的特性。

  2. wraps
  这个函数可用作一个装饰器,简化调用update_wrapper的过程,调用这个函数等价于调用partial(update_wrapper, wrapped = wrapped, assigned = assigned,updated = updated)。

from functools import wraps

def my_decorator(f):
    @wraps(f)
    def wrapper(*args,**kwds):
        print "Calling decorated function"
        return f(*args,**kwds)
    return wrapper

@my_decorator
def example():
    """DocString"""
    print "Called example function"

example()
print example.__name__
print example.__doc__

  控制台输出,

    Calling decorated function
    Called example function
    example
    DocString

  可以看到,最终调用函数example时,是经过 @my_decorator装饰的,装饰器的作用是接受一个被包裹的函数作为参数,对其进行加工,返回一个包裹函数,代码使用 @functools.wraps装饰将要返回的包裹函数wrapper,使得它的 namemodule,和 doc 属性与被装饰函数example完全相同,这样虽然最终调用的是经过装饰的example函数,但是某些属性还是得到维护。

如果在 @my_decorator的定义中不使用 @function.wraps装饰包裹函数,那么最终example.name 将会变成'wrapper',而example.doc 也会丢失。

将 @wraps(f)注释掉,然后运行程序,控制台输出,

    Calling decorated function
    Called example function
    wrapper
    None

  所以在上面我们自定义的登录状态验证装饰器就需要用到functools库中的wraps来装饰内嵌函数,维持被装饰的函数的_name_、_module_、_doc_属性,如果不用wraps装饰当我们在视图函数中进行反向解析被装饰函数时,会报如下错误:

1.png

如:

@blueprint.route('/')
@login_status
def hello_world():

    return 'hello'

@blueprint.route('/login/', methods=['GET', 'POST'])
def login():

    if request.method == 'GET':

        return render_template('login.html')

    if request.method == 'POST':
        # 模拟登录
        username = request.form.get('username')
        password = request.form.get('password')
        if username == 'root' and password == '123123':
            # 设置session值
            session['user_id'] = 1
        return redirect(url_for('first.hello_world'))  # 如果没有用@wraps装饰装饰器的内嵌函数则者句话在做反向解析时就会抛出上面提到的异常

推荐阅读更多精彩内容