ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

flask系列(4-1)-蓝图原理

2022-06-21 00:32:50  阅读:188  来源: 互联网

标签:None endpoint 系列 flask self 蓝图 rule func options


在引入蓝图概念之前,先分析app注册路由的原理

app注册路由的基本原理

## demo.py
from flask import Flask

app = Flask(__name__)


@app.route("/")  # 调用app.route方法
def index():
    return 'pass'


if __name__ == '__main__':
    app.run(debug=True)
## scaffold.py  上面的Flask其实继承于Scaffold
class Scaffold:  
    def route(self, rule: str, **options: t.Any) -> t.Callable[[F], F]:
        def decorator(f: F) -> F:
            endpoint = options.pop("endpoint", None)
  
            self.add_url_rule(rule, endpoint, f, **options)  # 这里的self指的是app对象,调用Flask类的add_url_rule方法
            return f

        return decorator
    @setupmethod
    def add_url_rule(
        self,
        rule: str,
        endpoint: t.Optional[str] = None,
        view_func: t.Optional[t.Callable] = None,
        provide_automatic_options: t.Optional[bool] = None,
        **options: t.Any,
    ) -> None:
## app.py
@setupmethod
    def add_url_rule(
        self,
        rule: str,
        endpoint: t.Optional[str] = None,
        view_func: t.Optional[t.Callable] = None,
        provide_automatic_options: t.Optional[bool] = None,
        **options: t.Any,
    ) -> None:
        if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)  # type: ignore
        options["endpoint"] = endpoint
        methods = options.pop("methods", None)

        # if the methods are not given and the view_func object knows its
        # methods we can use that instead.  If neither exists, we go with
        # a tuple of only ``GET`` as default.
        if methods is None:
            methods = getattr(view_func, "methods", None) or ("GET",)
        if isinstance(methods, str):
            raise TypeError(
                "Allowed methods must be a list of strings, for"
                ' example: @app.route(..., methods=["POST"])'
            )
        methods = {item.upper() for item in methods}

        # Methods that should always be added
        required_methods = set(getattr(view_func, "required_methods", ()))

        # starting with Flask 0.8 the view_func object can disable and
        # force-enable the automatic options handling.
        if provide_automatic_options is None:
            provide_automatic_options = getattr(
                view_func, "provide_automatic_options", None
            )

        if provide_automatic_options is None:
            if "OPTIONS" not in methods:
                provide_automatic_options = True
                required_methods.add("OPTIONS")
            else:
                provide_automatic_options = False

        # Add the required methods now.
        methods |= required_methods

        rule = self.url_rule_class(rule, methods=methods, **options)
        rule.provide_automatic_options = provide_automatic_options  # type: ignore

        self.url_map.add(rule)  # 将规则增加到url_map里面去,到这里路由函数的映射关系就结束了
        if view_func is not None:
            old_func = self.view_functions.get(endpoint)
            if old_func is not None and old_func != view_func:
                raise AssertionError(
                    "View function mapping is overwriting an existing"
                    f" endpoint function: {endpoint}"
                )
            self.view_functions[endpoint] = view_func

Blueprint概念
简单来说,Blueprint 是一个存储操作方法的容器,这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理请求。

Flask使用Blueprint让应用实现模块化,在Flask中,Blueprint具有如下属性:

一个应用可以具有多个Blueprint
可以将一个Blueprint注册到任何一个未使用的URL下比如 “/”、“/sample”或者子域名
在一个应用中,一个模块可以注册多次
Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
在一个应用初始化时,就应该要注册需要使用的Blueprint
但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。

1,创建一个蓝图对象
admin=Blueprint('admin',__name__)
2,在这个蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器
@admin.route('/')
def admin_home():
    return 'admin_home'
3,在应用对象上注册这个蓝图对象
app.register_blueprint(admin,url\_prefix='/admin')
当这个应用启动后,通过/admin/可以访问到蓝图中定义的视图函数

运行机制
蓝图是保存了一组将来可以在应用对象上执行的操作,注册路由就是一种操作
当在应用对象上调用 route 装饰器注册路由时,这个操作将修改对象的url_map路由表
然而,蓝图对象根本没有路由表,当我们在蓝图对象上调用route装饰器注册路由时,它只是在内部的一个延迟操作记录列表defered_functions中添加了一个项
当执行应用对象的 register_blueprint() 方法时,应用对象将从蓝图对象的 defered_functions 列表中取出每一项,并以自身作为参数执行该匿名函数,即调用应用对象的 add_url_rule() 方法,这将真正的修改应用对象的路由表

from flask import Flask

app = Flask(__name__)

from flask import Blueprint

order_blu = Blueprint('orders', __name__, static_folder='static', template_folder="templates") # 初始化蓝图对象,这里的Blueprint类也是继承于Scaffold


