经验拾忆(纯手工)=> Flask框架

发布时间:2019-09-29 21:56:01编辑:auto阅读(1868)

    Sanic 和 Flask 简要概述

    """
        Flask常用
        Sanic和Flask很像,于是按着Sanic官方文档学了一下,对比Flask学习并做下笔记,回顾一下
    """
    
    Flask:轻量级Web框架,三方组件齐全,用时安装,扩展灵活度高。
    Sanic: 和Flask特别像,基础语法,基础命名特别相似(使用几乎差不多)。
          Sanic是基于Uvloop(没用过,了解即可,windows不支持)实现,
          具有 异步-非阻塞的特性
          (网上也有说Sanic可以通过一些操作后,可以在Windows环境下使用,我试了貌似不行)
          (Linux下运行才会具有最好的性能表现)
          python3.5+ 原生支持原生协程 async+await, 这也可以在sanic的视图中用到,下面会介绍

    安装

    pip install flask
    pip install sanic

    入门程序 (Flask vs Sanic)

    Flask:
        from flask import Flask
        app = Flask(__name__)
        @app.route('/')
        def index():                    # 注意这行
            return 'hello'              # 注意这行
        
        if __name__=='__main__':
            app.run('0.0.0.0', 1119, debug=True)
            
    Sanic:
        from sanic import Sanic,response
        app = Sanic(__name__)
        @app.route('/')
        def index(request):                # 注意这行
            return response.text('hello')  # 注意这行
        
        if __name__=='__main__':
            app.run('0.0.0.0', 1119, debug=True)           

    使用Sanic的正确姿势 之 视图异步非阻塞

    上面入门程序可以看到 
    flask 和 sanic 的路由对应的视图函数,是一样的
    
    特殊的是:sanic支持异步高性能,每个 def 前面 需要加上 async 即可
    eg: 
        @app.route('/')
        async def index():             # 以后写每个视图函数前面都要加上 async
            return 'hello' 
    

    模板引擎(Flask VS Sanic)

    Flask:
       from flask import render_template
       
       app = Flask(__name__)
       
       @app.route('/')
       def home():
            return render_template('index.html',
                data=[dict(name='Tom', age=18), dict(name='Jerry', age=20)]
            )    # 这个模板渲染器是flask库中自带的,不需额外安装
       
    Sanic:
        pip install sanic-jinja2        # 还需安装这个三方插件
    
        from sanic import Sanic,response
        from sanic_jinja2 import SanicJinja2 as sj    # 导入就不说了,sj只是命名来方便调用
        
        app = Sanic(__name__)
        tp = sj(app)                        # 注意这里,这个和 flask三方应用注册是一个道理
        
        使用方式1:(Sanic特有的装饰器方式)
            @app.route('/')
            @tp.template('index.html')                   # 注意这行,以装饰器的方式渲染模板
            async def index(request):
                return {                                 # 注意这里,返回值就是向模板填充的数据
                    'data': [
                        dict(name='Tom', age=18),
                        dict(name='Jerry', age=8)
                    ]
                }
        使用方式2: (和Flask模板使用方法很像,很像)
            @app.route('/')
            async def index(request):
               return tp.render(            # 这个方法代替了 上一种方法的装饰器
                   'index.html', 
                   request,                 # 这个request参数,必须有
                   data = [                 # 模板渲染数据作为 redner()的 **kwargs参数来传递
                        dict(name='Tom', age=18),
                        dict(name='Jerry', age=8)
                   ]
               )
                
        if __name__=='__main__':
            app.run('0.0.0.0', 1119, debug=True)
        
        小结:
            Flask的模板渲染机制是集成在 flask库中,用 render_template方法来直接渲染模板
                并且,以方法参数的形式向模板传递数据
            Sanic的模板渲染机制是以第三方插件 sanic-jinja2 中的 SanicJinja2组件来实现。
                使用时,需要先注册到app中, 所接受的返回值,以装饰器的方式来渲染模板
                
        个人看法:
            某种程度上来说, Sanic 更加细粒度的将 功能 以第三方应用的方式划分出来。
            即便如此,但我还是喜欢 flask 中 render_template机制。
    

    response的各种返回方式对比分析(Flask VS Sanic)

    Flask:
        from flask import Markup, jsonify, send_file
        
        @app.route('/')
        def index():
            # return 'hello'                              # Content-Type='text/plain' 
            # return Markup('<h1>hello</h1>')             # 反转义
            # return jsonify(dict(name='Tom'))            # Content-Type-'application/json'
            # return send_file('static/1.png')            # 返回各种类型文件
            # return 'Tom'.encode('utf-8')                # 返回字节形式
            
            下面是修改 状态码 和 headers 的方式:
            # return render_template('home.html'), 220, {'a': 1}     
                格式: retrun 请求体,状态码,响应头
    Sanic: 
        @app.route('/')
        async def index(request):
            # return response.text('hello')             # Content-Type='text/plain'
            # return response.html('<h1>hello<h1/>')    # 代替反转义
            # return response.json(dict(name='Tom'))    # return response.redirect('/xxx')
            # return await response.file('static/1.png')  # 返回各种类型文件,注意有个 await 
            # return response.raw(b'Tom')               # 返回原生字节类型数据
            
            下面是修改 状态码 和 headers 的方式:
            1. 如果返回的响应体 为 模板,就用下面的方式
               @tp.template('index.html',status=220,headers={'name':'Tom'}) # 在装饰器参数里
            2. 如果返回的响应体 为 非模板内容,就用如下方式
               return response.text('hello',300,{'name':'Tom'})
               格式: response.text(请求体,状态码,响应头)
               
    小结:
        上面是针对response返回时,对各种数据类型的返回时可能用到的方式进行对比介绍。
        同时还对比讲述了 如何 修改 响应头 和 状态码
        
    个人看法:
        Flask:
            1. response各种变形返回方式 都封装了 flask这个模块之中
            2. response的响应信息(状态码,响应头)等, 通过 return 以 逗号或元组 方式构造返回.
                eg: return 响应体,状态码,响应头
        Sanic: 
            1. response的各种变形返回方式 都封装了 sanic 模块的 response 中 (分类更加明确)
            2. response的响应信息(状态码,响应头)等, 都放在函数中作为参数.
                eg: response.xx(响应体,状态码,响应头)

    request的各种请求方式对比分析 (Flask vs Sanic)

    Flask:
        from flask import request
        
        request.method      # 获取用户的请求方式:  GET 或 POST 
        request.args        # 接受get的url参数
        request.form        # 接受post的form表单   Content-Type='x-www-form-urlencoded'
        request.json        # 必须接受 Content-Type='application/json' 格式请求的数据
        request.data        # 请求数据对应的Content-Type除了表单(xxx-form)格式外,都可用此接受
        request.values      # 如有form 和 url 联合参数,用这个接受
        注:以上获取的对象都是 类字典对象, 可以使用字典的  get('前端name') 获取 value
            此外: 你还可以使用 to_dict()方法,就变成了纯种的字典  {k: v}
        
        img = request.files.get() # 接受文件类型
        img.save(img.filename)    # filename获取文件原始名, save直接保存, 默认当前路径。
        
        request.url         # http://localhost:5000/login
        request.path        # /login
        request.host        # localhost:5000
        request.host_url    # http://localhost:5000/
        request.remote_addr # 单纯获取IP地址
    
        
    Sanic:      
        flask中的request是导入进来的
        而sanic中的request是在视图参数之中(参考django)    eg: def f(request)  就是这个意思
        
        request.method    # 同Flask
        rqeust.args       # 同Flask
        request.form      # 同Flask
        request.json      # 请求若为表单(xxx-form)格式会报错, 除了表单都可接受 
        request.body      # 亲求若为表单(xxx-form)格式,则会出现一大堆 二进制信息,非表单都可接受
        
        request.url         # 同Flask
        request.path        # 同Flask
        request.host        # 同Flask
        request.ip          # 同样单纯获取IP,    属性名和上面 flask稍微有点不同

    路由讲解 (Flask vs Sanic)

    Flask:       
        @app.route(
            'login/<int:id>',          # 路由参数解析并 自动转换int类型, 冒号后为接受参数
            methods=['GET,'POST'],     # 建议全部大写
            endpoint='sign',           # 默认为下面的视图函数名,即login,用于url_for反向解析
            redirect_to='/xxx'         # 重定向跳转,注意请求过来直接跳转,不进入下面视图函数
        )      
        def login(id):                 # request是默认必须传递的参数, id是上面路由解析接收的
            return f'{id+1}'           # 路由参数 + python3.6新增语法实现 f-string 接受参数自增
            
        
    Sanic:
        @app.route(
            'login/<int:id>',          # 路由参数解析并 自动转换int类型, 冒号后为接受参数
            methods=['GET,'POST'],     # 建议全部大写
            name='sign'                # 同 flask 的 endpoint, 用于 url_for反向解析
        )      
        def login(request, id):        # request是默认必须传递的参数, id是上面路由解析接收的
            return f'{id+1}'           # 路由参数 + python3.6新增语法实现 f-string 接受参数自增

    Flask 模板 相关操作 (Flask)

    注:由于 sanic 的 template还不成熟, 花式操作我也就没找,下面就只讲一下 flask的常用模板操作
    
    模板宏(macro): 主要目的是为了前端代码的复用
        定义 模板宏 就和 定义 python的函数类似,  或者你可以把 macro 看作 python的 def
        eg:
            定义部分:可理解为函数定义
                {% macro user(type, value) %}
                    <input type="{{ type }}" value="{{ value }}">
                {% endmacro %}   
            调用部分:可理解为函数调用
                {{ user('text', '用户名') }}
                {{ user('password','密码') }}
                
    全局模板自定义函数:
        视图文件.py中定义:
            @app.template_global()
            def f(x):
                return x**2
        模板中调用:
            {{ f(5) }}
    
    模板过滤器自定义函数:
        视图文件.py中定义:
            @app.template_filter()
            def add(a, b):
                return a+b
        模板中调用:
            {{ 1 | add(2) }}
       
    
    模板继承: (可理解为挖坑 与 填坑)
        父级模板: header.html
            xxxxxxx前面若干内容
            {% block header %}                    # 这个 header,就是挖坑起的名,记住了
                这里面就是你挖的坑,啥也不用写
            {% endblock %}
            xxxxxxx后面若干内容
            
        子级模板:content.html
            {% extends 'header.html' %}           # 必须写上这句,这是填坑的暗号。。
            
            {% block header %}                    # 这个名header是和上面挖坑的名一样
                这里面就是你要填坑的数据
            {% endblock %}
            
            填完坑之后,这个 content.html子级模板的内容 = 父级模板内容 + 填坑内容
    
    模板代码块的导入(插入): 作用还是 html 代码的复用
        写好一个 html 代码块,放在 header.html中
            eg:  <h1> Tom <h1/>        # 注意,一个html文件中就写这一句就行
        另一个文件: index.html 写入 如下代码:
            xxxx前面若干内容
            {% include 'header.html' %}    # 这一句就可把 那一个html内容全部插入进来
            xxxx后面若干内容
            

    (中间件)钩子函数 (Flask vs Sanic)

    Flask:
        @app.errorhandler(404)        #  错误处理的钩子函数
        def f(err):
            return '出错了'
        注: 出现了异常,  before_request装饰的函数会立刻断掉,而 after_request的会依然倒序执行
        
        @app.before_request           #  视图函数执行之前
        def f():                      return None代表正常按顺序执行,
            xxx                       retrun其他值,就不会进入视图函数,直接response返回
        注: 如果有多个before_request,那么就 正序 装饰执行
            
        @app.after_request            #  视图执行之后,返回给 客户端之前
        def f(res):                   必须有个参数接受 response对象,并且return 回去
            xxx                       
            return res            
        注: 如果有多个before_request,那么就 倒序 装饰执行
        
        如果仍然不明白执行顺序,看西面例子:
        eg:
            @app.before_request
            def req1():
                print('请求来了-1')
            
            @app.before_request
            def req2():
                print('请求来了-2')
            
            @app.after_request
            def res1(response):
                print('请求走了-1')
            
            @app.after_request
            def res1(response):
                print('请求走了-2')
    
            
            @app.route('/lin')
            def lin():
                print('进入视图')
                return '11'
            
            结果>>>
                请求来了-1
                请求来了-2
                进入视图
                请求走了-2
                请求走了-1        # 这两个response是逆序的
        
    Sanic:
        from sanic.exceptions import NotFound
        @app.exception(NotFound)   # 错误处理的钩子函数
        def f(request, err):
            return response.text('出错了')
            
        请求,返回中间件和 flask大同小异,顺序有些区别,我直接上例子了
        eg:
            @app.middleware('request')
            async def req(request):
                print('请求来了-1')
            
            @app.middleware('request')
            async def req(request):
                print('请求来了-2')
            
            @app.middleware('response')
            async def res(request, response1):
                print('请求走了-1')
                return response1
            
            @app.middleware('response')
            async def res(request, response1):
                print('请求走了-2')
                return response1
            
            @app.route(xxx) 
            async def f(request):
                print('进入视图')
                return response.text('xx')
                
            结果>>> 
                请求来了-1
                请求来了-2
                进入视图
                请求走了-2        # 注意这里即可,只返回一个。下面不同点会详讲
                
    总结:Flask 与 Sanic 中间件返回顺序对比
        相同点: 
            request处理部分 : 装饰器代码vs执行流程  => 正序
            response处理部分: 装饰器代码vs执行流程  => 逆序
        不同点:
            Sanic 只执行     最后一个 用装饰器注册的 response
            Flask 执行顺序是  全部     用装饰器注册的 逆序返回的 response
    

    蓝图 (Flask vs Sanic)

    蓝图使用三部曲:
        1. 新建目录和文件,创建蓝图对象
        2. 在主app文件中, 导入蓝图对象
        3. 注册蓝图对象
    
    Flask:
        1. 新建 /Admin/user.py,写入如下代码
            from flask import Blueprint
            user_bp = Blueprint('user_bp', __name__, url_prefix='/admin') # 增加url前缀
            @user_bp.route('/user')
            def f():
                return 'admin-user'
        
        2. 在app中导入 蓝图对象
            from Admin.user import user_bp
            
        3. 注册蓝图
            app.register_blueprint(user_bp)   # 这里也可以写url前缀, 如果写了就会覆盖上面写的
            
    Sanic:
        1. 新建 /Admin/user.py,写入如下代码
            from sanic import Blueprint, response
            user_bp = Blueprint( __name__, url_prefix='/admin')
            @user_bp.route('/user')
            async def f(request):
                return response.text('admin-user')
        
        2. 在app中导入 蓝图对象(同Flask)
            from Admin.user import user_bp
            
        3. 注册蓝图 (本来是和flask一样用register_blueprint,后来版本更新改用 blueprint注册)
            app.blueprint(user_bp)   # 这里也可以写url前缀, 如果写了就会覆盖上面写的
        
    注:Flask的蓝图对象,同 Flask类似,都具有模板路径、静态文件路由 与 静态文件本地路径的配置
        因此,蓝图实例化的时候,配置响应参数即可:
            template_folder = 'xxx'     # 对应本地模板路径   ,默认 templates
            static_folder = 'xxx'       # 对应本地文件路径   ,默认 static
            static_url_path = '/xxx'    # 对应url路径       ,默认 /static
    注2: 如果蓝图 和 app 的 模板或静态文件命名重复,那么会优先选择  app下的模板或静态文件

    CBV (Flask vs Sanic)

    CBV(Class-Based-View): 就是拿类当作视图 (我们之前使用的函数作为视图)
    Flask 的 CBV感觉没 FVB好用, CBV是Django的重点
    
    Flask:
        from flask import views
        
        class UserView(views.MethodView):
            methods = ['GET']                # 这里写 的是 允许的请求方式
            decorators = [装饰器名,]            # 全局装饰器顺序装饰, 单独给函数加@装饰器也可以
            
            def get(self,*args, **kwargs):
                return xx
            def post(self, *args, **kwargs):
                return xx
            app.add_url_rule( '/user',None,UserView.as_view('endpoint名') )
    
    Sanic:
        from sanic.views import HTTPMethodView
            
        class UserView(HTTPMethodView):  
            async def get(self, request):        
                return text('get')
        
            async def post(self, request):
                return text('post')
        
        app.add_route(UserView.as_view(), '/')    # Sanic  视图在前,路由在后。
    
    总结: 讲道理,CBV在这两个轻型框架感觉用的很笨拙。。 还是很用CBV较好
    

    Flask的flash (Flask)

    flash原理: 服务器给flash设置了值,那么用户每请求一次,就会把session放到用户cookie中
        (后面会提到session插件方法)
        与此同时也把 flash值记录在里面。 flash就相当于一跟管道
        flash():                      # 把值塞进管道
        get_flashed_messages():       # 把值从管道取出来
        
    
    from flask import flash, get_flashed_messages
    
    # flash是基于 session来实现的,所以需要写一句:
    app.secret_key = '111'
    
    @app.route('/lin')
    def lin():
        flash('666')
        return redirect('/user')
    
    @app.route('/user')
    def user():
        msg = get_flashed_messages()
        print(msg)    # 注意从 flash取出来的是列表,因为你可以把不同数据多次 填入 flash
        return ''
    >>> [666]     

关键字