探索 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

最后

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

相关推荐
对象存储与RustFS19 分钟前
Spring Boot集成RustFS十大常见坑点及解决方案|踩坑实录
后端
RoyLin33 分钟前
TypeScript设计模式:原型模式
前端·后端·node.js
数据智能老司机1 小时前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
菜鸟谢1 小时前
Manjaro Tab 无自动补全
后端
Java水解1 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
Java水解1 小时前
Mysql查看执行计划、explain关键字详解(超详细)
后端·mysql
光殿2 小时前
性能测试
测试
数据智能老司机2 小时前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
追逐时光者2 小时前
.NET Fiddle:一个方便易用的在线.NET代码编辑工具
后端·.net
林树的编程频道3 小时前
快递的物流地图是怎么实现的
后端