本文最后更新于:2025年7月30日 下午
本文介绍了Python的ORM工具SQLAlchemy的使用方法,包括如何建立与数据库连接、建立映射关系、单表及多表的CRUD操作。通过示例展示了如何使用query()方法进行灵活的查询,以及如何处理多表之间的关系。此外,还演示了如何创建、修改和删除记录。。
简介
SQLAlchemy 是 Python 著名的 ORM(Object Relational Mapping) 工具包。通过 ORM,开发者可以用面向对象的方式来操作数据库,不再需要编写 SQL 语句。本篇不解释为什么要使用 ORM,主要讲解 SQLAlchemy 的用法。SQLAlchemy 支持多种数据库,除 sqlite 外,其它数据库需要安装第三方驱动。本篇以 sqlite 数据库为例进行说明。
关于ORM:
全称 Object Relational Mapping
(对象关系映射)。
特点是操纵Python对象而不是SQL查询,也就是在代码层面考虑的是对象,而不是SQL,体现的是一种程序化思维,这样使得Python程序更加简洁易读。
具体的实现方式是将数据库表转换为Python类,其中数据列作为属性,数据库操作作为方法。
优点:
- 简洁易读:将数据表抽象为对象(数据模型),更直观易读
- 可移植:封装了多种数据库引擎,面对多个数据库,操作基本一致,代码易维护
- 更安全:有效避免SQL注入
SQLAlchemy分成三部分:
- ORM,就是我们用类来表示数据库schema的那部分
- SQLAlchemy Core,就是一些基础的操作,例如 update, insert 等等,也可以直接使用这部分来进行操作,但是它们写起来没有ORM那么自然
- DBAPI,这部分就是数据库驱动
它们的关系如下
安装 SQLAlchemy
使用pip工具安装SQLAlchemy:
1 |
|
ORM
数据库表是一个二维表,包含多行多列。把一个表的内容用Python的数据结构表示出来的话,可以用一个list
表示多行,list
的每一个元素是tuple
,表示一行记录,比如,包含id
和name
的user
表:
1 |
|
Python的DB-API返回的数据结构就是像上面这样表示的。
但是用tuple
表示一行很难看出表的结构。如果把一个tuple
用class
实例来表示,就可以更容易地看出表的结构来:
1 |
|
这就是传说中的ORM技术:Object-Relational Mapping,把关系数据库的表结构映射到对象上。
简单示例
1 |
|
我们注意上面的几点:
-
pool_recycle,设置主动回收连接的时长,如果不设置,那么可能会遇到数据库主动断开连接的问题,例如MySQL通常会为连接设置 最大生命周期为八小时,如果没有通信,那么就会断开连接。因此不设置此选项可能就会遇到
MySQL has gone away
的报错。 -
engine,engine是SQLAlchemy 中位于数据库驱动之上的一个抽象概念,它适配了各种数据库驱动,提供了连接池等功能。其用法就是 如上面例子中,
engine = create_engine(<数据库连接串>)
,数据库连接串的格式是dialect+driver://username:password@host:port/database?参数
这样的,dialect 可以是mysql
,postgresql
,oracle
,mssql
,sqlite
,后面的 driver 是驱动,比如MySQL的驱动pymysql, 如果不填写,就使用默认驱动。再往后就是用户名、密码、地址、端口、数据库、连接参数了,我们来看几个例子:- MySQL:
engine = create_engine('mysql+pymysql://scott:tiger@localhost/foo?charset=utf8mb4')
- PostgreSQL:
engine = create_engine('postgresql+psycopg2://scott:tiger@localhost/mydatabase')
- Oracle:
engine = create_engine('oracle+cx_oracle://scott:tiger@tnsname')
- MS SQL:
engine = create_engine('mssql+pymssql://scott:tiger@hostname:port/dbname')
- SQLite:
engine = create_engine('sqlite:////absolute/path/to/foo.db')
- 详见:https://docs.sqlalchemy.org/en/13/core/engines.html
- MySQL:
-
Session,Session的意思就是会话,也就是说,是一个逻辑组织的概念,因此,这需要靠你的业务逻辑来划分哪些操作使用同一个Session, 哪些操作又划分为不同的业务操作,详见 这里。 举个简单的例子,以web应用为例,一个请求里共用一个Session就是一个好的例子,一个异步任务执行过程中使用一个Session也是一个例子。 但是注意,不能直接使用Session,而是使用Session的实例,借助上面的代码,我们可以直接这样写:
1
2with get_session() as s:
print(s.query(User).first()) -
Base,Base是ORM中的一个基类,通过集成Base,我们才能方便的使用一些基本的查询,例如
s.query(User).filter_by(User.name="nick").first()
。 -
BaseMixin,BaseMixin是我自己定义的一些通用的表结构,通过Mixin的方式集成到类里,比如上面的定义,我们常见的表结构里,都会有 ID、创建时间,更新时间,软删除标志等等,我们把它作为一个独立的类,这样通过继承即可获得相关表属性,省得重复写多次。
表的设计
表的设计通常就如 User
表一样:
1 |
|
首先使用 __tablename__
自定义表名,接着写各个表中的属性,也就是对应在数据库表中的列(column),常见的类型有:
1 |
|
常见操作
我们来看看使用SQLAlchemy完成常见的操作,例如增删查改:
常见查询操作
-
SELECT * FROM user
应该这样写:1
2with get_session() as s:
print(s.query(User).all()) -
SELECT * FROM user WHERE name='nick'
应该这样写:1
2
3with get_session() as s:
print(s.query(User).filter_by(User.name='nick').all())
print(s.query(User).filter(User.name == 'nick').all()) # 这样写是等同效果的 -
SELECT * FROM user WHERE name='nick' LIMIT 1
应该这样写:1
2with get_session() as s:
print(s.query(User).filter_by(User.name='nick').first())
如果需要加判定,例如确保只有一条数据,那就把 first()
替换为 one()
,如果确保一行或者没有,那就写 one_or_none()
。
-
SELECT * FROM user ORDER BY id DESC LIMIT 1
应该这样写:1
2with get_session() as s:
print(s.query(User).order_by(User.id.desc()).first()) -
SELECT * FROM user ORDER BY id DESC LIMIT 1 OFFSET 20
应该这样写:1
2with get_session() as s:
print(s.query(User).order_by(User.id.desc()).offset(20).first())
常见删除操作
-
DELETE FROM user
应该这样写:1
2with get_session() as s:
s.query(User).delete() -
DELETE FROM user WHERE name='nick'
:1
2with get_session() as s:
s.query(User).filter_by(User.name='nick').delete() -
DELETE FROM user WHERE name='nick' LIMIT 1
:1
2with get_session() as s:
s.query(User).filter_by(User.name='nick').limit(1).delete()
更新语句
-
UPDATE user SET name='nick'
:1
2with get_session() as s:
s.query(User).update({'name': 'nick'}) -
UPDATE user SET name='nick' WHERE id=1
:1
2with get_session() as s:
s.query(User).filter_by(User.id=1).update({'name': 'nick'})
也可以通过更改实例的属性,然后提交:
1 |
|
插入语句
这个就简单了,实例化对象,然后 session.add
,最后提交:
1 |
|
连表
SQLAlchemy 中可以直接使用join语句:
1 |
|
可以是这么几种写法:
1 |
|
数据库migration
我们使用alembic来做数据库migration,首先安装:
1 |
|
然后我们要修改 alembic/env.py
(假设你设置的保存migration的文件夹名称就是 alembic
),将对应部分修改成如下:
1 |
|
因为SQLAlchemy会把表的信息存储在 metadata 里,而我们都继承了 Base
,因此可以 通过 Base.metadata
来拿到所有表的信息,这样子alembic才能够拿到表的结构,然后和 数据库进行对比,生成migration脚本:
1 |
|
应用举例
尝试在 MySQL 的 test 数据库中创建的user
表,用SQLAlchemy来试试:
第一步,导入SQLAlchemy,并初始化DBSession:
1 |
|
以上代码完成SQLAlchemy的初始化和具体每个表的class
定义。如果有多个表,就继续定义其他class
,例如School:
1 |
|
create_engine()
用来初始化数据库连接。SQLAlchemy用一个字符串表示连接信息:
1 |
|
你只需要根据需要替换掉用户名、口令等信息即可。
下面,我们看看如何向数据库表中添加一行记录。
由于有了ORM,我们向数据库表中添加一行记录,可以视为添加一个User
对象:
1 |
|
可见,关键是获取session,然后把对象添加到session,最后提交并关闭。DBSession
对象可视为当前数据库连接。
如何从数据库表中查询数据呢?有了ORM,查询出来的可以不再是tuple
,而是User
对象。SQLAlchemy提供的查询接口如下:
1 |
|
运行结果如下:
1 |
|
可见,ORM就是把数据库表的行与相应的对象建立关联,互相转换。
由于关系数据库的多个表还可以用外键实现一对多、多对多等关联,相应地,ORM框架也可以提供两个对象之间的一对多、多对多等功能。
例如,如果一个User拥有多个Book,就可以定义一对多关系如下:
1 |
|
当我们查询一个User对象时,该对象的books属性将返回一个包含若干个Book对象的list。
参考资料
- https://liaoxuefeng.com/books/python/database/sqlalchemy/index.html
- https://blog.csdn.net/stone0823/article/details/112344065
- https://zhuanlan.zhihu.com/p/91169446
- https://dormousehole.readthedocs.io/en/latest/patterns/sqlalchemy.html
文章链接:
https://www.zywvvd.com/notes/coding/python/python-sqlalchemy/python-sqlalchemy/
“觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”

微信支付

支付宝支付