摘要:參數(shù)表示是否檢查表已經(jīng)存在。此外,這條語句列出了表里的每一列,而在插入時一般是不需要指定的,可以通過方法加以限制限制后,列已經(jīng)沒有了可見方法限制了語句所包含的列。
環(huán)境:Ubuntu 15.10 64-bit
SQLAlchemy 是 Python 的 ORM 框架,它的理念是:數(shù)據(jù)庫的量級和性能重要于對象集合,而對象集合的抽象又重要于表和行。
安裝直接通過 pip 安裝:
$ pip install sqlalchemy
打開 Python,測試是否安裝成功:
>>> import sqlalchemy >>> sqlalchemy.__version__ "1.0.9"創(chuàng)建引擎 SQLite
首先以 SQLite 為例,因?yàn)樗容^簡單。
from sqlalchemy import create_engine, MetaData engine = create_engine("sqlite:///foo.db", echo=True) metadata = MetaData(engine)
參數(shù) sqlite:///foo.db 解釋為:
sqlite:///
其中foo.db是相對路徑。也可寫成:
sqlite:///./foo.db
SQLAlchemy 缺省使用 Python 內(nèi)建的 sqlite3 模塊來連接或創(chuàng)建 SQLite 數(shù)據(jù)庫。執(zhí)行完 create_engine 后,可以發(fā)現(xiàn)當(dāng)前目錄多了 foo.db 文件,不妨用 sqlite 打開看看。
$ sqlite3 foo.db SQLite version 3.8.11.1 2015-07-29 20:00:57 Enter ".help" for usage hints. sqlite> .tables
注意這里用的是 sqlite3 而非 sqlite,因?yàn)?foo.db 是經(jīng)由 Python 內(nèi)建的 sqlite3 模塊創(chuàng)建的。
MySQL再來看看連接 MySQL 時怎么創(chuàng)建引擎。
本文后續(xù)示例全部基于 MySQL,這是與官方文檔不同的地方。
先在MySQL里創(chuàng)建一個測試數(shù)據(jù)庫:sa_test,后續(xù)示例都將基于這個數(shù)據(jù)庫。
mysql> CREATE DATABASE sa_test DEFAULT CHARACTER SET UTF8;
from sqlalchemy import create_engine, MetaData engine = create_engine("mysql+mysqldb://root:******@localhost/sa_test", echo=True) metadata = MetaData(engine)
這里的參數(shù)看上去就比較復(fù)雜了,完整的格式為:
dialect+driver://username:password@host:port/database
這里 driver 用了 mysqldb,詳見:MySQLdb:Python 操作 MySQL 數(shù)據(jù)庫
引擎配置的詳細(xì)信息可參考官方文檔:Engine Configuration
MetaData前面在創(chuàng)建 MetaData 時綁定了引擎:
metadata = MetaData(engine)
當(dāng)然也可以不綁定。綁定的好處是,后續(xù)很多調(diào)用 (比如 MetaData.create_all(),Table.create(),等等)就不用指定引擎了。
創(chuàng)建表創(chuàng)建兩張表,user 和 address,address 表里有一個 user id 的外鍵。
注意:表名沒有像官方文檔及很多人推薦的那樣使用復(fù)數(shù)形式,個人偏好而已,詳細(xì)討論請見 StackOverflow 的這個問題:Table Naming Dilemma: Singular vs. Plural Names (中文版)
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, ForeignKey engine = create_engine("mysql+mysqldb://root:******@localhost/sa_test", echo=True) metadata = MetaData(engine)
user_table = Table("user", metadata, Column("id", Integer, primary_key=True), Column("name", String(50)), Column("fullname", String(100)) ) address_table = Table("address", metadata, Column("id", Integer, primary_key=True), Column("user_id", None, ForeignKey("user.id")), Column("email", String(128), nullable=False) ) metadata.create_all()
執(zhí)行完 metadata.create_all() 這一句,兩張表就創(chuàng)建好了,可以在 MySQL 里立即查看。
MetaData.create_all() 可以多次調(diào)用,不會報錯,它在內(nèi)部會檢查表是否已經(jīng)創(chuàng)建。
因?yàn)?MetaData 創(chuàng)建時已經(jīng)綁定了引擎,所以此處 create_all() 就不必再指定了,否則得寫成:
metadata.create_all(engine)
創(chuàng)建引擎時,echo 參數(shù)為 True,程序運(yùn)行時便有很多調(diào)試信息打印出來。在這些調(diào)試信息中,可以看到如下兩條 MySQL的CREATE TABLE 語句:
CREATE TABLE user ( id INTEGER NOT NULL AUTO_INCREMENT, name VARCHAR(50), fullname VARCHAR(100), PRIMARY KEY (id) ) CREATE TABLE address ( id INTEGER NOT NULL AUTO_INCREMENT, user_id INTEGER, email VARCHAR(128) NOT NULL, PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES user (id) )
除了 metadata.create_all(),Table 自己也有 create 方法:
create(bind=None, checkfirst=False)
參數(shù) bind 一般就是指引擎。
參數(shù) checkfirst 表示是否檢查表已經(jīng)存在。為 True 時,若表已經(jīng)存在,不報錯,只是什么也不做;為
False 時,若表已經(jīng)存在,則將引發(fā)異常。
使用這個方法來創(chuàng)建這兩張表:
user_table.create(checkfirst=True) address_table.create(checkfirst=True)
這里忽略了 bind 參數(shù),因?yàn)閯?chuàng)建 MetaData 對象時已經(jīng)綁定了引擎,而創(chuàng)建表對象時又傳入了 metadata,所以順藤摸瓜,表自己是知道引擎的。
如果調(diào)整一下表的創(chuàng)建順序,就會報錯,因?yàn)?address 表里有一個 user 表的外鍵,而這時候 user 表還沒創(chuàng)建呢。所以,還是建議使用 MetaData.create_all() 吧,畢竟它也會檢查表是否已經(jīng)存在。
表創(chuàng)建好了,一般也就不動了。所以實(shí)際應(yīng)用時,往往表都已經(jīng)存在,并不需要創(chuàng)建,只需把它們”導(dǎo)入”進(jìn)來即可,這時就得使用 autoload 參數(shù)。
from sqlalchemy import create_engine, MetaData, Table engine = create_engine("mysql+mysqldb://root:******@localhost/sa_test", echo=False) metadata = MetaData(engine) user_table = Table("user", metadata, autoload=True) print "user" in metadata.tables print [c.name for c in user_table.columns] address_table = Table("address", metadata, autoload=True) print "address" in metadata.tables
輸出:
True ["id", "name", "fullname"] True
如果 MetaData 沒有綁定引擎,則另需指定 autoload_with 參數(shù):
user_table = Table("user", metadata, autoload=True, autoload_with=engine)
如果被反射的表外鍵引用了另一個表,那么被引用的表也會一并被反射。比如只反射 address 表,user 表也一并被反射了。
from sqlalchemy import create_engine, MetaData, Table engine = create_engine("mysql+mysqldb://root:******@localhost/sa_test", echo=False) metadata = MetaData(engine) address_table = Table("address", metadata, autoload=True) print "user" in metadata.tables print "address" in metadata.tables
輸出:
True True插入數(shù)據(jù)
插入數(shù)據(jù)之前,必須要有表對象,不管是新創(chuàng)建的,還是通過反射導(dǎo)入的。
Insert 對象要往表里插數(shù)據(jù),先創(chuàng)建一個 Insert 對象:
ins = user_table.insert() print ins
打印這個 Insert 對象,可以看到它所對應(yīng)的 SQL 語句:
INSERT INTO user (id, name, fullname) VALUES (%s, %s, %s)
如果連接的數(shù)據(jù)庫不是 MySQL 而是 SQLite,那輸出可能就是下面這樣:
INSERT INTO user (id, name, fullname) VALUES (?, ?, ?)
可見 SQLAlchemy 幫我們封裝了不同數(shù)據(jù)庫之間語法的差異。
如果 MetaData 創(chuàng)建時沒有綁定引擎,那么輸出會略有不同:
INSERT INTO "user" (id, name, fullname) VALUES (:id, :name, :fullname)
這時 SQLAlchemy 還不知道具體的數(shù)據(jù)庫語法,表名加了引號("user"),列名也改用為:id之類一般性的格式。
此外,這條INSERT語句列出了 user 表里的每一列,而id在插入時一般是不需要指定的,可以通過
Insert.values() 方法加以限制:
ins = ins.values(name="adam", fullname="Adam Gu") print ins
限制后,id 列已經(jīng)沒有了:
INSERT INTO user (name, fullname) VALUES (%s, %s)
可見 values() 方法限制了 INSERT 語句所包含的列。但是我們指定的 name 和 fullname 的值并沒有打印出來,這兩個值保存在 Insert 對象里,只有等到執(zhí)行時才會用到。
執(zhí)行我們一直在說的引擎,可以理解成一個數(shù)據(jù)庫連接對象的倉庫,通過連接對象可以往數(shù)據(jù)庫發(fā)送具體的 SQL 語句。調(diào)用引擎的 connect() 方法可以獲取一個連接:
conn = engine.connect()
現(xiàn)在把前面的 Insert 對象丟給它來執(zhí)行:
result = conn.execute(ins)
由調(diào)試信息可見具體的 INSERT 語句:
INSERT INTO user (name, fullname) VALUES (%s, %s) ("adam", "Adam Gu") COMMIT
返回值 result 是一個 ResultProxy 對象,ResultProxy 是對 DB-API 中 cursor 的封裝。插入語句的結(jié)果并不常用,但是查詢語句肯定是要用到它的。
不妨在 MySQL 里看一下剛插入的數(shù)據(jù)。
mysql> select * from user; +----+------+----------+ | id | name | fullname | +----+------+----------+ | 1 | adam | Adam Gu | +----+------+----------+ 1 row in set (0.00 sec)執(zhí)行多條語句
還記得前面的 Insert 對象使用 values() 方法來限制列嗎?
ins = ins.values(name="adam", fullname="Adam Gu")
這種方式其實(shí)不利于 Insert 對象的復(fù)用,更好的做法是把參數(shù)通過 execute() 方法傳進(jìn)去:
ins = user_table.insert() conn.execute(ins, name="adam", fullname="Adam Gu")
Insert 對象本身還是會包含所有列,最終 INSERT 語句里的列由 execute() 的參數(shù)決定。由調(diào)試信息可見具體的 INSERT 語句:
INSERT INTO user (name, fullname) VALUES (%s, %s) ("adam", "Adam Gu") COMMIT
一次插入多條記錄也很簡單,只要傳一個字典列表(每個字典的鍵必須一致)給 execute() 即可。
conn.execute(address_table.insert(), [ { "user_id": 1, "email": "sprinfall@gmail.com" }, { "user_id": 1, "email": "sprinfall@hotmail.com" }, ])
調(diào)試信息里具體的 INSERT 語句:
INSERT INTO address (user_id, email) VALUES (%s, %s) ((1, "sprinfall@gmail.com"), (1, "sprinfall@hotmail.com")) COMMIT
在 MySQL 里看一下插入的地址:
mysql> select * from address; +----+---------+-----------------------+ | id | user_id | email | +----+---------+-----------------------+ | 1 | 1 | sprinfall@gmail.com | | 2 | 1 | sprinfall@hotmail.com | +----+---------+-----------------------+ 2 rows in set (0.00 sec)
第一部分到此結(jié)束。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/17526.html
摘要:下一篇文章第節(jié)查詢條件設(shè)置是編程語言下的一款開源軟件。提供了工具包及對象關(guān)系映射工具,使用許可證發(fā)行。在關(guān)閉連接時會自動進(jìn)行事務(wù)提交操作。引入多條件查詢時使用。由于上下文函數(shù)退出時會自動提交事務(wù),所以無需顯示的調(diào)用使新增生效。 下一篇文章:Python-SQLAlchemy:第2節(jié):查詢條件設(shè)置 SQLAlchemy是Python編程語言下的一款開源軟件。提供了SQL工具包及對象關(guān)系...
摘要:參數(shù)表示是否檢查表已經(jīng)存在。此外,這條語句列出了表里的每一列,而在插入時一般是不需要指定的,可以通過方法加以限制限制后,列已經(jīng)沒有了可見方法限制了語句所包含的列。 環(huán)境:Ubuntu 15.10 64-bit SQLAlchemy 是 Python 的 ORM 框架,它的理念是:數(shù)據(jù)庫的量級和性能重要于對象集合,而對象集合的抽象又重要于表和行。 安裝 直接通過 pip 安裝: $ pi...
摘要:的模型使用模型的原因當(dāng)項目越來越大的時候會出現(xiàn)很多問題原生較多重復(fù)使用率低如果你的數(shù)據(jù)庫發(fā)生了改變所有的原生就都要進(jìn)行修改寫原生的時候會有安全隱患中文件關(guān)系對象的映射使用去操作數(shù)據(jù)庫的時候不會再去寫原生的了通過把表映射成類字段為你的屬性在 Flask-SQLalchemy flask的ORM模型 使用ORM模型的原因 當(dāng)項目越來越大的時候 會出現(xiàn)很多問題 原生SQL較多 重復(fù)使...
閱讀 780·2023-04-25 20:47
閱讀 2546·2019-08-30 15:53
閱讀 955·2019-08-26 14:05
閱讀 901·2019-08-26 11:59
閱讀 1689·2019-08-26 11:43
閱讀 1688·2019-08-26 10:57
閱讀 1366·2019-08-23 18:23
閱讀 2678·2019-08-23 12:57