Flask高级

关于Flask启动,和请求处理

#关于后端服务的启动,无非就是启动实现了WSGI协议的socket
#关于flask启动的无非就是下面两段代码

#加载配置,创建Flask对象
app = Flask(__name__)
#这个是启动werkzeug服务
app.run()

下面我们来分析上面两段代码

app.run()

run_simple(host, port, self, **options)找到起服务的核心代码

    def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
        if os.environ.get("FLASK_RUN_FROM_CLI") == "true":
            from .debughelpers import explain_ignored_app_run
            explain_ignored_app_run()
            return

        if get_load_dotenv(load_dotenv):
            cli.load_dotenv()

            if "FLASK_ENV" in os.environ:
                self.env = get_env()
                self.debug = get_debug_flag()
            elif "FLASK_DEBUG" in os.environ:
                self.debug = get_debug_flag()

        if debug is not None:
            self.debug = bool(debug)

        _host = "127.0.0.1"
        _port = 5000
        server_name = self.config.get("SERVER_NAME")
        sn_host, sn_port = None, None

        if server_name:
            sn_host, _, sn_port = server_name.partition(":")

        host = host or sn_host or _host
        port = int(next((p for p in (port, sn_port) if p is not None), _port))

        options.setdefault("use_reloader", self.debug)
        options.setdefault("use_debugger", self.debug)
        options.setdefault("threaded", True)

        cli.show_server_banner(self.env, self.debug, self.name, False)

        from werkzeug.serving import run_simple

        try:
        #############################################
            #开启werkzeug服务
            run_simple(host, port, self, **options)
        finally:
            self._got_first_request = False


请求-app.__call__()

上面我们已经启动了wsgi服务

run_simple(host, port, self, **options)

当来一个请求来的时候wsgi就会执行一次self()

当执行self()就相当于执行了app.__call__()

app.__call__()

    def __call__(self, environ, start_response):
        #来一次请求就执行一次这个函数
        return self.wsgi_app(environ, start_response)

wsgi_app(environ, start_response)

    #首先讲下函数的三个参数,self就是app,environ就行请求相关的数据,start_response是一个回调函数,是wsgi的用于返回一个响应行
    def wsgi_app(self, environ, start_response):
    #首先总览一下最核心的代码
    	#实例化一个ctx对象,里面封装了request和session等信息
        ctx = self.request_context(environ)
        error = None
        try:
            try:
            	#将ctx存到LOCAL对象的storage,存的格式为: storage={"执行id":{'stack':[ctx,]}}
            	#这个也就是flask的request的奇怪用法
                ctx.push()
                
                #这里主要实现的是请求扩展
                #如before_first_request,信号,和请求拓展这些函数规则的定制
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            #返回响应信息
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            #删除ctx
            ctx.auto_pop(error)

其他

蓝图-flask项目架构

蓝图的主要功能就是视图分离,不然所有视图写在一个文件会显得很杂乱

使用三步

  1. 创建蓝图

    from flask import Blueprint
    #view
    user_bp = Blueprint('user',__name__)
    
  2. 利用蓝图创建路由关系

    #view
    @bp.route('/login/')
    def login():
    return "login"
    
  3. 注册蓝图

    #app
    app.register_blueprint(bp)
    

注意

使用url_for,前面要加上蓝图名

url_for('zx.login')

实例

项目目录:

-templates
-static
-views
    -user.py
    -order.py
-app.py

views/user.py

from flask import Blueprint

# 1 创建蓝图
user_bp = Blueprint('user',__name__)

# 2 利用蓝图创建路由关系
@user_bp.route('/login/')
def login():
    return "login"

@user_bp.route('/logout/')
def logout():
    return "logout"

views/order.py

from flask import Blueprint

order_bp = Blueprint('order',__name__)

@order_bp.route('/add_order/')
def add_order():
    return "add_order"

@order_bp.route('/modify_order/')
def modify_order():
    return "modify_order"

app.py

from flask import Flask
from views.user import user_bp
from views.order import order_bp

app = Flask(__name__)
# 3 注册蓝图
app.register_blueprint(user_bp)
app.register_blueprint(order_bp)


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

g对象

g对象使用周期是一次请求,先设置值,在取值

from flask import g
 
 #设置值
 g.name = "zx"
 #取值
 g.name

信号

安装

pip install blinker

内置信号

request_started = _signals.signal('request-started')                # 请求到来前执行
request_finished = _signals.signal('request-finished')              # 请求结束后执行
 
before_render_template = _signals.signal('before-render-template')  # 模板渲染前执行
template_rendered = _signals.signal('template-rendered')            # 模板渲染后执行
 
got_request_exception = _signals.signal('got-request-exception')    # 请求执行出现异常时执行
 
request_tearing_down = _signals.signal('request-tearing-down')      # 请求执行完毕后自动执行(无论成功与否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 应用上下文执行完毕后自动执行(无论成功与否)
 
appcontext_pushed = _signals.signal('appcontext-pushed')            # 应用上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped')            # 应用上下文pop时执行
message_flashed = _signals.signal('message-flashed')                # 调用flask在其中添加数据时,自动触发

使用信号

# 往信号中注册函数
def func(*args,**kwargs):
    print('触发型号',args,kwargs)
    
#这个是内置信号
signals.request_started.connect(func)

一个源码中的内置信号

    def full_dispatch_request(self):
        """Dispatches the request and on top of that performs request
        pre and postprocessing as well as HTTP exception catching and
        error handling.

        .. versionadded:: 0.7
        """
        self.try_trigger_before_first_request_functions()
        try:
        	#这里看到没有
            request_started.send(self)
            rv = self.preprocess_request()
            if rv is None:
                rv = self.dispatch_request()
        except Exception as e:
            rv = self.handle_user_exception(e)
        return self.finalize_request(rv)

自定义信号

from flask import Flask, current_app, flash, render_template
from flask.signals import _signals
app = Flask(import_name=__name__)

# 自定义信号
xxxxx = _signals.signal('xxxxx')
 
def func(sender, *args, **kwargs):
    print(sender)
# 自定义信号中注册函数
xxxxx.connect(func)
@app.route("/x")
def index():
    # 触发信号
    xxxxx.send('123123', k1='v1')
    return 'Index' 
 
if __name__ == '__main__':
    app.run()

自定义session

我们知道Django的session是存在数据库的,而flask的默认是通过cookie实现的,那我们如果想要存在数据库怎么办呢?

安装

pip install flask-session

使用

from flask import Flask,session
from flask_session import RedisSessionInterface
import redis
app = Flask(__name__)

conn=redis.Redis(host='127.0.0.1',port=6379)
#use_signer是否对key签名,默认为false
#key_prefix为存储的k值的前缀
#permanent默认为True,浏览器关闭cookie失效
app.session_interface=RedisSessionInterface(conn,key_prefix='zx')

@app.route('/')
def hello_world():
    session['name']='zx125'
    print(session['name'])
    return 'Hello World!'

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

自定义命令

安装

pip install flask-script

使用

from flask_script import Manager
app = Flask(__name__)
manager=Manager(app)
...
if __name__ == '__main__':
    manager.run()
#以后在执行,直接:python3 manage.py runserver
#python3 manage.py runserver --help

参考链接

https://www.cnblogs.com/xiaoyuanqujing/protected/articles/11715484.html

原文地址:https://www.cnblogs.com/zx125/p/12057230.html