SQLAlchemy指南(tutorial)

轉自:http://blog.csdn.net/dupei/article/details/6014488

SQLAlchemy指南(tutorial)

對應版本: 0.3.4

目錄

這個入門指導用於SQLAlchemy的快速入門,並便利SQLAlchemy的簡單功能。如果你可以跳過這一部分進入 主文檔 會涉及更多內容。如下的例子全部是在Python交互模式下完成了,並且全部通過了 doctest 測試。

1 安裝

1.1 安裝SQLAlchemy

從 setuptools 安裝是非常簡單的,只要運行如下命令即可:

# easy_install SQLAlchemy

這將會在Python Cheese Shop獲取SQLAlchemy的最新版本並安裝。或者你也可以使用setup.py安裝一個發行包:

# python setup.py install

1.2 安裝一個數據庫API

SQLAlchemy被設計用於操作一個 DBAPI 實現,包括大多數常見的數據庫。如果你有一個支持DBAPI的實現,那麼可以跳入下一節。另外SQLite是一個易於使用的數據庫,可以快速開始,並且他可以使用內存數據庫。如果要使用SQLite,你將會需要:

  • pysqlite - SQLite的Python接口
  • SQLite函數庫

注意在Windows下並不需要SQLite函數庫,因爲Windows版的pysqlite已經內含了。pysqlite和SQLite可以被安裝到Linux或FreeBSD,通過預編譯或從源碼安裝。

預編譯包的地址爲:

http://initd.org/tracker/pysqlite/wiki/PysqlitePackages

2 快速開始

2.1 導入

SQLAlchemy提供了完整的命名空間,只要導入sqlalchemy即可,無需其子包。爲了方便使用本教程,我們導入所有命名到本地命名空間:

>>> from sqlalchemy import *

2.2 連接到數據庫

導入之後,下一步是連接到需要的數據庫,表現爲(represent)爲一個Engine對象。這個對象處理了連接的管理和特定數據庫的操作。下面,我們連接SQLite基於文件的數據庫”tutorial.db”

>>> db=create_engine("sqlite:///tutorial.db")

創建數據庫引擎的更多信息查看”Database Engines”。

3 SQLAlchemy是兩個庫的包裝

現在已經完成了安裝和連接數據庫,可以開始做點實際的事情了。但首先需要有些解釋。

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語言構造器的例子。

4 操作數據庫對象

在SQLAlchemy的核心哲學中表格和類是不同的。因爲如此,SQLAlchemy提供了構造表格的方法(使用表格的元信息table metadata)。所以我們從構造表格的元信息對象和定製操作他們的對象開始。稍後我們可以看到SQLAlchemy的ORM,提供了表格元信息的高層 封裝,允許我們隨心所欲的裝載和保存Python類。

4.1 定義元信息,綁定到引擎

首先,你的表格必須已經在MetaData集合中。我們將要創建簡單(handy)表格的MetaData,並自動連接到引擎(將一個模式(schema)對象連接到引擎成爲綁定binding):

>>> metadata=BoundMetaData(db)

一個構造BoundMetaData對象的等同方法是直接使用引擎URL,這將會幫我們調用 create_engine

>>> metadata=BoundMetaData("sqlite:///tutorial.db")

現在,我們告知metadata關於數據庫中的表格,我們可以使用(issue)CREATE語句來創建表格,並且通過他們來創建和執行SQL語 句,除非需要打開和關閉任何連接。這都是自動完成的。注意這個功能是推薦使用的。SQLAlchemy包含了使用模式進行連接管理和SQL構造的全部功 能,並可以在任何引擎上進行操作。

本教程的目的,是教會大家使用”bound”對象,他可以使得代碼簡單和易讀。

4.2 創建表格

使用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”中。

4.3 插入記錄

插入記錄是通過表格對象的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”。

4.4 查詢

我們可以檢查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”。

4.5 操作記錄

你可以看到,當我們打印記錄時返回的可執行對象,它以元組打印記錄。這些記錄實際上同時支持列表(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')

... ...

4.6 表間關係

我們可以創建第二個表格,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':'[email protected]','user_id':2},

...     {'email_address':'[email protected]','user_id':1})

INSERT INTO email_addresses (email_address,user_id) VALUES (?,?)

[['[email protected]',2],['[email protected]',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'[email protected]', 1),

(2,u'Tom', None, 1, u'[email protected]', 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

5 使用ORM工作

現在我們已經有了一些表格和SQL操作的知識了,讓我們來看看SQLAlchemy的ORM (Object Relational Mapper) 。使用ORM,你可以將表格(和其他可以查詢的對象)同Python聯繫起來,放入映射集(Mappers)當中。然後你可以執行查詢並返回 對象實例 列表,而不是結果集。 對象實例 也被聯繫到一個叫做 Session 的對象,確保自動跟蹤對象的改變,並可以使用 flush 立即保存結果。

5.1 創建一個映射

一個映射通常對應一個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會自動管理這個映射。

5.2 獲取會話(Session)

創建了一個映射之後,所有對映射的操作都需要一個重要的對象叫做 Session 。所有對象通過映射的載入和保存都 必須 通過 Session 對象,有如對象的工作空間一樣被加載到內存。特定對象在特定時間只能關聯到一個Session 。

缺省時,需要在載入和保存對象之前明確的創建 Session 對象。有多種方法來管理會話,但最簡明的方法是調用create_session()

>>> session=create_session()

>>> session

<sqlalchemy.orm.session.Session object at 0x...>

5.3 查詢對象

會話對象擁有載入和存儲對象的所有方法,同時也可以查看他們的狀態。會話還提供了查詢數據庫的方便接口,你可以獲取一個 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 對象。

5.4 修改數據

作爲小經驗,我們看看如何做出修改。首先,創建一個新的用戶”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 瞭解更多細節。

5.5 保存

在新建了用戶”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

5.6 關係

如果一個關係包含其他信息時,例如包含郵件地址的列表,我們可以在使用 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'[email protected]')]

向列表添加元素也很簡單。新的 Address 對象將會在調用會話的flush()時保存:

>>> mary.addresses.append(Address('[email protected]'))

>>> session.flush()

BEGIN

INSERT INTO email_addresses (email_address,user_id) VALUEs (?,?)

['[email protected]',1]

COMMIT

主文檔中關於使用映射的部分在如下地址:

http://www.sqlalchemy.org/docs/datamapping.myt#datamapping

5.7 事務

你可能已經注意到在上面例子中的 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('[email protected]'))

...     session.flush()

...     print "***flushed the session***"

...     fred=User()

...     fred.user_name='fred_again'

...     fred.addresses.append(Address('[email protected]'))

...     fred.addresses.append(Address('[email protected]'))

...     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 (?, ?)

['[email protected]', 4]

***flushed the session***

INSERT INTO users (user_name, password) VALUES (?, ?)

['fred_again', None]

INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?)

['[email protected]', 6]

INSERT INTO email_addresses (email_address, user_id) VALUES (?, ?)

['[email protected]', 6]

COMMIT

對應的主文檔:

http://www.sqlalchemy.org/docs/unitofwork.myt#unitofwork

5.8 下一步

如上已經介紹了一下SQLAlchemy。但是不同的人可能有不同的做事方法,比如定義不同風格的映射的關係,所以還是允許使用原始的SQL來定義表格,還有Engine、SQL語句、數據庫連接等。

 

鏈接:http://gashero.yeax.com/?p=6#id9

發佈了19 篇原創文章 · 獲贊 6 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章