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