likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 42

前言

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

自动映射

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

使用自动映射反射数据库

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

from sqlalchemy.ext.automap import automap_base

Base = automap_base()

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

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方法,将扫描我们刚刚创建的引擎上的所有可用内容,并反射它所能反射的所有内容。

Base.prepare(autoload_with=engine)

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

for k, v in Base.classes.items():
    print(f"{k}: {v}")
    break

输出结果如下,

user: <class 'sqlalchemy.ext.automap.user'>

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

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

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

from sqlalchemy.orm import Session

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

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

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.

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

反射

反射单个表

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

from sqlalchemy import create_engine, MetaData, Table

metadata = MetaData()

同样,需要初始化引擎,

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

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

user = Table('user', metadata, autoload_with=engine)

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

print(user.columns.keys())  # ['id', 'name', 'gender', 'area_code', 'mobile']

反射整个数据库

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

metadata.reflect(bind=engine)

使用反射表,

user = metadata.tables['user']

接下来使用就一样了。

反射原理

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

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

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

最后

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

转载自:https://juejin.cn/post/7400343697110695975
评论
请登录