摘要:參數表示是否檢查表已經存在。此外,這條語句列出了表里的每一列,而在插入時一般是不需要指定的,可以通過方法加以限制限制后,列已經沒有了可見方法限制了語句所包含的列。
環境:Ubuntu 15.10 64-bit
SQLAlchemy 是 Python 的 ORM 框架,它的理念是:數據庫的量級和性能重要于對象集合,而對象集合的抽象又重要于表和行。
安裝直接通過 pip 安裝:
$ pip install sqlalchemy
打開 Python,測試是否安裝成功:
>>> import sqlalchemy >>> sqlalchemy.__version__ "1.0.9"創建引擎 SQLite
首先以 SQLite 為例,因為它比較簡單。
from sqlalchemy import create_engine, MetaData engine = create_engine("sqlite:///foo.db", echo=True) metadata = MetaData(engine)
參數 sqlite:///foo.db 解釋為:
sqlite:///
其中foo.db是相對路徑。也可寫成:
sqlite:///./foo.db
SQLAlchemy 缺省使用 Python 內建的 sqlite3 模塊來連接或創建 SQLite 數據庫。執行完 create_engine 后,可以發現當前目錄多了 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,因為 foo.db 是經由 Python 內建的 sqlite3 模塊創建的。
MySQL再來看看連接 MySQL 時怎么創建引擎。
本文后續示例全部基于 MySQL,這是與官方文檔不同的地方。
先在MySQL里創建一個測試數據庫:sa_test,后續示例都將基于這個數據庫。
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)
這里的參數看上去就比較復雜了,完整的格式為:
dialect+driver://username:password@host:port/database
這里 driver 用了 mysqldb,詳見:MySQLdb:Python 操作 MySQL 數據庫
引擎配置的詳細信息可參考官方文檔:Engine Configuration
MetaData前面在創建 MetaData 時綁定了引擎:
metadata = MetaData(engine)
當然也可以不綁定。綁定的好處是,后續很多調用 (比如 MetaData.create_all(),Table.create(),等等)就不用指定引擎了。
創建表創建兩張表,user 和 address,address 表里有一個 user id 的外鍵。
注意:表名沒有像官方文檔及很多人推薦的那樣使用復數形式,個人偏好而已,詳細討論請見 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()
執行完 metadata.create_all() 這一句,兩張表就創建好了,可以在 MySQL 里立即查看。
MetaData.create_all() 可以多次調用,不會報錯,它在內部會檢查表是否已經創建。
因為 MetaData 創建時已經綁定了引擎,所以此處 create_all() 就不必再指定了,否則得寫成:
metadata.create_all(engine)
創建引擎時,echo 參數為 True,程序運行時便有很多調試信息打印出來。在這些調試信息中,可以看到如下兩條 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)
參數 bind 一般就是指引擎。
參數 checkfirst 表示是否檢查表已經存在。為 True 時,若表已經存在,不報錯,只是什么也不做;為
False 時,若表已經存在,則將引發異常。
使用這個方法來創建這兩張表:
user_table.create(checkfirst=True) address_table.create(checkfirst=True)
這里忽略了 bind 參數,因為創建 MetaData 對象時已經綁定了引擎,而創建表對象時又傳入了 metadata,所以順藤摸瓜,表自己是知道引擎的。
如果調整一下表的創建順序,就會報錯,因為 address 表里有一個 user 表的外鍵,而這時候 user 表還沒創建呢。所以,還是建議使用 MetaData.create_all() 吧,畢竟它也會檢查表是否已經存在。
表創建好了,一般也就不動了。所以實際應用時,往往表都已經存在,并不需要創建,只需把它們”導入”進來即可,這時就得使用 autoload 參數。
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 參數:
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插入數據
插入數據之前,必須要有表對象,不管是新創建的,還是通過反射導入的。
Insert 對象要往表里插數據,先創建一個 Insert 對象:
ins = user_table.insert() print ins
打印這個 Insert 對象,可以看到它所對應的 SQL 語句:
INSERT INTO user (id, name, fullname) VALUES (%s, %s, %s)
如果連接的數據庫不是 MySQL 而是 SQLite,那輸出可能就是下面這樣:
INSERT INTO user (id, name, fullname) VALUES (?, ?, ?)
可見 SQLAlchemy 幫我們封裝了不同數據庫之間語法的差異。
如果 MetaData 創建時沒有綁定引擎,那么輸出會略有不同:
INSERT INTO "user" (id, name, fullname) VALUES (:id, :name, :fullname)
這時 SQLAlchemy 還不知道具體的數據庫語法,表名加了引號("user"),列名也改用為:id之類一般性的格式。
此外,這條INSERT語句列出了 user 表里的每一列,而id在插入時一般是不需要指定的,可以通過
Insert.values() 方法加以限制:
ins = ins.values(name="adam", fullname="Adam Gu") print ins
限制后,id 列已經沒有了:
INSERT INTO user (name, fullname) VALUES (%s, %s)
可見 values() 方法限制了 INSERT 語句所包含的列。但是我們指定的 name 和 fullname 的值并沒有打印出來,這兩個值保存在 Insert 對象里,只有等到執行時才會用到。
執行我們一直在說的引擎,可以理解成一個數據庫連接對象的倉庫,通過連接對象可以往數據庫發送具體的 SQL 語句。調用引擎的 connect() 方法可以獲取一個連接:
conn = engine.connect()
現在把前面的 Insert 對象丟給它來執行:
result = conn.execute(ins)
由調試信息可見具體的 INSERT 語句:
INSERT INTO user (name, fullname) VALUES (%s, %s) ("adam", "Adam Gu") COMMIT
返回值 result 是一個 ResultProxy 對象,ResultProxy 是對 DB-API 中 cursor 的封裝。插入語句的結果并不常用,但是查詢語句肯定是要用到它的。
不妨在 MySQL 里看一下剛插入的數據。
mysql> select * from user; +----+------+----------+ | id | name | fullname | +----+------+----------+ | 1 | adam | Adam Gu | +----+------+----------+ 1 row in set (0.00 sec)執行多條語句
還記得前面的 Insert 對象使用 values() 方法來限制列嗎?
ins = ins.values(name="adam", fullname="Adam Gu")
這種方式其實不利于 Insert 對象的復用,更好的做法是把參數通過 execute() 方法傳進去:
ins = user_table.insert() conn.execute(ins, name="adam", fullname="Adam Gu")
Insert 對象本身還是會包含所有列,最終 INSERT 語句里的列由 execute() 的參數決定。由調試信息可見具體的 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" }, ])
調試信息里具體的 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)
第一部分到此結束。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/45414.html
摘要:下一篇文章第節查詢條件設置是編程語言下的一款開源軟件。提供了工具包及對象關系映射工具,使用許可證發行。在關閉連接時會自動進行事務提交操作。引入多條件查詢時使用。由于上下文函數退出時會自動提交事務,所以無需顯示的調用使新增生效。 下一篇文章:Python-SQLAlchemy:第2節:查詢條件設置 SQLAlchemy是Python編程語言下的一款開源軟件。提供了SQL工具包及對象關系...
摘要:參數表示是否檢查表已經存在。此外,這條語句列出了表里的每一列,而在插入時一般是不需要指定的,可以通過方法加以限制限制后,列已經沒有了可見方法限制了語句所包含的列。 環境:Ubuntu 15.10 64-bit SQLAlchemy 是 Python 的 ORM 框架,它的理念是:數據庫的量級和性能重要于對象集合,而對象集合的抽象又重要于表和行。 安裝 直接通過 pip 安裝: $ pi...
摘要:的模型使用模型的原因當項目越來越大的時候會出現很多問題原生較多重復使用率低如果你的數據庫發生了改變所有的原生就都要進行修改寫原生的時候會有安全隱患中文件關系對象的映射使用去操作數據庫的時候不會再去寫原生的了通過把表映射成類字段為你的屬性在 Flask-SQLalchemy flask的ORM模型 使用ORM模型的原因 當項目越來越大的時候 會出現很多問題 原生SQL較多 重復使...
閱讀 1811·2021-10-09 09:44
閱讀 3383·2021-09-28 09:35
閱讀 1372·2021-09-01 10:31
閱讀 1658·2019-08-30 15:55
閱讀 2697·2019-08-30 15:54
閱讀 923·2019-08-29 17:07
閱讀 1372·2019-08-29 15:04
閱讀 2001·2019-08-26 13:56