@order_blu.route("/order/list")  # 调用蓝图对象的route方法,实际上调用的Scaffol类的route方法
def orders():
    return 'pass'


app.register_blueprint(order_blu)

if __name__ == '__main__':
    print(app.url_map)
    app.run(debug=True)
## scaffold.py  上面的其实Blueprint继承于Scaffold
class Scaffold:  
    def route(self, rule: str, **options: t.Any) -> t.Callable[[F], F]:
        def decorator(f: F) -> F:
            endpoint = options.pop("endpoint", None)
  
            self.add_url_rule(rule, endpoint, f, **options)  # 这里的self指的是蓝图对象,调用Blueprint类的add_url_rule方法
            return f

        return decorator
    @setupmethod
    def add_url_rule(
        self,
        rule: str,
        endpoint: t.Optional[str] = None,
        view_func: t.Optional[t.Callable] = None,
        provide_automatic_options: t.Optional[bool] = None,
        **options: t.Any,
    ) -> None:
## bluerints.py
class Blueprint(Scaffold):
    self.deferred_functions: t.List[DeferredSetupFunction] = []  # 定义了空列表,用户存放下面lambda表达式的返回值,lambda的返回值就是函数的引用
    def add_url_rule(
        self,
        rule: str,
        endpoint: t.Optional[str] = None,
        view_func: t.Optional[t.Callable] = None,
        provide_automatic_options: t.Optional[bool] = None,
        **options: t.Any,
    ) -> None:
        """Like :meth:`Flask.add_url_rule` but for a blueprint.  The endpoint for
        the :func:`url_for` function is prefixed with the name of the blueprint.
        """
        if endpoint and "." in endpoint:
            raise ValueError("'endpoint' may not contain a dot '.' character.")

        if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__:
            raise ValueError("'view_func' name may not contain a dot '.' character.")
      ## self.record() 调用蓝图对象的record方法,里面参数是lambda表达式,返回值是匿名函数的引用,入参s,调用s.add_url_rule方法
      ## 主要看s是谁,调用这个匿名函数时就调用谁的add_url_rule
        self.record(
            lambda s: s.add_url_rule(
                rule,
                endpoint,
                view_func,
                provide_automatic_options=provide_automatic_options,
                **options,
            )
        )

   def record(self, func: t.Callable) -> None:
        """Registers a function that is called when the blueprint is
        registered on the application.  This function is called with the
        state as argument as returned by the :meth:`make_setup_state`
        method.
        """
        if self._got_registered_once and self.warn_on_modifications:
            from warnings import warn

            warn(
                Warning(
                    "The blueprint was already registered once but is"
                    " getting modified now. These changes will not show"
                    " up."
                )
            )
        self.deferred_functions.append(func)

接下来看app.register_blueprint(order_blu),调用app的register_blueprint方法

    ## app.py  上面的Flask其实继承于Scaffold
class Flask(Scaffold):
    def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None:
        
        blueprint.register(self, options)  # 这里blueprint指的是蓝图对象order_blu,调用蓝图对象的register方法
## blueprints.py
class Blueprint(Scaffold)
   def register(self, app: "Flask", options: dict) -> None:
          state = self.make_setup_state(app, options, first_bp_registration) ## 这里的state返回为BlueprintSetupState实例对象
          for deferred in self.deferred_functions: ## 依次遍历匿名函数列表
              deferred(state)   ## 调用上面的匿名函数,BlueprintSetupState为参数,所以执行上面的匿名函数就是执行BlueprintSetupState实例对象的add_url_rule方法,如下面
        
      def add_url_rule(
        self,
        rule: str,
        endpoint: t.Optional[str] = None,
        view_func: t.Optional[t.Callable] = None,
        **options: t.Any,
    ) -> None:
     
        ## 调用app.add_url_rule方法
        self.app.add_url_rule(
            rule,
            f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."),
            view_func,
            defaults=defaults,
            **options,
        )

## app.py
class Flask(Scaffold):
 @setupmethod
    def add_url_rule(
        self,
        rule: str,
        endpoint: t.Optional[str] = None,
        view_func: t.Optional[t.Callable] = None,
        provide_automatic_options: t.Optional[bool] = None,
        **options: t.Any,
    ) -> None:
     

        self.url_map.add(rule) ## 看到这里终于结束了,将规则添加到app.url_map里面去了
        if view_func is not None:
            old_func = self.view_functions.get(endpoint)
            if old_func is not None and old_func != view_func:
                raise AssertionError(
                    "View function mapping is overwriting an existing"
                    f" endpoint function: {endpoint}"
                )
            self.view_functions[endpoint] = view_func

标签:None,endpoint,系列,flask,self,蓝图,rule,func,options
来源: https://www.cnblogs.com/kxtomato/p/16395294.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有