探索 SQLAlchemy 中的数据库反射技术

前言

最近进行接口自动化框架优化时,总感觉直接写sql语句影响代码的美感,所以决定使用SQLAlchemy。但是有一个麻烦的地方, 就是需要手动定义表结构,这样就比较费时了,一个表都有几十个字段,手动定义显然不可行,投入产出比低。庆幸的是,SQLAlchemy提供了 反射技术,它使得 SQLAlchemy 能够在不提前定义表结构的情况下,直接从数据库中动态读取表结构并映射为Python对象。趁着这个机会正好 总结一下SQLAlchemy 反射技术。笔者采用的2.0版本。

自动映射

自动映射方法(automap_base() 和 prepare())更为简便和自动化,适用于不需要手动管理表对象的场景,适合于快速构建基于现有数据库的应用程序。

使用自动映射反射数据库

为了反射数据库,这里使用automap_base,使用automap_base创建一个Base对象,

ini 复制代码
from sqlalchemy.ext.automap import automap_base

Base = automap_base()

然后,需要将一个引擎连接到想反射的数据库,初始化引擎,

ini 复制代码
from sqlalchemy import create_engine

DATABASE_URL = f"mysql+pymysql://root:127.0.0.1:3306/pangxiaolu"
engine = create_engine(DATABASE_URL)

这样,就做好了反射数据库的准备工作:Base和引擎创建。

接下来,调用创建的Base对象的prepare方法,将扫描我们刚刚创建的引擎上的所有可用内容,并反射它所能反射的所有内容。

ini 复制代码
Base.prepare(autoload_with=engine)

只需要这一行代码,就完成了整个数据库的映射工作。这个反射已经为每个表创建了ORM对象。我们输出测试一下,

python 复制代码
for k, v in Base.classes.items():
    print(f"{k}: {v}")
    break

输出结果如下,

vbnet 复制代码
user: <class 'sqlalchemy.ext.automap.user'>

当然,我们也可以这样访问映射的类,比如有一个表user

bash 复制代码
User = Base.classes.user
print(User)  # <class 'sqlalchemy.ext.automap.user'>
print(type(User))  # <class 'sqlalchemy.orm.decl_api.DeclarativeMeta'>

接下来,使用映射的类,就简单了

ini 复制代码
from sqlalchemy.orm import Session

session = Session(bind=engine)
user = session.query(User).first()

注意:有些参数将被废弃,如果你是这样写的Base.prepare(engine, reflect=True) ,会发生警告,下面是官方文档介绍

vbnet 复制代码
engine --

legacy; use AutomapBase.autoload_with. Used to indicate the Engine or Connection with which to reflect tables with, if AutomapBase.reflect is True.

Deprecated since version 1.4: The AutomapBase.prepare.engine parameter is deprecated and will be removed in a future release. Please use the AutomapBase.prepare.autoload_with parameter.

reflect --

legacy; use AutomapBase.autoload_with. Indicates that MetaData.reflect() should be invoked.

Deprecated since version 1.4: The AutomapBase.prepare.reflect parameter is deprecated and will be removed in a future release. Reflection is enabled when AutomapBase.prepare.autoload_with is passed.

可以看到这两参数后面将会被删除。

反射

反射单个表

我们需要创建元数据对象,

css 复制代码
from sqlalchemy import create_engine, MetaData, Table

metadata = MetaData()

同样,需要初始化引擎,

ini 复制代码
DATABASE_URL = f"mysql+pymysql://root:127.0.0.1:3306/pangxiaolu"
engine = create_engine(DATABASE_URL)

创建好元数据和引擎之后,就有了反射一个表所需要的一切,进行反射

ini 复制代码
user = Table('user', metadata, autoload_with=engine)

autoload_with=engine:这是Table类的一个关键字参数,它告诉SQLAlchemy自动从数据库中加载user表的结构信息。autoload_with参数需要一个数据库引擎(engine)的实例作为值。

好了,我们来测试一下

scss 复制代码
print(user.columns.keys())  # ['id', 'name', 'gender', 'area_code', 'mobile']

反射整个数据库

为了反射整个数据库,可以使用 metadata 对象的 reflect 方法。 reflect 方法会扫描引擎上的所有可用内容,并反射它所能反射的所有内容。

ini 复制代码
metadata.reflect(bind=engine)

使用反射表,

ini 复制代码
user = metadata.tables['user']

接下来使用就一样了。

反射原理

  1. 创建数据库引擎(Engine)
  2. 加载元数据:使用元数据(MetaData)对象来加载数据库的结构信息。
  3. 反射数据库结构:调用 MetaData 对象的 reflect() 方法
  4. 映射到类:Table对象被创建,使用反射得到的Table对象,SQLAlchemy可以自动创建或更新Python类,这些类映射到数据库表。这个过程称为ORM映射。

看自动映射的源码,发现也是调用MetaData 对象的 reflect() 方法,核心源码如下

ini 复制代码
if reflect:
           assert autoload_with
           opts = dict(
               schema=schema,
               extend_existing=True,
               autoload_replace=False,
           )
           if reflection_options:
               opts.update(reflection_options)
           cls.metadata.reflect(autoload_with, **opts)  # type: ignore[arg-type]  # noqa: E501

最后

好了,基本用法目前可以满足我做接口自动化,更多高级用法工作中用到再继续延伸吧,也可以查看官方文档介绍。

相关推荐
Asthenia04125 分钟前
理解词法分析与LEX:编译器的守门人
后端
uhakadotcom6 分钟前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
Asthenia04121 小时前
Spring扩展点与工具类获取容器Bean-基于ApplicationContextAware实现非IOC容器中调用IOC的Bean
后端
bobz9651 小时前
ovs patch port 对比 veth pair
后端
Asthenia04122 小时前
Java受检异常与非受检异常分析
后端
uhakadotcom2 小时前
快速开始使用 n8n
后端·面试·github
JavaGuide2 小时前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql
bobz9652 小时前
qemu 网络使用基础
后端
Asthenia04123 小时前
面试攻略:如何应对 Spring 启动流程的层层追问
后端
Asthenia04123 小时前
Spring 启动流程:比喻表达
后端