【SQLALchemy】第三篇——ORM编程
创始人
2025-05-29 23:38:43

可以熟练使用面向对象编程语言——如Python——的人,一定会非常容易理解关系型数据库的设计原则。以MySQL为例,如果用Python中的一个类来表示数据库中的一张表,用类的属性来表示表的字段,那么类的一个实例就表示表中的一行数据。

按照这种方式,完全可以将表用类来表示。自然地,我们会想,既然数据表可以被映射成类,那么对类的操作是不是也可以同步到相应的表呢?答案是肯定的,这就是所谓的ORM技术。

ORM 是对象关系映射(Object-Relational Mapping)的缩写,它是一种软件开发技术,用于将面向对象的编程语言中的对象与关系数据库中的数据进行映射。ORM 通过将关系数据库中的表和列转换成面向对象编程语言中的类和属性,使得开发人员可以用面向对象的方式来操作数据库,而不必关心底层的 SQL 查询语句和数据库操作细节。ORM 技术旨在提高开发效率,降低代码维护成本,同时也可以减少 SQL 注入等安全问题的出现。常见的 ORM 框架有 Hibernate、Django ORM、SQLAlchemy 等。

——来自ChatGPT

1、利用类定义表并写入数据

1.1 建表

利用ORM技术,可以通过在Python中定义类来定义数据表:

from sqlalchemy import Column, Integer, String, ForeignKey, create_engine
from sqlalchemy.orm import declarative_baseBase = declarative_base()
engine = create_engine('mysql+pymysql://root:123456@127.0.0.1:3306/test')class User(Base):__tablename__ = "user_account"id = Column(Integer, primary_key=True)name = Column(String(30))fullname = Column(String(30))def __repr__(self):return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"class Address(Base):__tablename__ = "address"id = Column(Integer, primary_key=True)email_address = Column(String(30), nullable=False)user_id = Column(Integer, ForeignKey("user_account.id"))def __repr__(self):return f"Address(id={self.id!r}, email_address={self.email_address!r})"Base.metadata.create_all(engine)

关注类的定义代码不难看出,表结构是通过类属性定义的。其中,__tablename__是一个特殊的属性,用于表示该类所对应的表的名称。表的其他字段名称及其约束通过实例化Column为一个变量的方式进行声明。在实例化时,可以对字段的数据类型、长度、是否为主键等进行限制。

类中还定义了两张表的关系。在此处的情境中,一个用户被认为可以拥有多个邮件地址,而一个邮件地址只能属于一个用户,因此,它们之间的关系是“一对多”的。在address表中,定义的user_id字段是引用的主表user_account的主键id字段。

在定义指代表的类的时,主要它们都需要继承自Base——一个具有声明性质的类。只有继承自Base的类才能用上面的写法定义表结构。最后,通过Base.metadata.create_all(engine)来将在Python中定义好的类映射成表并写入数据库中。

1.2 插入数据

通过ORM方法来与数据库进行交互时用的最多的是一种被称为Session的对象。对于熟悉PyMySQL的读者而言,它非常类似于后者的Connection。实际上,Session对象与各种数据库进行交互时,就是通过各个驱动包的Connection(有可能是其他的名字)来实现的。

首先,通过实例化已定义的类来构造需要写入数据表中的数据行:

user1 = User(id=1, name='Everill', fullname='Everill Glen')
user2 = User(id=2, name='Hughes', fullname='Hughes Adnan')
addr1 = Address(id=1, email_address='everillglen1@example.com', user_id=1)
addr2 = Address(id=2, email_address='everillglen2@example.com', user_id=1)
addr3 = Address(id=3, email_address='hughesadnan@example.com', user_id=2)

这里简单地创建了两个User实例和三个Address实例。至此,它们还只是Python中的对象,与数据库还没有建立联系。为了将它们写入数据表,需要用到Session。通过以下方式,可以创建一个Session对象:

from sqlalchemy.orm import Session
session = Session(engine)

