Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析

一. Flask-SQLAlchemy 初始化源码

在文件中定义了工厂函数 create_app :

def create_app(config):
    """ 可以根据传入的 config 名称,加载不同的配置
"""

    app = Flask(__name__)
    app.config.from_object(configs.get(config))

    db.init_app(app)

如上所示的最后一行代码,db 对象一般是在 models.py 文件中定义的 SQLAlchemy 类的实例, db = SQLAlchemy()。这里将应用对象作为参数调用实例的 init_app 方法进行初始化。

到现在为止我们只知道初始化,但并不知道实际发生了什么。

我们看下 SQLAlchemy 这个类的源码,首先它是来自 flask_sqlalchemy.__init__ 模块中:

cd /usr/local/lib/python3.8/dist-packages/flask_sqlalchemy
sudo vim __init__.py
class SQLAlchemy(object):
    """This class is used to control the SQLAlchemy integration to one
    or more Flask applications.  Depending on how you initialize the
    object it is usable right away or will attach as needed to a
    Flask application.

    There are two usage modes which work very similarly.  One is binding
    the instance to a very specific Flask application::

        app = Flask(__name__)
        db = SQLAlchemy(app)

因为在创建该类的实例的时候,没有提供参数,我们就直接看 init_app 方法的源码:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
如上图所示,init_app 方法只需要提供一个 app 参数,也就是应用对象。

在第 847 行有一个判断,要求 app.config 这个类字典对象中 必须要有 SQLALCHEMY_DATABASE_URI 或者 SQLALCHEMY_BINDS 这两个配置项中的一个,不然就会抛出一个警告信息:默认使用 SQLite3 作为数据库。

我们已经在配置文件中定义了 SQLALCHEMY_DATABASE_URI ,所以不会出现这个警告。

下面就是一堆给 app.config 字典对象添加默认键值对的操作。

继续向下查看源码,如下图所示第 884 行是最重要的:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
应用对象在初始化的时候,定义了一个 extensions 属性, 属性值是空字典,这是用来存放各种扩展对象的地方。

在执行 db.init_app(app) 时,就会将 ‘sqlalchemy’ 字符串作为 key , _SQLAlchemyState 类的实例作为 value ,添加到 app.extensions 字典中。

我们看下这个 _SQLAlchemyState 类的源码,它也在 flask_sqlalchemy.__init__ 模块中:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析

如上图所示,初始化此类时提供的参数就是 SQLAlchemy 类的实例,也就是说 _SQLAlchemyState 类的实例的 db 属性值是 SQLAlchemy 类的实例,也就是 db 对象

总结:

Flask-SQLAlchemy 库中的 SQLAlchemy 类的实例是 db 变量,调用它的 init_app 方法初始化,主要就是为应用对象的配置增加一些默认值,以及将 ‘sqlalchemy’ 作为键将 db 添加到 extensions 字典中;

二、蓝图源码分析

蓝图的英文名字是 Blueprint ,是 Flask 提供的将应用组件化的一种机制。对一个中大型网站来说,有很多个板块。
每个板块下面都会有多个页面。我们知道,Flask 应用程序要实例化一个应用对象 app 来管理 Flask 应用,而 Blueprint 可以让你将板块注册为 app 下的一个子 app。

这个子 app,也就是我们注册的蓝图,它的使用方法基本上和 app 一样的

比如,我们注册一个课程板块的蓝图:

from flask import Blueprint

course = Blueprint('course', __name__, url_prefix='/courses')

然后调用 app 的 register_blueprint 将这个 course Blueprint 注册到 app:

def register_blueprints(app):
    """注册蓝图的函数"""
    app.register_blueprint(course)

这样在注册路由时,比如课程首页的路由就可以这样写:

@course.route('/')
def courses_index():
    return render_template('courses.html')

现在可以在 create_app 中调用注册蓝图的函数来注册蓝图:

def create_app(config):
    """ App 工厂"""

    app = Flask(__name__)
    app.config.from_object(configs.get(config))
    db.init_app(app)

    app.register_blueprint(api)

    return app

定义api.所对应的url

定义了所有 API 对应的 URL
"""
from flask import Blueprint

from rmon.views.index import IndexView

api = Blueprint('api', __name__)

api.add_url_rule('/', view_func=IndexView.as_view('index'))

