Python ORM架構SQLAlchemy學習筆記之資料添加和交易回復介紹

來源:互聯網
上載者:User
1. 添加一個新對象

前面介紹了映射到實體表的映射類User,如果我們想將其持久化(Persist),那麼就需要將這個由User類建立的對象執行個體添加到我們先前建立的Session會話執行個體中:

代碼如下:


ed_user = User('ed', 'Ed Jones', 'edspassword')
session.add(ed_user)


上面兩段代碼執行完後對象持久化了嗎?你或許會興沖沖的跑去資料庫裡查看,結果卻失望而歸——資料庫裡什麼都沒有。為什麼呢?因為SQLAlchemy採取的是Lazyload策略,也就是說現在這個對象被標記為Pending準備狀態,但沒有執行任何可能導致資料庫變化的SQL語句。那麼什麼時候會執行SQL語句並真正持久化呢?這個要等SQLAlchemy覺得需要的時候,比如我們現在查詢這個對象、對象的一個屬性或者顯式的調用flush方法,這時候SQLAlchemy覺得它“是時候”或者“不得不”執行SQL資料庫查詢以便於把標記為Pending的資料寫入資料庫表中了。假如這時候你執行的擷取對象、對象屬性或者類似的操作,SQLAlchemy在執行完SQL語句後會將你所要查詢的資料反饋給你。


為了更好的說明這一點,這裡舉一個例子,這裡涉及到我們第一個查詢樣本,我們調用了Query對象來協助我們完成這些,比如這裡我們擷取剛剛持久化的使用者ed,我們通過“過濾(filter by)”的方式來查詢使用者名稱為ed的使用者,當然我們只需要一個ed,假如有多個重名的ed的話,查詢將會返回所有叫ed的記錄集列表,我們就選擇第一個ed吧(first)。

代碼如下:


>>> our_user = session.query(User).filter_by(name='ed').first()
BEGIN (implicit)
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('ed', 'Ed Jones', 'edspassword')
SELECT users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.password AS users_password
FROM users
WHERE users.name = ?
LIMIT ? OFFSET ?
('ed', 1, 0)
>>> our_user


可以看到上面的查詢語句返回了一個User的執行個體,而這個執行個體恰恰是我們先前持久化的。同時由於我們指定了引擎的echo=True,所以再執行查詢時輸出了SQL語句,我們注意到除了普通的SELECT外,還有額外的INSERT語句,而INSERT處理的就是我們剛剛通過session.add()持久化標記為Pending的對象,也就是說你在實際操作持久化資料時才會由消極式載入(lazyload)真正觸發資料庫操作。

實際上Session查詢反饋給我們的User對象和我們剛剛持久化的對象是同一個對象,通過下面的代碼可以檢驗:

代碼如下:


>>> ed_user is our_user
True


實際上這裡ORM的操作概念文件有點類似於標識映射(identity map),也就是說在實體資料庫之前架設一張標識映射表,可以看作緩衝表的一種,任何儲存資料庫的對象會事先停留在這張表上,如果我們要查詢一個對象,將事先查詢這張標識映射表,如果這個對象存在則直接取出,否則就會查詢實體資料庫,我覺得這個有點像緩衝的作用,可以這麼理解吧。


一旦一個帶有獨一無二的主鍵的對象被Session持久化了,所有使用該主鍵在同一Session上查詢的對象將是同一個Python對象。當然對於在這個會話中持久化另外一個具有相同主鍵的對象將會拋出異常錯誤(主鍵不能重複)。

如果我們想一次性添加多個對象到Session中可以調用add_all():

代碼如下:


>>> session.add_all([
... User('wendy', 'Wendy Williams', 'foobar'),
... User('mary', 'Mary Contrary', 'xxg527'),
... User('fred', 'Fred Flinstone', 'blah')])


下面再談談修改,假如Ed覺得他的密碼不太安全,決定修改,可以直接這麼做:

代碼如下:


>>> ed_user.password = 'f8s7ccs'


同樣的道理,這個改動不會立即反映到資料庫裡,當然Session意識到你要修改Ed的密碼,它會暫時緩衝這個改動,我們可以通過dirty方法瞭解到我們的改動:

