前一章用SQL表达式(SQL Express)语法操作数据库时,仍然侧重于从Core API的角度来看 SQL表达式语言,主要目的是通过封装的SQL表达式来实现跨不同数据库目的。
本章主要介绍ORM API 基础知识、实现ORM CRUD 基础操作。我尽可能使用简洁的语言,配合实例代码,帮助你尽快上手。下一章将深入学习ORM API的主要方法、使用方式,以及较复杂需求的编程实现。
在第1章介绍过ORM原理时。 Python类对象通过ORM与数据库Table进行映射,通过Python类对象的方式来操作数据库。开发人员无须再使用各类数据库的接口API,以及SQL语法。
注: ORM的理想很丰满,现实却是这样的:用好ORM还是需要扎实的SQL基础才能用好。
ORM如何实现从Python对象到数据库的映射呢?
预备知识:
metadata:
上一章已详细介绍过,用于保存表结构, 通常1个应用定义1个全局metadata对象, 集中保存表结构。
declarative Table 声明式Table类
将数据库的表、字段,定义成Python类与属性的结构,这种结构称为声明式映射(Declarative Mapping)。Sqlalchemy2以后。table 类必须继承自 DeclarativeBase。
ORM API的使用流程:
(1)定义1个DeclarativeBase子类,做为所有Table类的父类
(2)定义Table类,映射到1个数据库表
(3)通过engine在数据库中实际创建表。 由Base.metadata向engine 发关create table DDL的事件.
(4)创建session对象,连接到engine,用于管理后续数据库操作。
(5)使用SQL Express Language语法的select, insert, update, delete等方法来执行数据库的增删改查操作。
继承自DeclarativeBase, column 字段定义遵照mapping 语法
定义1个Base做为Table类的基类
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass
注:Base对象包含metadfata. registry 属性
Base.metadata
Base.registry
设置数据库表名
__tablename__ = "user_account"
, 设置为db table 名
定义column属性,
name: Mapped[str] = mapped_column(String(30))
等号左边使用type notation注明Python类型,右边用mapped_column()给出最匹配的Database字段类型
这是SqlAlchemy2.0推荐的方式,别嫌麻烦,这样做可以大大减少数据库迁移时出错的概率,
下面是1个用声明式映射方式定义的Person 类。
from sqlalchemy.orm import DeclarativeBase, Session
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import String, Integer
class Base(DeclarativeBase):
pass
class Person(Base):
__tablename__ = 'person'
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(30))
age: Mapped[int] = mapped_column(Integer)
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def __repr__(self):
return f"Person({self.name}, {self.age})"
说明:
上述模型类,除将用属性映射到数据库字段外,与普通类定义一样,也提供了对象初始化方法__init__(),以及__repr__()魔法方法。 还可以添加其它的方法,如属性的校验方法等。
创建数据库连接引擎对象
engine = create_engine(
"mysql+mysqlconnector://root:12345678@localhost:3306/testdb")
# 请用你的mysql用户名及密码替换root:12345678
将DDL语句映射到数据库表,如果数据库表不存在,则创建该表
Base.metadata.create_all(engine)
我们现在可以在数据库中插入数据了。通过创建类的实例来实现这一点. 还要使用一个名为Session的对象将它们传递到数据库,该对象利用Engine与数据库进行交互。使用Se木本油料Session.add(obj)添加1个对象(数据记录行),或使用Session.add_all()方法一次添加多个对象,而Session.commit()方法将用于保存对数据库的变更。
session = Session(engine)
# 创建Person实例对象
p1 = Person("刘备", 40)
p2 = Person("关羽", 38)
p3 = Person("张飞", 35)
# 将Person实例对象添加到session
session.add(p1)
session.add(p2)
session.add(p3)
session.commit() # 提交事务到数据库
建议通过上下文管理器使用Session,即使用Python with语句。
对于数据库中的某些行,以下是发出SELECT语句以加载某些对象的最简单形式。要创建SELECT语句,
1)使用SELECT() 函数创建一个新的SELECT对象.
2) 使用Session调用该对象。在查询ORM对象时,通常有用的方法是Session.scalars()方法,它将返回一个ScalarResult对象,该对象将遍历我们选择的ORM对象:
stmt = select(Person).where(Person.name == "刘备")
results = session.scalars(stmt)
print(results.all())
Output:
[Person(刘备, 40)]
返回值 results对象为ScalarResult类实例,成员方法 all()以列表方式获取查询结果, 是1个对象列表,也很符合OOP的规范。
ScalarResult对象还提供了first(), fetchone(), fetchmany(), fetchall()等方法,习惯于DBAPI编程的同样都很熟悉。
ScalarResult对象也是可迭代的,用for 循环遍历读取结果
stmt = select(Person).where(Person.name.in_(["刘备", "关羽"]))
results = session.scalars(stmt)
for p in results:
print(p)
Output:
Person(刘备, 40)
Person(关羽, 38)
results = session.scalars(select(Person).order_by(Person.age))
Where()方法不支持 and, or逻辑运算符,但支持where()方法的链式调用来实现多条件查询。
stmt = select(Person).where(Person.age > 35).where(Person.age < 50)
results = session.scalars(stmt)
print(results.all())
更新数据时,先以对象的方式读取到1条数据后,更新该对象的属性,然后通过session提交事件,即自动将更新数据库相应记录。
例如。我们先在Person表中插入1条name=”张辽”的数据
p4 = Person("张辽", 36)
session.add(p4)
session.commit() # 提交事务到数据库
然后获取该记录对象,将name属性改为”赵云”,提交更新。
stmt = select(Person).where(Person.name == "张辽")
person = session.scalars(stmt).first()
person.name = "赵云"
person.age = 32
session.commit()
results = session.scalars(select(Person).order_by(Person.age))
print(results.all())
Output
[Person(赵云, 32), Person(张飞, 35), Person(关羽, 38), Person(刘备, 40)]
删除数据,也就是用session.delete()方法向engine传递1个对象。如果该对象不存在,则会抛出异常,如果该对象存在,则删除。
p5 = Person("曹操", 50)
try:
session.delete(p5)
except Exception as e:
print("数据库中不存在该记录")
p6 = session.scalars(select(Person).where(Person.name == "赵云")).first()
session.delete(p6)
session.commit()
results = session.scalars(select(Person).order_by(Person.age))
print(results.all())
Output:
数据库中不存在该记录
[Person(张飞, 35), Person(关羽, 38), Person(刘备, 40)]
ORM API编程的基本流程为:
1)用声明式映射方式,定义Python Table类,其父类必须是DeclarativeBase
2)Table类的属性映射到数据库字段,左边用 maped()注明Python类型,右边用mapped_column()申明对应的数据库字段类型。
3)用Base.metadata.emit()方法将DDL语句发送到数据库创建表。
4)创建Session对象,做为ORM 至 Database的管理器。
5)通过实例对象完成数据库的添加、修改、删除操作
6)通过select()查询数据, 并且提供了where(), order_by()等支持条件查询,排序等。