调用Sessionadd()方法,可以将一个实例“绑定”到session上。这里有5个实例,为简单起见可以调用Sessionadd_all()将这些实例一次性绑定到session上:

session.add_all([user1, user2, addr1, addr2, addr3])

上述“绑定”的过程,实际上就是Session对象积累变更的过程。出于效率和其他因素的考虑,除非用户发出明确地指令,SQLALchemy不会将这些变更自动同步到数据库。

通过调用Sessionflush()方法,可以将积累的变更同步到数据库:

session.flush()

调用flush()实际上是创建一个事务以将变更对应的SQL发送到数据库服务器进行执行。如果一切顺利,最后还需要调用commit()方法来真正完成上述事务。

当然,在实际中,可以直接调用commit()方法,SQLALchemy会自动进行flush。

1.3 按主键查询

既然UserAddress已经和数据库中的user_accountaddress表相对应,那么当然可以通过表的主键来查询特定的实例:

session.get(User, 1)

1.4 修改实例

user = session.get(User, 1)
user.fullname = 'Everill Green'

上述两行代码将id为1的实例的fullname属性进行了修改。与插入数据的过程类似,这种修改此时仍积累在Session对象中,并未同步到数据库。

Session对象通过一个集合来保存这些变更了的实例:

user in session.dirty
# True

再调用commit()方法后,上述语句返回False,表明user不存在待提交的变更。

如果不想将这些变更提交到数据库,则调用rollback()方法:

session.rollback()
session.dirty  # 回滚后,变更集合为空

1.5 删除实例

类似地,先获取实例,再调用delete()方法,则可以删除实例:

user = session.get(User, 1)
session.delete(user)

要想删除生效,仍需要调用commit()方法。

至此,可以总结发现,通过Session对象对数据库进行增删改查等基本操作时,SQLALchemy都会将其转化为对应的SQL并开启一个事务进行执行。若要执行生效,需要显示地进行提交(commit());若要进行回滚,则执行rollback()

2、获取已有的数据表

很多时候,我们需要做的是对已经存在于数据库中的表进行增删改查等操作。显然,应对这个问题时,我们希望将数据表批量映射为Python中的类,而不是从头将它们在Python中定义一遍。

在SQLALchemy中,我们可以使用automap_base()函数可以自动将数据库中的表映射为ORM类:

from sqlalchemy.ext.automap import automap_base# 自动加载所有现有数据表并创建ORM映射对象
Base = automap_base()
Base.prepare(engine, reflect=True)# 获取ORM映射对象
User = Base.classes.user_account
Address = Base.classes.address

至此,便可以通过第一部分所说的方法来对数据表进行操作了。

相关内容

热门资讯

