官方文檔:
本章數(shù)據(jù)庫模型
本節(jié)中的操作將在以下數(shù)據(jù)庫模型里進(jìn)行。
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 插入數(shù)據(jù)
在使用 ORM 插入時(shí),Session 對(duì)象負(fù)責(zé)構(gòu)建 Insert 結(jié)構(gòu),并在事務(wù)中發(fā)送這些結(jié)構(gòu)。我們首先需要通過向 Session 添加對(duì)象;然后 Session 確保這些新條目在需要時(shí)被傳送到數(shù)據(jù)庫,該過程被稱為刷新(flush)。
用類的實(shí)例代表數(shù)據(jù)庫的行
在使用 ORM 時(shí),我們直接使用 Python 類的實(shí)例來代表要插入的數(shù)據(jù),這個(gè)數(shù)據(jù)庫類在我們定義數(shù)據(jù)表的時(shí)候就定義好了,一個(gè)實(shí)例代表數(shù)據(jù)庫中的一行。
例子:
>>> squidward = User(name="squidward", fullname="Squidward Tentacles")
>>> krabs = User(name="ehkrabs", fullname="Eugene H. Krabs")
注意,在創(chuàng)建實(shí)例的時(shí)候我們沒有包括主鍵(即 id 列),因?yàn)槲覀兿肜脭?shù)據(jù)庫的自動(dòng)遞增主鍵功能,如果我們要查看上述對(duì)象的 id 屬性值,顯示為 None。
>>> squidward
User(id=None, name='squidward', fullname='Squidward Tentacles')
None 值是由 SQLAlchemy 提供的,表示該屬性到目前為止還沒有值。在 Python 中, SQLAlchemy 映射的屬性總要返回一個(gè)值,這是為了在處理還沒有賦值的新對(duì)象時(shí),不會(huì)引發(fā) AttributeError。
到目前為止,我們上面的兩個(gè)對(duì)象被成為處于"瞬時(shí)的狀態(tài)”(transient)——它們沒有與任何數(shù)據(jù)庫狀態(tài)相關(guān)聯(lián),而且還沒有與一個(gè)可以為它們生成 INSERT 語句的 Session 對(duì)象相關(guān)聯(lián)。
添加對(duì)象到 Session
先導(dǎo)入 Session 類并創(chuàng)建 session 對(duì)象:
>>> from sqlalchemy.orm import Session
>>> session = Session(engine)
然后使用 Session.add() 方法將這些對(duì)象添加到 session 中。現(xiàn)在這些對(duì)象處于待定狀態(tài),還沒有被插入。
>>> session.add(squidward)
>>> session.add(krabs)
當(dāng)有待處理對(duì)象時(shí),可以通過查看 Session.new 集合來看到這個(gè)狀態(tài)。
>>> session.new
IdentitySet([
User(id=None, name='squidward', fullname='Squidward Tentacles'),
User(id=None, name='ehkrabs', fullname='Eugene H. Krabs')]
)
刷新
Session 使用一種被稱為工作單元的模式,也就是說,它可以積累一個(gè)或多個(gè)變化,但直到需要時(shí)才將它們傳遞給數(shù)據(jù)庫。
>>> 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 語句,它們現(xiàn)在處于開放狀態(tài),即不被執(zhí)行,直到我們調(diào)用 Session.commit()、Session.rollback() 或 Session.close() 方法。
雖然 Session.flush() 可以用來手動(dòng)推送當(dāng)前事務(wù)的未決更改,但通常是不必要的,因?yàn)?Session 有自動(dòng)刷新功能,當(dāng)調(diào)用 Session.commit() 時(shí),它就會(huì)被觸發(fā)。
自動(dòng)生成的主鍵屬性
一旦執(zhí)行了 session.flush(),我們創(chuàng)建的兩個(gè)Python對(duì)象就處于一種被稱為持久化的狀態(tài)(persistent),在這種狀態(tài)下,它們與添加或加載它們的 Session 對(duì)象相關(guān)聯(lián),并且具有許多其他的方法與屬性。
另一個(gè)影響是 ORM 為每個(gè)新對(duì)象提供了新的主鍵標(biāo)識(shí)符。它通常使用 CursorResult.inserted_primary_key 訪問器?,F(xiàn)在 squidward 和 krabs 對(duì)象有了主鍵標(biāo)識(shí)符,我們可以通過訪問 id 屬性來查看它們。
>>> squidward.id
4
>>> krabs.id
5
身份映射
身份映射是一個(gè)內(nèi)存存儲(chǔ),它將當(dāng)前加載在內(nèi)存中的所有對(duì)象通過它們的主鍵身份和數(shù)據(jù)庫對(duì)象聯(lián)系起來。我們可以通過使用 Session.get() 方法檢索上述對(duì)象。
>>> some_squidward = session.get(User, 4)
>>> some_squidward
User(id=4, name='squidward', fullname='Squidward Tentacles')
提交到數(shù)據(jù)庫
使用 Session.commit() 方法把添加的事務(wù)提交到數(shù)據(jù)庫并走出相應(yīng)的更改。
>>> session.commit()
COMMIT
使用 ORM 更改數(shù)據(jù)
使用 ORM 更改數(shù)據(jù)也是在之前定義的數(shù)據(jù)庫類的某個(gè)實(shí)例上來進(jìn)行更改,我們先獲取到這個(gè)實(shí)例。
>>> 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')
其實(shí)這里的 sandy 實(shí)例和我們上一小節(jié)的使用 Session.get() 方法所獲得的是一樣的。
>>> sandy1 = session.execute(select(User).filter_by(name="sandy")).scalar_one()
>>> sandy2 = session.get(User, 2)
>>> sandy1 == sandy2
Ture
查看一下 sandy 實(shí)例:
>>> sandy
User(id=2, name='sandy', fullname='Sandy Cheeks')
當(dāng)我們嘗試修改這個(gè)實(shí)例的屬性, session 會(huì)跟蹤這些變化。
>>> sandy.fullname = "Sandy Squirrel"
sandy 實(shí)例屬性被修改后,它會(huì)被放在名為 session.dirty 的集合中。
>>> sandy in session.dirty
True
這時(shí)候使用 session.commit() 把修改提交到數(shù)據(jù)庫:
>>> session.commit()
我們還可以使用第二種 ORM 的方法來修改數(shù)據(jù)。
from sqlalchemy import select, update
session.execute(
update(User).
where(User.name == "sandy").
values(fullname="Sandy Squirrel Extraordinaire")
)
session.commit()
使用 ORM 刪除數(shù)據(jù)
我們通過使用 Session.delete() 方法來標(biāo)記刪除的數(shù)據(jù)。
>>> 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)