欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python3-开发进阶Flask的基础(2)

程序员文章站 2022-04-04 15:25:28
知识回顾 1、django 和flask区别? 最大的不同就是django请求相关的数据,通过参数一个一个传递过去的,而flask就是先把放在某个地方,然后去取,这个东西叫上下文管理 2、什么是wsgi? web服务网关接口,wsgi就是一个协议和规范,实现该协议的模块: -wsgiref -wer ......

知识回顾

1、django 和flask区别?

  最大的不同就是django请求相关的数据,通过参数一个一个传递过去的,而flask就是先把放在某个地方,然后去取,这个东西叫上下文管理

2、什么是wsgi?

  web服务网关接口,wsgi就是一个协议和规范,实现该协议的模块:

  -wsgiref

  -werkzeug

  实现其协议的模块本质上就是socket服务端用于接收用户请求,并处理,

  一般web框架基于wsgi实现,这样实现关注点分离(通俗的说业务相关的,交给框架)

from werkzeug.wrappers import  response
from werkzeug.serving import run_simple

def run_sever(environ,start_response):
    response=response('duoduo')   # 封装一下
    return response(environ,start_response)

if __name__ == '__main__':
    run_simple('127.0.0.1',8000,run_sever)

3、flask提供的功能

配置文件 

  所有配置文件都在flask的对象.config中

  flask的对象.config.from_object('引入类的路径')   小写也是生效的

  应用: importlib 、getattr

    django中间件

    rest framework全局配置

session

  加密后放置在用户浏览器的cookie中

  流程:

    请求到来--------》视图函数---------》请求结束

  配置文件   生命周期31天   加密设置

路由

  带参数的装饰器,自定义装饰器放下面

  常用url,methods,endpoint   反向 url_for

视图

  fbv

特殊装饰器

  before_first_request

  before_request

  after_request

  template_global()

  template_filter()

  errorhandler(404)

请求和响应

  请求:request

  响应:四种 字符串、render_template(模板)、redirect、jsonify (序列化)

  加响应头就make_response

 

一、路由和视图

from flask import flask
duo=flask(__name__)

@duo.route('/index')
def index():
    print('index')
    return 'index'

if __name__ == '__main__':
    duo.run()

我们今天开看看route的源码是什么样子的:

先不看中间的,发现定义阶段只是返回了 函数decorator的名字

python3-开发进阶Flask的基础(2)

1、限制性  decorator=duo.route('/index')

2、@decorator   这就和装饰器的原理一样,执行的话就把被装饰的函数名字传进去

3、上图中的f就是index函数,而rule就是(’/index‘)

python3-开发进阶Flask的基础(2)

这里就可以看出这个装饰器实质上就是:

duo.add_url_rule('/index', none, index)

我们注释掉装饰器,就用这一句代码跑跑看,脚本跑起来没问题!

我们继续看。add_url_rule里面的源码:

python3-开发进阶Flask的基础(2)

 验证了之前说过的为什么endpoint默认情况等于函数名

python3-开发进阶Flask的基础(2)

 获得参数都放到类似一个类,点开看看

 python3-开发进阶Flask的基础(2)

果然是一个类

 python3-开发进阶Flask的基础(2)

 将路有关系添加到所有路由关系映射表

 python3-开发进阶Flask的基础(2)

 

 python3-开发进阶Flask的基础(2)

总结:

尽量不要让endpoint重名,如果重名函数一定要相同

 

参数:

rule,                       url规则
view_func,                  视图函数名称
defaults=none,              默认值,当url中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
endpoint=none,              名称,用于反向生成url,即: url_for('名称')
methods=none,               允许的请求方式,如:["get","post"]
strict_slashes=none,        对url最后的 / 符号是否严格要求,
                            @app.route('/index',strict_slashes=false),
                            访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可
                            @app.route('/index',strict_slashes=true)
                            仅访问 http://www.xx.com/index 
redirect_to=none,           重定向到指定地址
                            @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
                             或
                             def func(adapter, nid):
                                 return "/home/888"
                             @app.route('/index/<int:nid>', redirect_to=func)
subdomain=none,             子域名访问

cbv:

