官方文檔:
本章數據庫模型
本節(jié)中的操作將在以下數據庫模型里進行。
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 結構,并在事務中發(fā)送這些結構。我們首先需要通過向 Session 添加對象;然后 Session 確保這些新條目在需要時被傳送到數據庫,該過程被稱為刷新(flush)。
用類的實例代表數據庫的行
在使用 ORM 時,我們直接使用 Python 類的實例來代表要插入的數據,這個數據庫類在我們定義數據表的時候就定義好了,一個實例代表數據庫中的一行。
例子:
>>> squidward = User(name="squidward", fullname="Squidward Tentacles")
>>> krabs = User(name="ehkrabs", fullname="Eugene H. Krabs")
注意,在創(chuàng)建實例的時候我們沒有包括主鍵(即 id 列),因為我們想利用數據庫的自動遞增主鍵功能,如果我們要查看上述對象的 id 屬性值,顯示為 None。
>>> squidward
User(id=None, name='squidward', fullname='Squidward Tentacles')
None 值是由 SQLAlchemy 提供的,表示該屬性到目前為止還沒有值。在 Python 中, SQLAlchemy 映射的屬性總要返回一個值,這是為了在處理還沒有賦值的新對象時,不會引發(fā) AttributeError。
到目前為止,我們上面的兩個對象被成為處于"瞬時的狀態(tài)”(transient)——它們沒有與任何數據庫狀態(tài)相關聯,而且還沒有與一個可以為它們生成 INSERT 語句的 Session 對象相關聯。
添加對象到 Session
先導入 Session 類并創(chuàng)建 session 對象:
>>> from sqlalchemy.orm import Session
>>> session = Session(engine)
然后使用 Session.add() 方法將這些對象添加到 session 中?,F在這些對象處于待定狀態(tài),還沒有被插入。
>>> session.add(squidward)
>>> session.add(krabs)
當有待處理對象時,可以通過查看 Session.new 集合來看到這個狀態(tài)。
>>> 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 語句,它們現在處于開放狀態(tài),即不被執(zhí)行,直到我們調用 Session.commit()、Session.rollback() 或 Session.close() 方法。
雖然 Session.flush() 可以用來手動推送當前事務的未決更改,但通常是不必要的,因為 Session 有自動刷新功能,當調用 Session.commit() 時,它就會被觸發(fā)。
自動生成的主鍵屬性
一旦執(zhí)行了 session.flush(),我們創(chuàng)建的兩個Python對象就處于一種被稱為持久化的狀態(tài)(persistent),在這種狀態(tài)下,它們與添加或加載它們的 Session 對象相關聯,并且具有許多其他的方法與屬性。
另一個影響是 ORM 為每個新對象提供了新的主鍵標識符。它通常使用 CursorResult.inserted_primary_key 訪問器?,F在 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 實例和我們上一小節(jié)的使用 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)