SQLAlchemy入門(一)

發表於2017-04-07

 

環境:Ubuntu 15.10 64-bit

SQLAlchemy是Python的ORM框架,它的理念是:資料庫的量級和效能重要於物件集合,而物件集合的抽象又重要於表和行。

安裝

直接通過pip安裝:

開啟Python,測試是否安裝成功:

建立引擎

SQLite

首先以SQLite為例,因為它比較簡單。

引數 sqlite:///foo.db 解釋為:

其中foo.db是相對路徑。也可寫成:

SQLAlchemy預設使用Python內建的sqlite3模組來連線或建立SQLite資料庫。執行完create_engine後,可以發現當前目錄多了foo.db檔案,不妨用sqlite開啟看看。

注意這裡用的是sqlite3而非sqlite,因為foo.db是經由Python內建的sqlite3模組建立的。

MySQL

再來看看連線MySQL時怎麼建立引擎。
本文後續示例全部基於MySQL,這是與官方文件不同的地方。
先在MySQL裡建立一個測試資料庫:sa_test,後續示例都將基於這個資料庫。

這裡的引數看上去就比較複雜了,完整的格式為:

這裡driver用了mysqldb,詳見:MySQLdb:Python操作MySQL資料庫

引擎配置的詳細資訊可參考官方文件:Engine Configuration

MetaData

前面在建立MetaData時繫結了引擎:

當然也可以不繫結。繫結的好處是,後續很多呼叫 (比如 MetaData.create_all(),Table.create(),等等)就不用指定引擎了。

建立表

建立兩張表,user和address,address表裡有一個user id的外來鍵。
注意:表名沒有像官方文件及很多人推薦的那樣使用複數形式,個人偏好而已,詳細討論請見StackOverflow的這個問題:Table Naming Dilemma: Singular vs. Plural Names中文版

執行完metadata.create_all()這一句,兩張表就建立好了,可以在MySQL裡立即檢視。

MetaData.create_all()可以多次呼叫,不會報錯,它在內部會檢查表是否已經建立。
因為MetaData建立時已經繫結了引擎,所以此處create_all()就不必再指定了,否則得寫成:

建立引擎時,echo引數為True,程式執行時便有很多除錯資訊列印出來。在這些除錯資訊中,可以看到如下兩條MySQL的CREATE TABLE語句:

除了metadata.create_all(),Table自己也有create方法:

引數bind一般就是指引擎。
引數checkfirst表示是否檢查表已經存在。為True時,若表已經存在,不報錯,只是什麼也不做;為False時,若表已經存在,則將引發異常。
使用這個方法來建立這兩張表:

這裡忽略了bind引數,因為建立MetaData物件時已經繫結了引擎,而建立表物件時又傳入了metadata,所以順藤摸瓜,表自己是知道引擎的。
如果調整一下表的建立順序,就會報錯,因為address表裡有一個user表的外來鍵,而這時候user表還沒建立呢。所以,還是建議使用MetaData.create_all()吧,畢竟它也會檢查表是否已經存在。

表的反射 Table Reflection

表建立好了,一般也就不動了。所以實際應用時,往往表都已經存在,並不需要建立,只需把它們”匯入”進來即可,這時就得使用autoload引數。

輸出:

如果MetaData沒有繫結引擎,則另需指定autoload_with引數:

如果被反射的表外來鍵引用了另一個表,那麼被引用的表也會一併被反射。比如只反射address表,user表也一併被反射了。

輸出:

插入資料

插入資料之前,必須要有表物件,不管是新建立的,還是通過反射匯入的。

Insert物件

要往表裡插資料,先建立一個Insert物件:

列印這個Insert物件,可以看到它所對應的SQL語句:

如果連線的資料庫不是MySQL而是SQLite,那輸出可能就是下面這樣:

可見SQLAlchemy幫我們封裝了不同資料庫之間語法的差異。
如果MetaData建立時沒有繫結引擎,那麼輸出會略有不同:

這時SQLAlchemy還不知道具體的資料庫語法,表名加了引號(”user”),列名也改用為:id之類一般性的格式。
此外,這條INSERT語句列出了user表裡的每一列,而id在插入時一般是不需要指定的,可以通過Insert.values()方法加以限制:

限制後,id列已經沒有了:

可見values()方法限制了INSERT語句所包含的列。但是我們指定的name和fullname的值並沒有列印出來,這兩個值儲存在Insert物件裡,只有等到執行時才會用到。

執行

我們一直在說的引擎,可以理解成一個資料庫連線物件的倉庫,通過連線物件可以往資料庫傳送具體的SQL語句。呼叫引擎的connect()方法可以獲取一個連線:

現在把前面的Insert物件丟給它來執行:

由除錯資訊可見具體的INSERT語句:

返回值result是一個ResultProxy物件,ResultProxy是對DB-API中cursor的封裝。插入語句的結果並不常用,但是查詢語句肯定是要用到它的。
不妨在MySQL裡看一下剛插入的資料。

執行多條語句

還記得前面的Insert物件使用values()方法來限制列嗎?

這種方式其實不利於Insert物件的複用,更好的做法是把引數通過execute()方法傳進去:

Insert物件本身還是會包含所有列,最終INSERT語句裡的列由execute()的引數決定。由除錯資訊可見具體的INSERT語句:

一次插入多條記錄也很簡單,只要傳一個字典列表(每個字典的鍵必須一致)給execute()即可。

除錯資訊裡具體的INSERT語句:

在MySQL裡看一下插入的地址:

第一部分到此結束。

相關文章