4.使用 ORM 的数据操作
官方文档:
本章数据库模型
本节中的操作将在以下数据库模型里进行。
from sqlalchemy import create_engine, MetaData
from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship
engine = create_engine('sqlite:///memory.db', echo=True, future=True)
Base = declarative_base()
class User(Base):
__tablename__ = 'user_account'
id = Column(Integer, primary_key=True)
name = Column(String(30))
fullname = Column(String)
addresses = relationship("Address", back_populates="user")
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, nullable=False)
user_id = Column(Integer, ForeignKey('user_account.id'))
user = relationship("User", back_populates="addresses")
def __repr__(self):
return f"Address(id={self.id!r}, email_address={self.email_address!r})"
使用 ORM 插入数据
在使用 ORM 插入时,Session
对象负责构建 Insert
结构,并在事务中发送这些结构。我们首先需要通过向 Session
添加对象;然后 Session
确保这些新条目在需要时被传送到数据库,该过程被称为刷新(flush
)。
用类的实例代表数据库的行
在使用 ORM 时,我们直接使用 Python 类的实例来代表要插入的数据,这个数据库类在我们定义数据表的时候就定义好了,一个实例代表数据库中的一行。
例子:
>>> squidward = User(name="squidward", fullname="Squidward Tentacles")
>>> krabs = User(name="ehkrabs", fullname="Eugene H. Krabs")
注意,在创建实例的时候我们没有包括主键(即 id
列),因为我们想利用数据库的自动递增主键功能,如果我们要查看上述对象的 id
属性值,显示为 None
。
>>> squidward
User(id=None, name='squidward', fullname='Squidward Tentacles')
None
值是由 SQLAlchemy 提供的,表示该属性到目前为止还没有值。在 Python 中, SQLAlchemy
映射的属性总要返回一个值,这是为了在处理还没有赋值的新对象时,不会引发 AttributeError
。
到目前为止,我们上面的两个对象被成为处于"瞬时的状态”(transient)——它们没有与任何数据库状态相关联,而且还没有与一个可以为它们生成 INSERT
语句的 Session
对象相关联。
添加对象到 Session
先导入 Session
类并创建 session
对象:
>>> from sqlalchemy.orm import Session
>>> session = Session(engine)
然后使用 Session.add()
方法将这些对象添加到 session
中。现在这些对象处于待定状态,还没有被插入。
>>> session.add(squidward)
>>> session.add(krabs)
当有待处理对象时,可以通过查看 Session.new
集合来看到这个状态。
>>> session.new
IdentitySet([
User(id=None, name='squidward', fullname='Squidward Tentacles'),
User(id=None, name='ehkrabs', fullname='Eugene H. Krabs')]
)
刷新
Session
使用一种被称为工作单元的模式,也就是说,它可以积累一个或多个变化,但直到需要时才将它们传递给数据库。
>>> session.flush()
BEGIN (implicit)
INSERT INTO user_account (name, fullname) VALUES (?, ?)
[...] ('squidward', 'Squidward Tentacles')
INSERT INTO user_account (name, fullname) VALUES (?, ?)
[...] ('ehkrabs', 'Eugene H. Krabs')
上诉例子中我们可以观察到 Session
新增的 SQL 语句,它们现在处于开放状态,即不被执行,直到我们调用 Session.commit()
、Session.rollback()
或 Session.close()
方法。
虽然 Session.flush()
可以用来手动推送当前事务的未决更改,但通常是不必要的,因为 Session
有自动刷新功能,当调用 Session.commit()
时,它就会被触发。
自动生成的主键属性
一旦执行了 session.flush()
,我们创建的两个Python对象就处于一种被称为持久化的状态(persistent),在这种状态下,它们与添加或加载它们的 Session
对象相关联,并且具有许多其他的方法与属性。
另一个影响是 ORM 为每个新对象提供了新的主键标识符。它通常使用 CursorResult.inserted_primary_key
访问器。现在 squidward
和 krabs
对象有了主键标识符,我们可以通过访问 id
属性来查看它们。
>>> squidward.id
4
>>> krabs.id
5
身份映射
身份映射是一个内存存储,它将当前加载在内存中的所有对象通过它们的主键身份和数据库对象联系起来。我们可以通过使用 Session.get()
方法检索上述对象。
>>> some_squidward = session.get(User, 4)
>>> some_squidward
User(id=4, name='squidward', fullname='Squidward Tentacles')
提交到数据库
使用 Session.commit()
方法把添加的事务提交到数据库并走出相应的更改。
>>> session.commit()
COMMIT
使用 ORM 更改数据
使用 ORM 更改数据也是在之前定义的数据库类的某个实例上来进行更改,我们先获取到这个实例。
>>> sandy = session.execute(select(User).filter_by(name="sandy")).scalar_one()
BEGIN (implicit)
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
WHERE user_account.name = ?
[...] ('sandy',)
>>> sandy
User(id=2, name='sandy', fullname='Sandy Cheeks')
其实这里的 sandy
实例和我们上一小节的使用 Session.get()
方法所获得的是一样的。
>>> sandy1 = session.execute(select(User).filter_by(name="sandy")).scalar_one()
>>> sandy2 = session.get(User, 2)
>>> sandy1 == sandy2
Ture
查看一下 sandy
实例:
>>> sandy
User(id=2, name='sandy', fullname='Sandy Cheeks')
当我们尝试修改这个实例的属性, session
会跟踪这些变化。
>>> sandy.fullname = "Sandy Squirrel"
sandy
实例属性被修改后,它会被放在名为 session.dirty
的集合中。
>>> sandy in session.dirty
True
这时候使用 session.commit()
把修改提交到数据库:
>>> session.commit()
我们还可以使用第二种 ORM 的方法来修改数据。
from sqlalchemy import select, update
session.execute(
update(User).
where(User.name == "sandy").
values(fullname="Sandy Squirrel Extraordinaire")
)
session.commit()
使用 ORM 删除数据
我们通过使用 Session.delete()
方法来标记删除的数据。
>>> patrick = session.get(User, 3)
SELECT user_account.id AS user_account_id, user_account.name AS user_account_name,
user_account.fullname AS user_account_fullname
FROM user_account
WHERE user_account.id = ?
[...] (3,)
>>> session.delete(patrick)