代碼如下:


>>> session.dirty
IdentitySet([])


同樣的我們可以通過new方法“窺看”到先前用add_all()持久化的對象列表:

代碼如下:


>>> session.new
IdentitySet([,
,
])


當然這些改動都沒有真正反饋到資料庫裡,相當雩都被ORM緩衝了。接下來我們可以顯式的調用commit()來告訴Session:“我們目前就添加或者改動這麼多可以提交資料庫了”:

代碼如下:


>>> session.commit()
UPDATE users SET password=? WHERE users.id = ?
('f8s7ccs', 1)
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('wendy', 'Wendy Williams', 'foobar')
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('mary', 'Mary Contrary', 'xxg527')
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('fred', 'Fred Flinstone', 'blah')
COMMIT


於是剛才緩衝的資料或者變更全部被作為事務一次性flush到資料庫了,通過輸出的SQL語句我們也可以看出來。

這個操作完成後被會話(Session)引用的資料庫連接資源將被回收到串連池中,接下來的對於這個Session的任何操作將會觸發一個新的事務(Transaction),當然會再次和串連池申請獲得資料庫連接資源。

之前文章介紹到Ed的User對象的id為None,現在讓我們來看看吧:

代碼如下:


>>> ed_user.id
BEGIN (implicit)
SELECT users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.password AS users_password
FROM users
WHERE users.id = ?
(1,)
1


除了由於echo=True導致輸出的SQL語句,看看是不是有了值,值為1。

無論是立即(commit、flush)或者通過“首次訪問載入(load-on-first-access)”,在Session在資料庫插入一條新記錄後,所有新產生的標識和資料庫產生的預設值對於執行個體來說才可以被訪問到。

當調用了commit()以後,SQLAlchemy將會重新整理當前事務的所有資料到資料庫裡。

2. 交易回復

本文以及同系列的文章是以自己的想法翻譯的,不當之處還請指正,不做權威依據。好了,下面我還是簡單介紹一下交易回復吧,其實這個和資料庫的交易回復一個意思,就是我們做錯事後要撤消之前的變更。

因為Session是作為事務(transaction)來工作的,所以我們可以復原(roll back)先前所做的更改。接下來讓我們做兩個稍後會被撤銷(復原)的更改,第一個是修改ed_user.name:

代碼如下:


>>> ed_user.name = 'Edwardo'


第二個是增加一個“不期望”的使用者fake_user:

代碼如下:


>>> fake_user = User('fakeuser', 'Invalid', '12345')
>>> session.add(fake_user)


查詢當前會話,我們可以看到這兩個變更已經被flush到當前事務裡了:

代碼如下:


>>> session.query(User).filter(User.name.in_(['Edwardo', 'fakeuser'])).all()
UPDATE users SET name=? WHERE users.id = ?
('Edwardo', 1)
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
('fakeuser', 'Invalid', '12345')
SELECT users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.password AS users_password
FROM users
WHERE users.name IN (?, ?)
('Edwardo', 'fakeuser')
[, ]


好吧,接下來是見證奇蹟的時刻,我們復原(rolling back)事務:

代碼如下:


>>> session.rollback()
ROLLBACK
>>> ed_user.name
BEGIN (implicit)
SELECT users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.password AS users_password
FROM users
WHERE users.id = ?
(1,)
u'ed'
>>> fake_user in session
False


我們可以看到ed_user的名字變回ed,並且我們不期望的使用者fake_user被“踢出”會話(Session)了。

最後,我們可以查詢一下使用者名稱在['ed', 'fakeuser']範圍的使用者,確保我們的更改是有效:

代碼如下:


>>> session.query(User).filter(User.name.in_(['ed', 'fakeuser'])).all()
SELECT users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.password AS users_password
FROM users
WHERE users.name IN (?, ?)
('ed', 'fakeuser')
[]


好了,今天就到這裡,今天我們講解了添加對象和交易回復,或多或少穿插了些簡單的查詢,接下來我們會介紹較為複雜一些的查詢語句,敬請期待!
  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.