4.使用 ORM 的數(shù)據(jù)操作

官方文檔:






本章數(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)在 squidwardkrabs 對(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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容