對應版本: | 0.3.4 |
---|
這個入門指導用於SQLAlchemy的快速入門,並便利SQLAlchemy的簡單功能。若是你能夠跳過這一部分進入 主文檔 會涉及更多內容。以下的例子所有是在Python交互模式下完成了,而且所有經過了 doctest 測試。python
從 setuptools 安裝是很是簡單的,只要運行以下命令便可:sql
# easy_install SQLAlchemy
這將會在Python Cheese Shop獲取SQLAlchemy的最新版本並安裝。或者你也可使用setup.py安裝一個發行包:數據庫
# python setup.py install
SQLAlchemy被設計用於操做一個 DBAPI 實現,包括大多數常見的數據庫。若是你有一個支持DBAPI的實現,那麼能夠跳入下一節。另外SQLite是一個易於使用的數據庫,能夠快速開始,而且他可使用內存數據庫。若是要使用SQLite,你將會須要:api
注意在Windows下並不須要SQLite函數庫,由於Windows版的pysqlite已經內含了。pysqlite和SQLite能夠被安裝到Linux或FreeBSD,經過預編譯或從源碼安裝。安全
預編譯包的地址爲:session
http://initd.org/tracker/pysqlite/wiki/PysqlitePackages併發
SQLAlchemy提供了完整的命名空間,只要導入sqlalchemy便可,無需其子包。爲了方便使用本教程,咱們導入全部命名到本地命名空間:app
>>> from sqlalchemy import *
導入以後,下一步是鏈接到須要的數據庫,表現爲(represent)爲一個Engine對象。這個對象處理了鏈接的管理和特定數據庫的操做。下面,咱們鏈接SQLite基於文件的數據庫」tutorial.db」函數
>>> db=create_engine("sqlite:///tutorial.db")
建立數據庫引擎的更多信息查看」Database Engines」。
如今已經完成了安裝和鏈接數據庫,能夠開始作點實際的事情了。但首先須要有些解釋。
SQLAlchemy的核心有兩個徹底不一樣的功能,一個在另外一個之上工做。一個是 SQL語言構造器 ,另外一個是 ORM 。SQL語言構造器容許調用 ClauseElements 來構造SQL表達式。這些 ClauseElements 能夠在編譯成字符串並綁定到數據庫後用於執行,並返回一個叫作 ResultProxy 的對象,相似於一個結果集對象,可是更象dbapi高版本的 cursor 對象。
ORM是創建在SQL語言構造器之上的工具集,用於將Python對象映射到數據庫的行,提供了一系列接口用於從數據庫中存取對象(行)。在ORM 工做時,在底層調用SQL語言構造器的API,這些通用的操做有些許的不一樣。不一樣的是,你再也不使用行,而是使用自定義類的對象來操做。另外,數據庫的查詢 方式也不一樣,ORM的能夠生成大多數的SQL查詢,除此以外還能夠在類中定義更多操做。
SA功能強大,無與倫比,只是有兩個混合在一塊兒的方法有些複雜。有效的使用SA的方法是先了解這兩種不一樣的工具集,這是兩個不一樣的概念,而你們經常 混交SQL語言構造器和ORM。關鍵的不一樣是,使用cursor形式的結果集時使用的是SQL語言構造器;而使用類實例進行管理時使用的是ORM。
本指南首先介紹SQL語言構造器,首先須要聲明的數據庫信息叫作 table metadata 。本指南包含了一些SQL構造的例子,包括如何有效的使用SQL語言構造器的例子。
在SQLAlchemy的核心哲學中表格和類是不一樣的。由於如此,SQLAlchemy提供了構造表格的方法(使用表格的元信息table metadata)。因此咱們從構造表格的元信息對象和定製操做他們的對象開始。稍後咱們能夠看到SQLAlchemy的ORM,提供了表格元信息的高層 封裝,容許咱們爲所欲爲的裝載和保存Python類。
首先,你的表格必須已經在MetaData集合中。咱們將要建立簡單(handy)表格的MetaData,並自動鏈接到引擎(將一個模式(schema)對象鏈接到引擎成爲綁定binding):
>>> metadata=BoundMetaData(db)
一個構造BoundMetaData對象的等同方法是直接使用引擎URL,這將會幫咱們調用 create_engine
>>> metadata=BoundMetaData("sqlite:///tutorial.db")
如今,咱們告知metadata關於數據庫中的表格,咱們可使用(issue)CREATE語句來建立表格,而且經過他們來建立和執行SQL語 句,除非須要打開和關閉任何鏈接。這都是自動完成的。注意這個功能是推薦使用的。SQLAlchemy包含了使用模式進行鏈接管理和SQL構造的所有功 能,並能夠在任何引擎上進行操做。
本教程的目的,是教會你們使用」bound」對象,他可使得代碼簡單和易讀。
使用metadata做爲基本鏈接,咱們能夠建立表格:
>>> users_table=Table('user',metadata,
... Column('user_id',Integer,primary_key=True),
... Column('user_name',String(40)),
... Column('password',String(10))
... )
有如你看到的,咱們剛剛定義了一個叫作users的表格並擁有3個列:user_id做爲主鍵,user_name和password。它如今只是 一個對象而與數據庫中的表格沒有必然聯繫。爲了讓表格生效,咱們使用create()方法。有趣的是,咱們可讓SQLAlchemy發送SQL語句到數 據庫時同時顯示SQL語句,只要設置BoundMetaData關聯的Engine的echo選項便可:
>>> metadata.engine.echo=True
>>> users_table.create()
CREATE TABLE users (
user_id INTEGER NOT NULL,
user_name VARCHAR(40),
password VARCHAR(10),
PRIMARY KEY (user_id)
)
...
或者,若是users表格已經存在了(好比你第二次運行這些例子),在這種狀況下你能夠跳過create()方法的調用。你設置能夠跳過列定義,而是讓SQLAlchemy自動從數據庫裝入定義:
>>> users_table=Table('users',metadata,autoload=True)
>>> list(users_table.columns)[0].name
'user_id'
關於表格元信息的文檔在」Database Meda Data」中。
插入記錄是經過表格對象的insert()方法實現的,這將會定義一個子句對象(clause object)(就是CluseElement)來代理INSERT語句:
>>> i=users_table.insert()
>>> i
<sqlalchemy.sql._Insert object at 0x...>
>>> print i
INSERT INTO users (user_id,user_name,password) VALUES (?,?,?)
當咱們建立這個插入語句對象時,語句自己也綁定到了Engine,並已經能夠執行了。子句對象的execute()方法將會將對象編譯爲特定引擎的SQL方言,而且執行語句:
>>> i.execute(user_name='Mary',password='secure')
INSERT INTO users (user_name,password) VALUES (?,?)
['Mary','secure']
COMMIT
<sqlalchemy.engine.base.ResultProxy object at 0x...>
>>> i.execute({'user_name':'Tom'},{'user_name':'Fred'},{'user_name':'Harry'})
INSERT INTO users (user_name) VALUES (?)
[['Tom'],['Fred'],['Harry']]
COMMIT
<sqlalchemy.engine.base.ResultProxy object at 0x...>
注意VALUES子句會自動調整參數數量。這是由於ClauseElement的編譯步驟並不依賴於特定的數據庫,執行的參數也是如此。
當構造子句對象時,SQLAlchemy會綁定全部的值到參數。在構造時,參數綁定老是依靠鍵值對。在編譯時,SQLAlchemy會轉換他們到適當的格式,基於DBAPI的參數風格。這在DBAPI中描述的參數位置綁定中一樣工做的很好。
這些文檔繼承於」Inserts」。
咱們能夠檢查users表中已經存在的數據。方法同插入的例子,只是你須要調用表格的select()方法:
>>> s=users_table.select()
>>> print s
SELECT users.user_id,users.user_name,users.password
FROM users
>>> r=s.execute()
SELECT users.user_id,users.user_name,users.password
FROM users
[]
這時,咱們並無忽略execute()的返回值。他是一個ResultProxy實例,保存告終果,而行爲很是相似於DBAPI中的cursor對象:
>>> r
<sqlalchemy.engine.base.ResultProxy object at 0x...>
>>> r.fetchone()
(1,u'Mary',u'secure')
>>> r.fetchall()
[(2,u'Tom',None),(3,u'Fred',None),(4,u'Harry',None)]
查詢條件同Python表達式,使用Column對象。全部表達式中的Column對象都是ClauseElements的實例,例如Select、Insert和Table對象自己:
>>> r=users_table.select(users_table.c.user_name=='Harry').execute()
SELECT users.user_id,users.user_name,users.password
FROM users
WHERE users.user_name=?
['Harry']
>>> row=r.fetchone()
>>> print row
(4,u'Harry',None)
所幸的是全部標準SQL操做均可以用Python表達式來構造,包括鏈接(join)、排序(order)、分組(group)、函數 (function)、子查詢(correlated subquery)、聯合(union)等等。關於查詢的文檔」Simple Select」。
你能夠看到,當咱們打印記錄時返回的可執行對象,它以元組打印記錄。這些記錄實際上同時支持列表(list)和字典(dict)接口。字典接口容許經過字符串的列名定位字段,或者經過Column對象:
>>> row.keys()
['user_id','user_name','password']
>>> row['user_id'],row[1],row[users_table.c.password]
(4,u'Harry',None)
經過Column對象來定位是很方便的,由於這樣避免了使用列名的方式。
結果集也是支持序列操做的。可是相對於select還有微小的差異,就是容許指定所選的列:
>>> for row in select([user_table.c.user_id,users_table.c.user_name]).execute():
... print row
SELECT users.user_id,users.user_name
FROM users
[]
(1,u'Mary')
... ...
咱們能夠建立第二個表格,email_addresses,這會引用users表。定義表間的關聯,使用ForeignKey構造。咱們未來也會考慮使用表格的CREATE語句:
>>> email_addresses_table=Table('email_addresses',metadata,
... Column('address_id',Integer,primary_key=True),
... Column('email_address',String(100),nullable=False),
... Column('user_id',Integer,ForeignKey('users.user_id')))
>>> email_addresses_table.create()
CREATE TABLE email_addresses (
address_id INTEGER NOT NULL,
email_address VARCHAR(100) NOT NULL,
user_id INTEGER,
PRIMARY KEY (address_id),
FOREIGN KEY(user_id) REFERENCES users (user_id)
)
...
上面的email_addresses表與表users經過ForeignKey(’users.user_id’)相聯繫。ForeignKey 的構造器須要一個Column對象,或一個字符串表明代表和列名。當使用了字符串參數時,引用的表必須已經存在於相同的MetaData對象中,固然,可 以是另一個表。
下面能夠嘗試插入數據:
>>> email_addresses_table.insert().execute(
... {'email_address':'tom@tom.com','user_id':2},
... {'email_address':'mary@mary.com','user_id':1})
INSERT INTO email_addresses (email_address,user_id) VALUES (?,?)
[['tom@tom.com',2],['mary@mary.com',1]]
COMMIT
<sqlalchemy.engine.base.ResultProxy object at 0x...>
在兩個表之間,咱們可使用 join 方法構造一個鏈接:
>>> r=users_table.join(email_addresses_table).select().execute()
SELECT users.user_id, users.user_name, users.password,
email_addresses.address_id, email_addresses.email_address,
email_addresses.user_id
FROM users JOIN email_addresses ON users.user_id=email_addresses.user_id
[]
>>> print [row for row in r]
[(1, u'Mary', u'secure', 2, u'mary@mary.com', 1),
(2,u'Tom', None, 1, u'tom@tom.com', 2)]
join 方法同時也是 sqlalchemy 命名空間中的一個獨立的函數。鏈接條件指明瞭表對象給定的外鍵。條件(condition),也能夠叫作 「ON 子句」 ,能夠被明確的指定,例如這個例子咱們查詢全部使用郵件地址做爲密碼的用戶:
>>> print join(users_table, email_addresses_table,
... and_(users_table.c.user_id==email_addresses_table.c.user_id,
... users_table.c.password==email_addresses_table.c.email_address)
... )
users JOIN email_addresses ON users.user_id=email_addresses.user_id AND
users.password=email_address.email_address
如今咱們已經有了一些表格和SQL操做的知識了,讓咱們來看看SQLAlchemy的ORM (Object Relational Mapper) 。使用ORM,你能夠將表格(和其餘能夠查詢的對象)同Python聯繫起來,放入映射集(Mappers)當中。而後你能夠執行查詢並返回 對象實例 列表,而不是結果集。 對象實例 也被聯繫到一個叫作 Session 的對象,確保自動跟蹤對象的改變,並可使用 flush 當即保存結果。
一個映射一般對應一個Python類,其核心意圖是,「這個類的對象是用做這個表格的行來存儲的」。讓咱們建立一個類叫作 User ,描述了一個用戶對象,並保存到 users 表格。:
>>> class User(object):
... def __repr__(self):
... return "%s(%r,%r)"%(
... self.__class__.__name__,self.user_name,self.password)
這個類是一個新形式(new style)的類(繼承自 object )而且不須要構造器(在須要時默認提供)。咱們只實現了一個 __repr__ 方法,用於顯示 User 對象的基本信息。注意 __repr__ 方法應用了實例變量 user_name 和 password ,這是還沒定義的。咱們可選的定義這些屬性,並能夠進行處理;SQLAlchemy的 Mapper 的構造器會自動管理這些,並且會自動協調到 users 表格的列名。讓咱們建立映射,並觀察這些屬性的定義:
>>> usermapper=mapper(User,users_table)
>>> ul=User()
>>> print ul.user_name
None
>>> print ul.password
None
函數 mapper 返回新建的 Mapper 實例。這也是咱們爲 User 類建立的第一個映射,也就是類的 主映射 。通常來講,不須要保存 usermapper 變量;SA的ORM會自動管理這個映射。
建立了一個映射以後,全部對映射的操做都須要一個重要的對象叫作 Session 。全部對象經過映射的載入和保存都 必須 經過 Session 對象,有如對象的工做空間同樣被加載到內存。特定對象在特定時間只能關聯到一個 Session 。
缺省時,須要在載入和保存對象以前明確的建立 Session 對象。有多種方法來管理會話,但最簡明的方法是調用 create_session()
>>> session=create_session()
>>> session
<sqlalchemy.orm.session.Session object at 0x...>
會話對象擁有載入和存儲對象的全部方法,同時也能夠查看他們的狀態。會話還提供了查詢數據庫的方便接口,你能夠獲取一個 Query 對象:
>>> query=session.query(User)
>>> print query.select_by(user_name='Harry')
SELECT users.user_name AS users_user_name, users.password AS users_password,
users.user_id AS users_user_id
FROM users
WHERE users.user_name=? ORDER BY users.oid
['Harry']
[User(u'Harry',None)]
對象全部的查詢操做實際上都是經過 Query 的。 Mapper 對象的多種 select 方法也是偷偷的在使用 Query 對象來執行操做。一個 Query 老是聯繫到一個特定的會話上。
讓咱們暫時關閉數據庫回顯,並嘗試 Query 的幾個方法。結尾是 _by 的方法主要用於對象的鍵參數。其餘的方法容許接受 ClauseElement 對象,使用 Column 對象的Python表達式產生,一樣的方法咱們在上一節使用過。使用 ClauseElement 結構來查詢更加冗長,可是更加靈活:
>>> metadata.engine.echo=False
>>> print query.select(User.c.user_id==3)
[User(u'Fred',None)]
>>> print query.get(2)
User(u'Tom',None)
>>> print query.get_by(user_name='Mary')
User(u'Mary',u'secure')
>>> print query.selectfirst(User.c.password==None)
User(u'Tom',None)
>>> print query.count()
4
Note
User類有一個特別的屬性 c ,這個屬性描述了User映射表格對象的列。
User.c.user_name 等同於 users_table.c.user_name ,記得 User 是Python對象,而 users 是 Table 對象。
做爲小經驗,咱們看看如何作出修改。首先,建立一個新的用戶」Ed」,而後加入會話:
>>> ed=User()
>>> ed.user_name='Ed'
>>> ed.password='edspassword'
>>> session.save(ed)
>>> ed in session
True
也能夠修改數據庫中的其餘對象。使用 Query 對象載入,而後改變:
>>> mary=query.get_by(user_name='Mary')
>>> harry=query.get_by(user_name='Harry')
>>> mary.password='marysnewpassword'
>>> harry.password='harrysnewpassword'
這時,什麼東西都沒有保存到數據庫;咱們全部的修改都在內存中。若是這時程序其餘部分嘗試修改’Mary’會發生什麼呢?由於使用相同的會話,因此再次載入’Mary’實際上定位到相同的主鍵’Mary’,而且 返回同一對象實例 。這個行爲用在會話中確保數據庫的一致性:
>>> mary2=query.get_by(user_name='Mary')
>>> mary is mary2
True
在惟一映射中,單一的會話能夠確保安全的操做對象。
若是兩個不一樣的會話同時操做一個對象,會檢測到併發;SA會使用簡單的併發控制來保存對象,能夠選擇使用擁有更強的使用ids的檢查。參考 Mapper Arguments 瞭解更多細節。
在新建了用戶」ed」並對」Mary」和」Harry」做修改以後,咱們再刪除」Fred」
>>> fred=query.get_by(user_name='Fred')
>>> session.delete(fred)
而後發送更改到數據庫,使用會話的 flush() 方法。開啓回顯來查看過程:
>>> metadata.engine.echo=True
>>> session.flush()
BEGIN
UPDATE users SET password=? WHERE users.user_id=?
['marysnewpassword',1]
UPDATE users SET password=? WHERE users.user_id=?
['harrysnewpassword',4]
INSERT INTO users (user_name,password) VALUES (?,?)
['Ed','edspassword']
DELETE FROM users WHERE users.user_id=?
[3]
COMMIT
若是一個關係包含其餘信息時,例如包含郵件地址的列表,咱們能夠在使用 relation() 建立 Mapper 時聲明。固然,你還能夠對這個關係做不少事情,咱們舉幾個簡單的例子。首先使用 users 表,它擁有一個外鍵關係鏈接到 email_addresses 表。 email_addresses 表中的每一行都有列 user_id 用來引用 users 表中的一行;並且 email_addresses 表中的多行能夠引用 users 表中的同一行,這叫一對多關係。
首先,處理 email_addresses 表。咱們建立一個新的類 Address 描述了 email_addresses 表中的一行,而且也建立了用於 Address 類的映射對象:
>>> class Address(object):
... def __init__(self,email_address):
... self.email_address=email_address
... def __repr__(self):
... return "%s(%r)"%(
... self.__class__.__name__,self.email_address)
>>> mapper(Address, email_addresses_table)
<sqlalchemy.orm.mapper.Mapper object at 0x...>
而後,咱們經過使用 relation() 建立一個關係鏈接 User 和 Address 類,而且添加關係到 User 映射,使用 add_property 函數:
>>> usermapper.add_property('addresses',relation(Address))
函數 relation() 須要一個類或映射做爲首參數,而且還有不少選項來控制行爲。 User 映射如今給每個 User 實例添加了一個屬性叫 addresses 。SA將會自動檢測這個一對多關係。而且隨後建立 addresses 列表。當新的 User 對象建立時,這個列表爲空 。
讓咱們看看數據庫作了什麼。當咱們修改映射的配置時,最好清理一下會話,讓全部載入的 User 對象能夠從新載入:
>>> session.clear()
咱們隨之可使用 User 對象的 addresses 屬性來象列表同樣處理:
>>> mary=query.get_by(user_name='Mary')
SELECT users.user_name AS users_user_name, users.password AS users_password,
users.user_id AS users_user_id
FROM users
WHERE users.user_name=? ORDER BY users.oid
LIMIT 1 OFFSET 0
['Mary']
>>> print [a for a in mary.address]
SELECT email_addresses.user_id AS email_address_user_id,
email_addresses.address_id AS email_addresses_address_id,
email_addresses.email_address AS email_addresses_email_address
FROM email_addresses
WHERE ?= email_addresses.user_id ORDER BY email_addresses.oid
[1]
[Address(u'mary@mary.com')]
向列表添加元素也很簡單。新的 Address 對象將會在調用會話的flush()時保存:
>>> mary.addresses.append(Address('mary2@gmail.com'))
>>> session.flush()
BEGIN
INSERT INTO email_addresses (email_address,user_id) VALUEs (?,?)
['mary2@gmail.com',1]
COMMIT
主文檔中關於使用映射的部分在以下地址:
http://www.sqlalchemy.org/docs/datamapping.myt#datamapping
你可能已經注意到在上面例子中的 session.flush() ,SQLAlchemy使用 BEGIN 和 COMMIT 來使用數據庫事務。 flush() 方法使用事務來對一些記錄執行一系列的指令。若是但願在 flush() 以外使用更大規模的事務,能夠經過 SessionTransaction 對象,其生成使用 session.create_transaction() 。下面將會執行一個很是複雜的 SELECT 語句,進行大量的修改,而後再建立一個有兩個郵箱的用戶,這些都在事務中完成。並且將會在中間使用 flush() 來保存,而後在執行最後的 commit() 時將全部改變寫入數據庫。咱們把事務封裝在一個 try/except 語句塊當中確保資源的安全釋放:
>>> transaction=session.create_transaction()
>>> try:
... (ed,harry,mary)=session.query(User).select(
... User.c.user_name.in_('Ed','Harry','Mary'),
... order_by=User.c.user_name
... )
... del mary.address[1]
... harry.addresses.append(Address('harry2@gmail.com'))
... session.flush()
... print "***flushed the session***"
... fred=User()
... fred.user_name='fred_again'
... fred.addresses.append(Address('fred@fred.com'))
... fred.addresses.append(Address('fredsnewemail@fred.com'))
... session.save(fred)
... transaction.commit()
... except:
... transaction.rollback()
... raise
BEGIN
SELECT users.user_name AS users_user_name,
users.password AS users_password,
users.user_id AS users_user_id
FROM users
WHERE users.user_name IN (?, ?, ?) ORDER BY users.user_name
['Ed', 'Harry', 'Mary']
SELECT email_addresses.user_id AS email_addresses_user_id,
email_addresses.address_id AS email_addresses_address_id,
email_addresses.email_address AS email_addresses_email_address
FROM email_addresses
WHERE ? = email_addresses.user_id ORDER BY email_addresses.oid
[4]
UPDATE email_addresses SET user_id=? WHERE email_addresses.address_id = ?
[None, 3]
INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?)
['harry2@gmail.com', 4]
***flushed the session***
INSERT INTO users (user_name, password) VALUES (?, ?)
['fred_again', None]
INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?)
['fred@fred.com', 6]
INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?)
['fredsnewemail@fred.com', 6]
COMMIT
對應的主文檔:
http://www.sqlalchemy.org/docs/unitofwork.myt#unitofwork
如上已經介紹了一下SQLAlchemy。可是不一樣的人可能有不一樣的作事方法,好比定義不一樣風格的映射的關係,因此仍是容許使用原始的SQL來定義表格,還有Engine、SQL語句、數據庫鏈接等。
連接:http://gashero.yeax.com/?p=6#id9