from flask import flask,views
import functools
def wrapper(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner

duo=flask(__name__)

class userview(views.methodview):
    methods = ['get']  #只允许get请求
    decorators = [wrapper,]   #加上要执行的装饰器的名字
    def get(self,*args,**kwargs):

        return 'get'

    def post(self,*args,**kwargs):
        return 'post'

duo.add_url_rule('/user',none,userview.as_view('uuuu'))

if __name__ == '__main__':
    duo.run()

 

python3-开发进阶Flask的基础(2)

 基本上大同小异

python3-开发进阶Flask的基础(2)

 

自定义正则:

from  flask import flask,url_for
duo=flask(__name__)

#步骤一:定制类
from werkzeug.routing import baseconverter

class regexconverter(baseconverter):
    '''
    自定义url匹配正则表达式
    '''
    def __init__(self,map,regex):
        super(regexconverter,self).__init__(map)
        self.regex=regex

    def to_python(self, value):
        '''
        路由匹配时,匹配成功过后传递给视图函数中参数的值
        :param value:
        :return:
        '''
        return int(value)

    def to_url(self, value):
        '''
        使用url_for反向生成url时,传递的参数经过该方法处理,返回值用于生成url中的参数
        :param value:
        :return:
        '''
        val=super(regexconverter,self).to_url(value)
        return val   #完全可以不写

#步骤二:添加到转换器
duo.url_map.converters['reg']=regexconverter

'''
1、用户发送请求
2、flask内部进行正则匹配
3、调用to_python(正则匹配的结果)方法
4、to_python的返回值会交给视图函数的参数'''

#步骤三:使用自定义正则
@duo.route('/index/<reg("\d+"):nid>')
def index(nid):
    print(nid,type(nid))
    print(url_for('index',nid=1))
    return 'index'

if __name__ == '__main__':
    duo.run()

 

二、session实现原理(源码)

flask 

 

python3-开发进阶Flask的基础(2)

 

python3-开发进阶Flask的基础(2)

 

 python3-开发进阶Flask的基础(2)

下面我们来一行行来验证上面猜想的:

python3-开发进阶Flask的基础(2)

self 是flask的对象duo,environ请求相关的原始数据

 python3-开发进阶Flask的基础(2)

python3-开发进阶Flask的基础(2)

 

python3-开发进阶Flask的基础(2)

 

python3-开发进阶Flask的基础(2)

 

python3-开发进阶Flask的基础(2)

 

python3-开发进阶Flask的基础(2)

 

python3-开发进阶Flask的基础(2)

 

 三、蓝图

目标:给开发者提供一个目录结构

1、首先创建一个和项目一样名字的文件夹,在文件夹中的__init__.py,先定义一个创建对象的函数

from flask import flask

def create_app():
    duo=flask(__name__)
    return duo

2、在项目一样名字的文件夹下,创建视图文件夹,在里面单独创建需求的功能视图

from flask import blueprint

uc=blueprint('ac',__name__)
@uc.route('/index/')
def index():
    return 'index'

3、建立他们之间的关系,在__init__导入生成的对象 uc,最后注册进去

from flask import flask
from .views.user import uc


def create_app():
    duo=flask(__name__)
    duo.register_blueprint(uc,url_prefix='/duoduuo')  #url_prefix适用版本控制   
    return duo


#最后再写一个运行的脚本,

其他的功能:

自定义模板、静态文件

某一类url添加前缀

某一类url添加before_request

四、threading.local(和flask无任何关系)

先看一个简单事例:

import threading
import time
v=0
def task(i):
    global v
    v=i
    time.sleep(1)
    print(v)

for i in range(10):
    t=threading.thread(target=task,args=(i,))
    t.start()


#结果都是9,最后一个修改覆盖所有,解决方法加锁

看一种解决方案:

import threading
from threading import local
#为每给线程创建一个独立空间来修改数据,使得线程对自己的空间中的数据进行操作(数据隔离)
import time

obj=local()

def task(i):
    obj.duo=i
    time.sleep(1)
    print(obj.duo,i)

for i in range(10):
    t=threading.thread(target=task,args=(i,))
    t.start()

现在我们来考虑几个问题,

如何获取一个线程的唯一标记 ?

threading.get_ident()  #跟linux进程pid类似

根据字典自定义一个类似于threading.local功能?

通过getattr,setattr 构建出加强版的threading.local功能!

import  time
import threading
try:
    import greenlet      #有就是协程,没有就是线程
    get_ident=greenlet.getcurrent
except exception as e:
    get_ident=threading.get_ident
class local(object):
    dic={}
    def __getattr__(self, item):    #self.xxx  触发执行
        # print('getattr',item)
        ident=get_ident()
        if ident in self.dic:
            return self.dic[ident].get(item)
        return none

    def __setattr__(self, key, value):    #self.xxx=123  触发执行
        # print('setattr',key,value)
        ident=get_ident()
        if ident in self.dic:
            self.dic[ident][key]=value
        else:
            self.dic[ident]={key:value}
obj=local()
def task(i):
    obj.duo=i
    time.sleep(1)
    print(obj.duo,i)

for i in range(10):
    t=threading.thread(target=task,args=(i,))
    t.start()

后续还是有看源码的博客:

五、简述上下文管理

1、当请求到来时,在源码中wsgi_app 中 ctx 对象是requestcontext封装所有的对象和请求相关的原始数据,

这个对象就有request和session,在接下来的对session进行补充,将包含了request/session的ctx对象放到’神奇的地方‘

这个神奇的地方就是一个大字典,用线程或协程的唯一标识为key,

 

2、当视图函数使用的时候:

假使取一个request.method  ,我们先导入 from  flask import request ,根据当前线程取ctx对象,再取request,再取.method

 

3、当请求结束:

根据当前线程的唯一标记,将’神奇地方‘上的数据移除