重磅!2025陆家嘴论坛6月1... 《国际金融报》记者获悉,5月30日,上海市政府举行新闻发布会,上海市委金融办、中国人民银行办公厅、上...
零基础入门学习Python 0... 目录 1.函数的参数  2.函数文档 3.返回值  4. 局部变量全局变量 5.内嵌函数 6.闭包...
剧本中的灭世海啸即将到来?特朗... 欢迎来到:自说自话的总裁两周前,94岁的巴菲特在退休直播中说,美元,将「下地狱」,我们不会投资。这句...
sdbusplus:servi... 有的时候需要在service(记做s1)的method内访问其他serv...
EEPROM芯片(24c02)... 1、前言 (1)本文主要是通过24c02芯片来讲解I2C接口的EEPROM操作方法,...
鼓励中长期资金指数化投资!上证... 文/王兆寰2025年5月30日下午,上海证券交易所与中证指数公司公布优化上证380指数编制方案,并发...
龙虎榜丨机构今日买入这15股,... 盘后数据显示,5月30日龙虎榜中,共33只个股出现了机构的身影,15只股票呈现机构净买入,18只股票...
年轻人的“茅台”新高不断!重仓... 今年以来,新消费概念股走势强劲。港股的泡泡玛特(国内潮玩行业龙头)今年来新高不断,截至2025年5月...
履新超130天,荣耀CEO李健... 5月下旬,一系列新品发布浪潮席卷而来。中端智能手机市场本就是国内各大厂商的战略要地,如今竞争再度白热...
岷山环能IPO:全员被批“学历... 近日,北交所官网显示,岷山环能高科股份公司(以下简称“岷山环能”)IPO提交注册。据悉,岷山环能20...
【TDengine】一篇文章带... 目录 1、通过docker方式安装 2、相关步骤解释 3、停止运行taos与卸载 1、通过doc...
华人健康并购狂飙:24倍溢价、... 本报(chinatimes.net.cn)记者于娜 见习记者 赵文娟 北京报道在医药零售行业整体放缓...
“国产版ChatGPT”文心一... 文章目录前言实验结果一、文学创作问题1 :《三体》的作者是哪里人?问题2:...
上海试点不动产信托登记,相关税... 继北京之后,上海也开始探索不动产信托财产登记确权新机制。中共上海市委金融委员会办公室、 国家金融监督...
德邦证券董事会改组后“换帅”,... 德邦证券 视觉中国 资料图德邦证券股份有限公司(下称“德邦证券”)步入国有实控阶段后,同步“换帅”...
连续盈利后,理想要靠纯电车赚钱... 增程市场进入红海后,覆盖新能源市场中更多品类,以及进入下沉市场是理想汽车下一步重心文|陈亮 蒋雨彤编...
字节短剧大地震:红果吞并抖音团... 2025年5月29日,抖音集团宣布成立“短剧版权中心”,整合旗下抖音短剧、红果短剧相关BD团队(商务...
算法基础课 算法基础课 第一章 基础算法(一) 1.快速排序——分治[O(n log...
通用航空ETF基金今日上市 低... 中证网讯 5月30日,华夏国证通用航空产业ETF(基金简称:通用航空ETF基金,基金代码:15923...
马斯克放豪言:特斯拉人形机器人... IT之家 5 月 30 日消息,SpaceX 和特斯拉的 CEO 埃隆・马斯克在今日的“Road t...
iOS_performSele... 看一下以下方法的执行结果: 例1:全局并发队列中执行异步任务 dispa...
来了!新型浮动费率基金| 一文... 首批新型浮动费率基金,本周开卖!这是公募改革方案发布后率先推出的创新产品,是将基金公司与投资者利益深...
刷题笔记之十三(有假币、最难的... 目录 1. 求正数数组的最小不可组成和 2. 有假币 3. 继承时先调用父类的构造方法;类中的成员变...
县城的万达广场,王健林才舍不得... 来源:源Sight记者:王言流动危机下,王健林似乎铁了心要将家底一卖到底。近日,国家市场监管总局披露...
三度递表终闻锣响 手回集团今日... 南方财经全媒体记者 林汉垚 北京报道三闯港交所终圆梦。5月30日,历经两次递表折戟后,小雨伞保险经纪...
后“531时代”来袭!隆基绿能... “物竞天择,适者生存。一款产品‘打天下’的时代已经终结,光伏市场已经进化到组件差异化的时代,分布式市...
英伟达吞下45亿美元“哑巴亏”... 出品 | 虎嗅科技组作者 | 丸都山编辑 | 苗正卿头图 | 视觉中国一份喜忧参半的财报,却罕见地让...
授之以渔篇-论文目录设置、双边... 文章目录细说风云自定义目录页眉、页码空白页删除缘起 细说风云 因为平日使用WPS比较习惯࿰...
基于jeecgboot的大屏设...      之前主要是讲了数据源的录入与数据库测试功能,但保存这块没有说,...
江丰电子,8年狂飙14倍,全国... 2017年6月,江丰电子登陆资本市场,发行价4.64元;截至2025年5月29日,按后复权计算,江丰...