在引入蓝图概念之前,先分析app注册路由的原理
app注册路由的基本原理
| |
| from flask import Flask |
| |
| app = Flask(__name__) |
| |
| |
| @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: |
| |
| @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) |
| options["endpoint"] = endpoint |
| methods = options.pop("methods", None) |
| |
| |
| |
| |
| 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} |
| |
| |
| required_methods = set(getattr(view_func, "required_methods", ())) |
| |
| |
| |
| 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 |
| |
| |
| methods |= required_methods |
| |
| rule = self.url_rule_class(rule, methods=methods, **options) |
| rule.provide_automatic_options = provide_automatic_options |
| |
| self.url_map.add(rule) |
| 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") |
| |
| |
| @order_blu.route("/order/list") |
| 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: |
| |
| |
| |
| if endpoint and in endpoint: |
| raise ValueError() |
| |
| if view_func and hasattr(view_func, ) and in view_func.__name__: |
| raise ValueError() |
| ## 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: |
| |
| |
| |
| |
| |
| if self._got_registered_once and self.warn_on_modifications: |
| from warnings import warn |
| |
| warn( |
| Warning( |
| |
| |
| |
| ) |
| ) |
| self.deferred_functions.append(func) |
接下来看app.register_blueprint(order_blu),调用app的register_blueprint方法
| |
| class Flask(Scaffold): |
| def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None: |
| |
| blueprint.register(self, options) |
| |
| class Blueprint(Scaffold) |
| def register(self, app: "Flask", options: dict) -> None: |
| state = self.make_setup_state(app, options, first_bp_registration) |
| for deferred in self.deferred_functions: |
| deferred(state) |
| |
| def add_url_rule( |
| self, |
| rule: str, |
| endpoint: t.Optional[str] = None, |
| view_func: t.Optional[t.Callable] = None, |
| **options: t.Any, |
| ) -> None: |
| |
| |
| 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 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?