下面来研究一下源码,弄清楚创建蓝图时的参数在蓝图内部是怎么使用的。
Blueprint 类定义在 flask.blueprints 模块中:

cd /usr/local/lib/python3.8/dist-packages/flask
sudo vim blueprints.py
class Blueprint(Scaffold):
    """Represents a blueprint, a collection of routes and other
    app-related functions that can be registered on a real application
    later.

    A blueprint is an object that allows defining application functions
    without requiring an application object ahead of time. It uses the
    same decorators as :class:~flask.Flask, but defers the need for an
    application by recording them for later registration.

看下它的初始化方法:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
在我们的代码中,涉及到的参数有三个,如上图所示蓝色框中。它们分别赋值给蓝图对象的同名属性:第 184 、185 行设置了 name 和 url_prefix 属性, 而 import_name 是由父类设置的。第 181 行调用了父类的初始化方法,此类定义在 flask.helpers 模块中:
Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
定义好蓝图之后, 就可以使用蓝图的路由装饰器来创建视图函数(也叫路由函数)了,我们看下此方法的源码:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
与应用对象 app 的 route 方法类似,都是调用自身的 add_url_rule 方法添加路由规则。我们以 front 蓝图的 index 视图函数为例,参数 rule 是相对路径字符串 '/' ,options 是空字典,第 278 行设置变量 endpoint 的值为视图函数名 'index'

调用 add_url_rule 方法就是上图 284 行的方法。第 288 行判断 endpoint 变量字符串中是否有点号,如果有就抛出异常。同理第 290 行 判断视图函数的 __name__ 属性值是否带点号

最后 294 行调用自身的 record 方法,参数是一个匿名函数。先不管这个匿名函数,我们看下 record 方法:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
如上图所示,这里调用了蓝图的 deferred_functions 的 append 方法, deferred_functions 属性是一个空列表,它是在蓝图初始化时定义的。

也就是说, 在创建蓝图对象之后,会执行路由装饰器 route 方法把每一个视图函数对应生成的匿名函数放到蓝图的 deferred_functions 列表里。

注册蓝图流程分析

启动应用程序后,创建应用对象,然后执行注册蓝图的函数。

在app.py 文件中注册了蓝图,也就是调用应用对象的 register_blueprint 方法:

def register_blueprints(app):
    """注册蓝图的函数"""

    from .handlers import front, course, admin
    app.register_blueprint(front)
    app.register_blueprint(course)
    app.register_blueprint(admin)

我们看下此方法的源码:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析

删掉了源码中的一些注释以便于讲解。如上图所示,我们在调用此方法时只提供了一个蓝图对象作为 blueprint 这个参数的值。

第 1143 行有一个判断,blueprint 就是蓝图对象, blueprint.name 是我们定义蓝图时提供的第一个参数字符串。

以 front 这个蓝图为例,blueprint.name 的值就是 ‘front’ 。而 self.blueprints应用对象 app 在初始化时定义的属性,属性值是空字典。所以这个语句为 False ,继续执行 1150 行下面的内容。第 1151 行将蓝图名字作为 key ,蓝图作为 value 向 app 的 blueprints 字典中添加一组键值对。

第 1152 行有个 _blueprint_order 属性,它也是在应用对象初始化时定义的属性,属性值是空列表:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
回到 register_blueprint 方法中,最后一行 1155 行代码调用了蓝图自身的 register 方法,我们再回到定义蓝图的 flask.blueprints 模块中找到这个方法的源码:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
如上图所示,第 246 行通过 make_setup_state 方法创建了一个当前模块中的 BlueprintSetupState 类的实例并赋值给 state 变量。

这里我们主要关注上图所示最后两行代码,循环调用蓝图的 deferred_functions 列表中的匿名函数, 这个匿名函数就是蓝图的 add_url_rule 方法中的那个

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析

调用此匿名函数时,参数 s 就是 state ,也就是当前模块中的 BlueprintSetupState 类的实例,在匿名函数内部调用该实例的 add_url_rule 方法:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析

如上图所示第 70 行的 self.url_prefix 是来自 与蓝图的同名属性,例如 front 对应的就是 None ,course 对应的就是 ‘/courses’ 。
第 65 行的参数 rule 就 是相对路径,举两个例子:front 蓝图的 index 视图函数的 rule 就是 ‘/’ ,在 add_url_rule 内部的值不变;admin 的 index 视图函数的 rule 也是 ‘/’ ,它在 add_url_rule 内部第 70 行被处理,加上了 url_prefix ,最后变成 '/admin'

再举一个明显的例子,course 蓝图中假定有一个视图函数,它被 @course.route('/create_new') 装饰器装饰,那么在这里 rule 参数的值就是 '/create_new' ,在函数内部处理后的值就是 '/courses/create_new' 。也就是说,所有的 course 蓝图下的视图函数在这里被处理时,rule 都是以 '/courses' 开头的绝对路径。

参数 view_func 是视图函数,第 77 行调用 _endpoint_from_view_func 获取视图函数的名字字符串并赋值给 endpoint 变量。

第 78 ~ 80 行处理变量 defaults ,它是 蓝图在初始化时设置的默认属性,属性值一直是空字典,到这里依然是。

关键就在第 81 行,调用 self.app 的 add_url_rule 方法, self.app 就是应用对象,它的 add_url_rule 方法我们在前面的实验中已经学习过,注意第二个参数是用点号将蓝图的 name 属性与 endpoint 连起来得到的字符串,举个首页的例子:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
关于应用对象的 add_url_rule 方法,这里再描述一下:
Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
以首页为例,参数 rule 是 URL 的绝对路径 ‘/’ , endpoint 是蓝图 name 属性值 + 视图函数名字的字符串 'front.index' ,view_func 是定义在 /handlers/front.py 文件中的视图函数。

下面仅对此方法的关键代码进行说明:

Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析
如上图所示第 1275 行的 rule 是根据参数 rule 创建的 werkzeug.routing.Rule 类的实例。注意这里的 options 关键字参数不是空字典啦,它里面至少有一组键值对,key 是 ‘endpoint’ ,value 是 ‘front.index’ 。相关代码如下,就不贴图了:
options["endpoint"] = endpoint

在对 werkzeug.routing.Rule 类进行实例化时,将实例的 rule 属性值设为 ‘/’ ,将实例的 endpoint 属性值设为 ‘front.index’ 。

第 1278 行, self.url_map 是一个 Map 对象,将 rule 添加到其 _rules 属性中,这个属性是一个列表对象。

第 1286 行, self.view_functions 是字典对象,将 endpoint 'front.index' 作为 key 、front 蓝图下的 index 视图函数作为 value 添加到字典中。

flask 中浏览器请求后端视图函数的过程

应用程序启动后,服务器收到浏览器的请求,处理 URL 后得到绝对路径 ‘/’ ,到 app.url_map 中找到 rule 属性值为 ‘/’ 的 Rule 对象,然后获取它的 endpoint 属性值 'front.index' ,再到 app.view_functions 字典中找到 key 为 ‘front.index’ 对应的 value ,也就是视图函数,最后调用视图函数处理请求。

思路整理

  1. 创建蓝图;
  2. 定义视图函数,视图函数的 route 装饰器会创建对应的匿名函数放到蓝图的 deferred_functions 列表中;
  3. 应用对象 app 调用自身的 register_blueprint 方法注册蓝图;
  4. 在注册蓝图的方法内部调用蓝图自身的 register 方法;
  5. 蓝图的 register 方法调用蓝图的 deferred_functions 列表中的匿名函数;
  6. 匿名函数再通过一些流程调用应用对象 app 的 add_url_rule 方法将视图函数、路径、endpoint 这些关键点存到 Map 和字典对象中;
  7. 请求进来后,提取路由中的绝对路径,根据路径找到 endpoint ,根据 endpoint 再找到视图函数,最后调用视图函数处理请求。

最后的结果就是一个大的项目根据业务分成几类,每一类的路由归到一个蓝图下,根据蓝图的 url_prefix 路由前缀来区分。

Original: https://blog.csdn.net/xili2532/article/details/122323958
Author: 胖虎是只mao
Title: Flask-SQLAlchemy 初始化源码和蓝图blueprint源码小分析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/746061/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 使用dockerfile部署springboot应用

    🚀 优质资源分享 🚀 学习路线指引(点击解锁)知识定位人群定位🧡 Python实战微信订餐小程序 🧡 进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯…

    Python 2023年8月9日
    050
  • 使用SVM对鸢尾花数据进行分类

    numpy中含逗号的切片: 对于普通的切片: list名[首:尾:步长] numpy中的切片: 数组名[首:尾,首:尾:步长](逗号用于区分维度,注意:逗号前面不能限定步长) 什么…

    Python 2023年8月27日
    050
  • python脚本封装成接口且,由前端平台调用

    步骤: 1、编写前端平台代码。react语言 2、搭建后端服务(flask轻便),编写服务端代码,封装脚本为接口调用方式,postman调试接口请求OK,达到预期效果。python…

    Python 2023年8月12日
    045
  • pipelines mysql_scrapy基础知识之将item 通过pipeline保存数据到mysql mongoDB:

    pipelines.py class xxPipeline(object): def process_item(self, item, spider): con=pymysql.c…

    Python 2023年10月5日
    039
  • Python自制图片拼图小游戏

    前言 唉,怎么说,感觉只有上班的时候摸鱼玩游戏才是最爽的 等于带薪摸鱼,现在不是有点流行说什么 带薪…带薪** 干嘛的 今天也是有点无聊,就想起之前搞了个拼图的小游戏,…

    Python 2023年8月1日
    091
  • 程序员核心——详解调试(2)

    所爱隔山海,山海皆可平,所念皆星河,星河不可及。 上课! 接着上节课讲的调试(1),本节课进一步讲解调试(2). 文章目录 1.调试实例讲解(2) 校招笔试题 2.如何写出好的(易…

    Python 2023年9月15日
    078
  • python 字典列表指定key排序

    import operatorhero_list=[{‘AGILITY’: True, ‘a’: ‘6.19’, ‘d’: ‘3.24’, ‘denies’: ‘11.95’, ‘…

    Python 2023年5月25日
    079
  • Django使用mysqlclient服务连接并写入数据库

    Django使用mysqlclient服务连接并写入数据库 文章目录 Django使用mysqlclient服务连接并写入数据库 准备 一、安装mysqlclient服务 二、se…

    Python 2023年8月5日
    056
  • 1. pip和conda的区别

    Pip 或者 conda的时候经常被混合使用,这两者也通常被认为是几乎相同的,尽管他们的很多功能是重叠的,但它们的设计和使用目的不同。所以这次参考官方的解释,来进行如下总结和归纳:…

    Python 2023年9月9日
    069
  • 【踩坑日常】如何用pycharm运行一个flask项目以及flask项目配置

    由于最近老师安排的作业需要用到树莓派和python以及flask框架,就说一下我在配置和运行flask项目时踩到的坑。 运行flask项目需要用到的东西: pyCharm(到官网下…

    Python 2023年8月13日
    065
  • Python学习日记-第二十一天- 飞机大战项目准备

    系列文章目录 目标 项目准备 使用pygame 创建图形窗口 理解图像并实现图像绘制 理解游戏循环 和 游戏时钟 理解精灵 和精灵组 前言 一共分为这么5步,进行准备,由于一天学习…

    Python 2023年9月24日
    035
  • 国产API工具爆火,为什么大家都开始用Eolink了?

    Eolink 一、为什么大家都开始用Eolink了? 二、下载安装 三、硬核的文档管理 * 1、手动创建 2、自动生成 3、IDEA一键生成 4、变更通知 5、文档评论 6、版本控…

    Python 2023年9月30日
    060
  • jupyter使用虚拟环境

    为了在jupyter中使用pyTorch的虚拟环境,来记录一下怎么操作一、conda命令的使用因为使用的是jupyter,所有就使用Anaconda Prompt来创建虚拟环境(也…

    Python 2023年11月1日
    054
  • matplotlib绘制风能玫瑰图

    概述 在之前的风资源分析文章中,有提到过用widrose包来进行玫瑰图的绘制,目前的可视化绘图包有很多,但是最基础和底层的,本人认为还是matplotlib,有时候为了画1-2个图…

    Python 2023年9月4日
    055
  • Numpy的各种下标操作

    本文所使用的Numpy版本为: Version: 1.20.3。基于Python和C++开发的Numpy一般被认为是Python中最好的Matlab替代品,其中最常见的就是各种Nu…

    Python 2023年8月24日
    066
  • 计算机毕业设计Python+Django的汽车销售网站(源码+系统+mysql数据库+Lw文档)

    项目介绍 随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,汽车销售商城当然也不能排除在外。汽车销售商城是以实际运用为开发…

    Python 2023年8月5日
    059
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球