簡(jiǎn)介
創(chuàng)新互聯(lián)長(zhǎng)期為成百上千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為亞?wèn)|企業(yè)提供專(zhuān)業(yè)的做網(wǎng)站、網(wǎng)站制作,亞?wèn)|網(wǎng)站改版等技術(shù)服務(wù)。擁有十載豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
在任何應(yīng)用中,都需要持久化存儲(chǔ),一般有3種基礎(chǔ)的存儲(chǔ)機(jī)制:文件、數(shù)據(jù)庫(kù)系統(tǒng)以及一些混合類(lèi)型。這種混合類(lèi)型包括現(xiàn)有系統(tǒng)上的API、ORM、文件管理器、電子表格、配置文件等。在了解數(shù)據(jù)庫(kù)以及如何在Python中使用他們之前,首先需要知道數(shù)據(jù)庫(kù)概念以及SQL語(yǔ)句。
底層存儲(chǔ)
數(shù)據(jù)庫(kù)通常使用文件系統(tǒng)作為基本的持久化存儲(chǔ),它可以是普通的操作系統(tǒng)文件、專(zhuān)用的操作系統(tǒng)文件,甚至是原始的磁盤(pán)分區(qū)。
用戶接口
大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)提供了命令行工具,可以使用其執(zhí)行SQL語(yǔ)句或查詢。此外還有一些GUI工具,使用命令行客戶端或數(shù)據(jù)庫(kù)客戶端庫(kù),向用戶提供便捷的界面。
數(shù)據(jù)庫(kù)
一個(gè)關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)(RDBMS)通??梢怨芾矶鄠€(gè)數(shù)據(jù)庫(kù),比如銷(xiāo)售、市場(chǎng)、用戶支持等,都可以在同一個(gè)服務(wù)端。
組件
數(shù)據(jù)庫(kù)存儲(chǔ)可以抽象為一張表。每行數(shù)據(jù)都有一些字段對(duì)應(yīng)于數(shù)據(jù)庫(kù)的列。每一行的表定義的集合以及每個(gè)表的數(shù)據(jù)類(lèi)型放到一起定義了數(shù)據(jù)庫(kù)的模式(schema)。數(shù)據(jù)庫(kù)可以創(chuàng)建(create)和刪除(drop),表也一樣。往數(shù)據(jù)庫(kù)里添加新行叫做插入(insert),修改表中已存在的行叫做更新(update),而移除表中已存在的行叫做刪除(delete)、這些動(dòng)作通常稱(chēng)為數(shù)據(jù)庫(kù)命令或操作。使用可選條件請(qǐng)求獲取數(shù)據(jù)庫(kù)中的行稱(chēng)為查詢(query)。
SQL
數(shù)據(jù)庫(kù)命令和查詢操作是通過(guò)SQL語(yǔ)句提交給數(shù)據(jù)庫(kù)的。雖然并非所有數(shù)據(jù)庫(kù)都是用SQL語(yǔ)句,但是大多數(shù)關(guān)系數(shù)據(jù)庫(kù)使用。下面是一些SQL命令示例,大部分?jǐn)?shù)據(jù)庫(kù)不區(qū)分大小寫(xiě),但是對(duì)數(shù)據(jù)庫(kù)關(guān)鍵字使用大寫(xiě)字母是最為廣泛接受的風(fēng)格。大多數(shù)命令需要結(jié)尾的分號(hào)(;)來(lái)結(jié)束這條語(yǔ)句。
創(chuàng)建數(shù)據(jù)庫(kù)
MySQL> CREATE DATABASE test; Query OK, 1 row affected (0.00 sec)
使用數(shù)據(jù)庫(kù)與刪除數(shù)據(jù)庫(kù)
mysql> USE test; Database changed mysql> DROP DATABASE test; Query OK, 0 rows affected (0.00 sec)
創(chuàng)建表
mysql> CREATE TABLE users (login VARCHAR(8),userid INT,projid INT); Query OK, 0 rows affected (0.02 sec)
插入行
mysql> INSERT INTO users VALUES('lena',211,1); Query OK, 1 row affected (0.00 sec)
更新行
mysql> UPDATE users SET userid=311 WHERE projid=1; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0
刪除行
mysql> DELETE FROM users WHERE projid=1; Query OK, 1 row affected (0.00 sec)
刪除表并清空數(shù)據(jù)
mysql> DROP TABLE users; Query OK, 0 rows affected (0.00 sec)
在Python中數(shù)據(jù)庫(kù)是通過(guò)適配器的方式進(jìn)行訪問(wèn)。適配器是一個(gè)Python模塊,使用它可以與關(guān)系型數(shù)據(jù)庫(kù)的客戶端接口相連。如圖所示為編寫(xiě)Python數(shù)據(jù)庫(kù)應(yīng)用的結(jié)構(gòu),包括使用和沒(méi)有使用ORM的情況。從圖中可以看出DB-API是連接到數(shù)據(jù)庫(kù)客戶端的C語(yǔ)言的接口。
Python的DB-API
DB-API是闡明一系列所需對(duì)象和數(shù)據(jù)庫(kù)訪問(wèn)機(jī)制的標(biāo)準(zhǔn),它可以為不同的數(shù)據(jù)庫(kù)適配器和底層數(shù)據(jù)庫(kù)提供一致性的訪問(wèn)。DB-API標(biāo)準(zhǔn)要求必須提供下表的功能和屬性。
屬性 | 描述 |
apilevel | 需要適配器兼容的DB-API版本 |
threadsafety | 本模塊的線程安全級(jí)別 |
paramstyle | 本模塊的SQL語(yǔ)句參數(shù)風(fēng)格 |
connect() | Connect()函數(shù) |
(多種異常) | 異常 |
數(shù)據(jù)屬性
apilevel,該字符串致命模塊需要兼容的DB-API最高版本
threadsafety,指明模塊線程的安全級(jí)別
0:不支持線程安全,線程間不能共享模塊。
1:最小化線程安全支持,線程間可以共享模塊,但不能共享連接。
2:適度的線程安全支持,線程間可以共享模塊和連接,但不能共享游標(biāo)。
3:完全的線程安全支持,線程可以共享模塊,連接和游標(biāo)。
參數(shù)風(fēng)格
DB-API支持以不同的方式指明如何將參數(shù)與SQL語(yǔ)句進(jìn)行整合,并最終傳遞給服務(wù)器中執(zhí)行。該參數(shù)是一個(gè)字符,用于指定構(gòu)建查詢行或命令時(shí)使用的字符串替代形式。
參數(shù)風(fēng)格 | 描述 | 示例 |
numeric | 數(shù)值位置風(fēng)格 | WHERE name=:1 |
named | 命名風(fēng)格 | WHERE name=:name |
pyformat | Python字典printf()格式轉(zhuǎn)換 | WHERE name=%(name)s |
qmark | 問(wèn)號(hào)風(fēng)格 | WHERE name=? |
format | ANSIC的printf()格式轉(zhuǎn)換 | WHERE name=%s |
函數(shù)屬性
connect()函數(shù)通過(guò)Connection對(duì)象訪問(wèn)數(shù)據(jù)庫(kù)。兼容模塊繼續(xù)實(shí)現(xiàn)connect()函數(shù),該函數(shù)創(chuàng)建并返回一個(gè)Connection對(duì)象。connect()函數(shù)可以使用包含多個(gè)參數(shù)的字符串來(lái)傳遞數(shù)據(jù)庫(kù)連接信息,也可以按照位置傳遞每個(gè)參數(shù),或者時(shí)使用關(guān)鍵字參數(shù)的形式傳遞。
connect(host ='localhost', user = 'root', passwd ='123456',db='movie',charset='utf8')
參數(shù) | 描述 |
host | 主機(jī)名 |
user | 用戶名 |
passwd | 密碼 |
db | 數(shù)據(jù)庫(kù)名 |
charset | 字符集 |
異常
異常 | 描述 |
Warning | 警告異?;?lèi) |
Error | 錯(cuò)誤異常基類(lèi) |
InterfaceError | 數(shù)據(jù)接口錯(cuò)誤 |
DatabaseError | 數(shù)據(jù)庫(kù)錯(cuò)誤 |
DataError | 處理數(shù)據(jù)時(shí)出現(xiàn)錯(cuò)誤 |
OperationError | 數(shù)據(jù)庫(kù)操作執(zhí)行期間出現(xiàn)錯(cuò)誤 |
IntegrityError | 數(shù)據(jù)庫(kù)關(guān)系完整性錯(cuò)誤 |
InternalError | 數(shù)據(jù)庫(kù)內(nèi)部錯(cuò)誤 |
ProgrammingError | SQL命令執(zhí)行失敗 |
NotSupportedError | 出現(xiàn)不支持的操作 |
Connection對(duì)象
應(yīng)用與數(shù)據(jù)之間進(jìn)行通信需要建立數(shù)據(jù)庫(kù)連接。它是最基本的機(jī)制,只有通過(guò)數(shù)據(jù)庫(kù)連接才能把命令傳遞到服務(wù)器,并得到返回的結(jié)果。當(dāng)一個(gè)連接建立后,可以創(chuàng)建一個(gè)游標(biāo),向數(shù)據(jù)庫(kù)發(fā)送請(qǐng)求,然后從數(shù)據(jù)庫(kù)中接收回應(yīng)。
Connection對(duì)象不需要包含任何數(shù)據(jù),不過(guò)應(yīng)當(dāng)定義下標(biāo)的幾個(gè)方法:
方法名 | 描述 |
close() | 關(guān)閉數(shù)據(jù)庫(kù)連接 |
commit() | 提交當(dāng)前事務(wù) |
rollback() | 取消當(dāng)前事務(wù) |
cursor() | 使用該鏈接創(chuàng)建一個(gè)游標(biāo)或類(lèi)游標(biāo)的對(duì)象 |
errorhandler(cxn,sur,errcls,errval) | 作為給定連接的游標(biāo)的處理程序 |
Cursor對(duì)象
當(dāng)建立連接后,就可以和數(shù)據(jù)庫(kù)進(jìn)行通信。游標(biāo)可以讓用戶提交數(shù)據(jù)庫(kù)命令,并且獲得查詢結(jié)果行。Python DB-API游標(biāo)對(duì)象總能提供游標(biāo)的功能,游標(biāo)對(duì)象最重要的屬性是execute()和fetch()方法,所有針對(duì)數(shù)據(jù)庫(kù)的服務(wù)請(qǐng)求都是通過(guò)它們執(zhí)行的。
對(duì)象屬性 | 描述 |
arraysize | 使用fetchmany()方法時(shí),一次取出的結(jié)果行數(shù),默認(rèn)1 |
connection | 創(chuàng)建次游標(biāo)的連接 |
description | 返回游標(biāo)活動(dòng)狀態(tài) |
lastrowid | 上次修改行的行ID |
rowcount | 上次execute()方法處理或影響的行數(shù) |
callproc(func[,args]) | 調(diào)用存儲(chǔ)過(guò)程 |
close() | 關(guān)閉游標(biāo) |
execute(op[,args]) | 執(zhí)行數(shù)據(jù)庫(kù)查詢或命令 |
executemany(op,args) | 類(lèi)似execute()和map()的結(jié)合,為給定的所有參數(shù)準(zhǔn)備并執(zhí)行數(shù)據(jù)庫(kù)查詢或命令 |
fetchone() | 獲取查詢結(jié)果的下一行 |
fetchmany([size=cursor,arraysize]) | 獲取查詢結(jié)果的下size行 |
fetchall() | 獲取查詢結(jié)果的剩余所有行 |
__iter__() | 為游標(biāo)創(chuàng)建迭代器對(duì)象 |
messages | 游標(biāo)執(zhí)行后從數(shù)據(jù)庫(kù)中獲得的消息列表 |
next() | 被迭代器用于獲取查詢結(jié)果的下一行 |
nextset() | 移動(dòng)到下一個(gè)結(jié)果集合 |
rownumber | 當(dāng)前結(jié)果集中游標(biāo)的索引 |
setinputsizes(sizes) | 設(shè)置允許的最大輸入大小 |
setoutputsize(size[,col]) | 設(shè)置獲取的最大緩沖區(qū)大小 |
ORM與SQLAlchemy
ORM(Object-Relational Mapping,對(duì)象關(guān)系映射)的作用實(shí)在關(guān)系型數(shù)據(jù)庫(kù)和業(yè)務(wù)實(shí)體對(duì)象之間做一個(gè)映射,這樣開(kāi)發(fā)者在操作數(shù)據(jù)庫(kù)的數(shù)據(jù)時(shí),就不需要再去和復(fù)雜的SQL語(yǔ)句打交道,只需要簡(jiǎn)單的操作對(duì)象的屬性和方法。所有ORM必須具備3個(gè)方面的基本能力:映射技術(shù)、CURD操作和緩存技術(shù)。
ORM在卡發(fā)者和數(shù)據(jù)庫(kù)之間建立了中間層,把數(shù)據(jù)庫(kù)中的數(shù)據(jù)轉(zhuǎn)換成了Python中的對(duì)象實(shí)體,這樣即屏蔽不同數(shù)據(jù)庫(kù)之間的差異性,又使開(kāi)發(fā)者可以非常方便的操作數(shù)據(jù)庫(kù)中的數(shù)據(jù)。當(dāng)前SQLAlchemy是Python中最成熟的ORM框架,資源和文檔豐富。大多數(shù)Python Web框架對(duì)其都有很好的支持。
Dialect用于和數(shù)據(jù)API進(jìn)行連接,根據(jù)配置文件的不同調(diào)用不同的數(shù)據(jù)庫(kù)API,從而實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的操作:
MySQL-Python mysql+mysqldb:// pymysql mysql+pymysql:// MySQL-Connector mysql+mysqlconnector:// cx_Oracle oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...] |
連接數(shù)據(jù)庫(kù):
In [1]: from sqlalchemy import create_engine In [2]: engine = create_engine('mysql+mysqlconnector://root@127.0.0.1:3306 ...: /test',echo=True)
創(chuàng)建表:
In [3]: from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey,Seque ...:nce In [4]: metadata = MetaData() In [5]: users = Table('users', metadata, ...: Column('id', Integer, Sequence('user_id_seq'), primary_key=True), ...: Column('name', String(50)), ...: Column('fullname', String(50)), ...: Column('password', String(12)) ...: ) In [6]: addresses = Table('addresses', metadata, ...: Column('id', Integer, primary_key=True), ...: Column('user_id', None, ForeignKey('users.id')), ...: Column('email_address', String(50), nullable=False) ...: ) In [7]: metadata.create_all(engine) 2017-05-19 17:59:46,958 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode' 2017-05-19 17:59:46,959 INFO sqlalchemy.engine.base.Engine {} 2017-05-19 17:59:46,960 INFO sqlalchemy.engine.base.Engine SELECT DATABASE() 2017-05-19 17:59:46,960 INFO sqlalchemy.engine.base.Engine {} 2017-05-19 17:59:46,962 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1 2017-05-19 17:59:46,962 INFO sqlalchemy.engine.base.Engine {} 2017-05-19 17:59:46,963 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1 2017-05-19 17:59:46,963 INFO sqlalchemy.engine.base.Engine {} 2017-05-19 17:59:46,964 INFO sqlalchemy.engine.base.Engine DESCRIBE `users` 2017-05-19 17:59:46,964 INFO sqlalchemy.engine.base.Engine {} 2017-05-19 17:59:46,965 INFO sqlalchemy.engine.base.Engine DESCRIBE `addresses` 2017-05-19 17:59:46,965 INFO sqlalchemy.engine.base.Engine {} 2017-05-19 17:59:46,966 INFO sqlalchemy.engine.base.Engine ROLLBACK 2017-05-19 17:59:46,967 INFO sqlalchemy.engine.base.Engine CREATE TABLE addresses ( id INTEGER NOT NULL AUTO_INCREMENT, user_id INTEGER, email_address VARCHAR(50) NOT NULL, PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES users (id) ) 2017-05-19 17:59:46,967 INFO sqlalchemy.engine.base.Engine {} 2017-05-19 17:59:46,994 INFO sqlalchemy.engine.base.Engine COMMIT
插入數(shù)據(jù):
In [8]: ins = users.insert() In [9]: str(ins) Out[9]: 'INSERT INTO users (id, name, fullname, password) VALUES (:id, :name, :fullname, :password)' In [10]: ins = users.insert().values(id=1,name='jack', fullname='Jack Jones') In [11]: ins.compile().params Out[11]: {'fullname': 'Jack Jones', 'id': 1, 'name': 'jack'} In [12]: conn = engine.connect() In [13]: result = conn.execute(ins) 2017-05-19 18:04:29,982 INFO sqlalchemy.engine.base.Engine INSERT INTO users (id, name, fullname) VALUES (%(id)s, %(name)s, %(fullname)s) 2017-05-19 18:04:29,982 INFO sqlalchemy.engine.base.Engine {'id': 1, 'name': 'jack', 'fullname': 'Jack Jones'} 2017-05-19 18:04:29,982 INFO sqlalchemy.engine.base.Engine COMMIT In [16]: conn.execute(addresses.insert(), [ #多條語(yǔ)句插入 ...: ... {'user_id': 1, 'email_address' : 'jack@yahoo.com'}, ...: ... {'user_id': 1, 'email_address' : 'jack@msn.com'}, ...: ... {'user_id': 2, 'email_address' : 'www@www.org'}, ...: ... {'user_id': 2, 'email_address' : 'wendy@aol.com'}, ...: ... ]) 2017-05-19 18:07:29,203 INFO sqlalchemy.engine.base.Engine INSERT INTO addresses (user_id, email_address) VALUES (%(user_id)s, %(email_address)s) 2017-05-19 18:07:29,203 INFO sqlalchemy.engine.base.Engine ({'user_id': 1, 'email_address': 'jack@yahoo.com'}, {'user_id': 1, 'email_address': 'jack@msn.com'}, {'user_id': 2, 'email_address': 'www@www.org'}, {'user_id': 2, 'email_address': 'wendy@aol.com'}) 2017-05-19 18:07:29,204 INFO sqlalchemy.engine.base.Engine COMMIT Out[16]:
查詢
In [17]: from sqlalchemy.sql import select In [18]: s = select([users]) In [19]: result = conn.execute(s) 2017-05-19 18:08:59,639 INFO sqlalchemy.engine.base.Engine SELECT users.id, users.name, users.fullname, users.password FROM users 2017-05-19 18:08:59,639 INFO sqlalchemy.engine.base.Engine {} In [20]: for row in result: ...: print(row) ...: (1, 'jack', 'Jack Jones', None) In [22]: for row in conn.execute(select([users, addresses])): #多條查詢 ...: print(row) ...: 2017-05-19 18:11:41,681 INFO sqlalchemy.engine.base.Engine SELECT users.id, users.name, users.fullname, users.password, addresses.id, addresses.user_id, addresses.email_address FROM users, addresses 2017-05-19 18:11:41,681 INFO sqlalchemy.engine.base.Engine {} (1, 'jack', 'Jack Jones', None, 1, 1, 'jack@yahoo.com') (1, 'jack', 'Jack Jones', None, 2, 1, 'jack@msn.com') (1, 'jack', 'Jack Jones', None, 3, 2, 'www@www.org') (1, 'jack', 'Jack Jones', None, 4, 2, 'wendy@aol.com')
更新
In [27]: stmt = users.update().values(fullname="Fullname: " + users.c.name) In [28]: conn.execute(stmt) 2017-05-19 18:27:33,489 INFO sqlalchemy.engine.base.Engine UPDATE users SET fullname=(concat(%(name_1)s, users.name)) 2017-05-19 18:27:33,489 INFO sqlalchemy.engine.base.Engine {'name_1': 'Fullname: '} 2017-05-19 18:27:33,490 INFO sqlalchemy.engine.base.Engine COMMIT Out[28]:
刪除
In [31]: conn.execute(addresses.delete()) 2017-05-19 18:30:02,296 INFO sqlalchemy.engine.base.Engine DELETE FROM addresses 2017-05-19 18:30:02,296 INFO sqlalchemy.engine.base.Engine {} 2017-05-19 18:30:02,297 INFO sqlalchemy.engine.base.Engine COMMIT Out[31]:In [32]: conn.execute(users.delete().where(users.c.name > 'm')) 2017-05-19 18:30:12,159 INFO sqlalchemy.engine.base.Engine DELETE FROM users WHERE users.name > %(name_1)s 2017-05-19 18:30:12,159 INFO sqlalchemy.engine.base.Engine {'name_1': 'm'} 2017-05-19 18:30:12,159 INFO sqlalchemy.engine.base.Engine COMMIT Out[32]: