Python數據庫編程
簡介
在任何應用中,都需要持久化存儲,一般有3種基礎的存儲機制:文件、數據庫系統以及一些混合類型。這種混合類型包括現有系統上的API、ORM、文件管理器、電子表格、配置文件等。在了解數據庫以及如何在Python中使用他們之前,首先需要知道數據庫概念以及SQL語句。
底層存儲
數據庫通常使用文件系統作為基本的持久化存儲,它可以是普通的操作系統文件、專用的操作系統文件,甚至是原始的磁盤分區。
用戶接口
大多數數據庫系統提供了命令行工具,可以使用其執行SQL語句或查詢。此外還有一些GUI工具,使用命令行客戶端或數據庫客戶端庫,向用戶提供便捷的界面。
數據庫
一個關系數據庫管理系統(RDBMS)通常可以管理多個數據庫,比如銷售、市場、用戶支持等,都可以在同一個服務端。
組件
數據庫存儲可以抽象為一張表。每行數據都有一些字段對應於數據庫的列。每一行的表定義的集合以及每個表的數據類型放到一起定義了數據庫的模式(schema)。數據庫可以創建(create)和刪除(drop),表也一樣。往數據庫裏添加新行叫做插入(insert),修改表中已存在的行叫做更新(update),而移除表中已存在的行叫做刪除(delete)、這些動作通常稱為數據庫命令或操作。使用可選條件請求獲取數據庫中的行稱為查詢(query)。
SQL
數據庫命令和查詢操作是通過SQL語句提交給數據庫的。雖然並非所有數據庫都是用SQL語句,但是大多數關系數據庫使用。下面是一些SQL命令示例,大部分數據庫不區分大小寫,但是對數據庫關鍵字使用大寫字母是最為廣泛接受的風格。大多數命令需要結尾的分號(;)來結束這條語句。
創建數據庫
mysql> CREATE DATABASE test; Query OK, 1 row affected (0.00 sec)
使用數據庫與刪除數據庫
mysql> USE test; Database changed mysql> DROP DATABASE test; Query OK, 0 rows affected (0.00 sec)
創建表
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)
刪除表並清空數據
mysql> DROP TABLE users; Query OK, 0 rows affected (0.00 sec)
在Python中數據庫是通過適配器的方式進行訪問。適配器是一個Python模塊,使用它可以與關系型數據庫的客戶端接口相連。如圖所示為編寫Python數據庫應用的結構,包括使用和沒有使用ORM的情況。從圖中可以看出DB-API是連接到數據庫客戶端的C語言的接口。
Python的DB-API
DB-API是闡明一系列所需對象和數據庫訪問機制的標準,它可以為不同的數據庫適配器和底層數據庫提供一致性的訪問。DB-API標準要求必須提供下表的功能和屬性。
屬性 | 描述 |
apilevel | 需要適配器兼容的DB-API版本 |
threadsafety | 本模塊的線程安全級別 |
paramstyle | 本模塊的SQL語句參數風格 |
connect() | Connect()函數 |
(多種異常) | 異常 |
數據屬性
apilevel,該字符串致命模塊需要兼容的DB-API最高版本
threadsafety,指明模塊線程的安全級別
0:不支持線程安全,線程間不能共享模塊。
1:最小化線程安全支持,線程間可以共享模塊,但不能共享連接。
2:適度的線程安全支持,線程間可以共享模塊和連接,但不能共享遊標。
3:完全的線程安全支持,線程可以共享模塊,連接和遊標。
參數風格
DB-API支持以不同的方式指明如何將參數與SQL語句進行整合,並最終傳遞給服務器中執行。該參數是一個字符,用於指定構建查詢行或命令時使用的字符串替代形式。
參數風格 | 描述 | 示例 |
numeric | 數值位置風格 | WHERE name=:1 |
named | 命名風格 | WHERE name=:name |
pyformat | Python字典printf()格式轉換 | WHERE name=%(name)s |
qmark | 問號風格 | WHERE name=? |
format | ANSIC的printf()格式轉換 | WHERE name=%s |
函數屬性
connect()函數通過Connection對象訪問數據庫。兼容模塊繼續實現connect()函數,該函數創建並返回一個Connection對象。connect()函數可以使用包含多個參數的字符串來傳遞數據庫連接信息,也可以按照位置傳遞每個參數,或者時使用關鍵字參數的形式傳遞。
connect(host =‘localhost‘, user = ‘root‘, passwd =‘123456‘,db=‘movie‘,charset=‘utf8‘)
參數 | 描述 |
host | 主機名 |
user | 用戶名 |
passwd | 密碼 |
db | 數據庫名 |
charset | 字符集 |
異常
異常 | 描述 |
Warning | 警告異常基類 |
Error | 錯誤異常基類 |
InterfaceError | 數據接口錯誤 |
DatabaseError | 數據庫錯誤 |
DataError | 處理數據時出現錯誤 |
OperationError | 數據庫操作執行期間出現錯誤 |
IntegrityError | 數據庫關系完整性錯誤 |
InternalError | 數據庫內部錯誤 |
ProgrammingError | SQL命令執行失敗 |
NotSupportedError | 出現不支持的操作 |
Connection對象
應用與數據之間進行通信需要建立數據庫連接。它是最基本的機制,只有通過數據庫連接才能把命令傳遞到服務器,並得到返回的結果。當一個連接建立後,可以創建一個遊標,向數據庫發送請求,然後從數據庫中接收回應。
Connection對象不需要包含任何數據,不過應當定義下標的幾個方法:
方法名 | 描述 |
close() | 關閉數據庫連接 |
commit() | 提交當前事務 |
rollback() | 取消當前事務 |
cursor() | 使用該鏈接創建一個遊標或類遊標的對象 |
errorhandler(cxn,sur,errcls,errval) | 作為給定連接的遊標的處理程序 |
Cursor對象
當建立連接後,就可以和數據庫進行通信。遊標可以讓用戶提交數據庫命令,並且獲得查詢結果行。Python DB-API遊標對象總能提供遊標的功能,遊標對象最重要的屬性是execute()和fetch()方法,所有針對數據庫的服務請求都是通過它們執行的。
對象屬性 | 描述 |
arraysize | 使用fetchmany()方法時,一次取出的結果行數,默認1 |
connection | 創建次遊標的連接 |
description | 返回遊標活動狀態 |
lastrowid | 上次修改行的行ID |
rowcount | 上次execute()方法處理或影響的行數 |
callproc(func[,args]) | 調用存儲過程 |
close() | 關閉遊標 |
execute(op[,args]) | 執行數據庫查詢或命令 |
executemany(op,args) | 類似execute()和map()的結合,為給定的所有參數準備並執行數據庫查詢或命令 |
fetchone() | 獲取查詢結果的下一行 |
fetchmany([size=cursor,arraysize]) | 獲取查詢結果的下size行 |
fetchall() | 獲取查詢結果的剩余所有行 |
__iter__() | 為遊標創建叠代器對象 |
messages | 遊標執行後從數據庫中獲得的消息列表 |
next() | 被叠代器用於獲取查詢結果的下一行 |
nextset() | 移動到下一個結果集合 |
rownumber | 當前結果集中遊標的索引 |
setinputsizes(sizes) | 設置允許的最大輸入大小 |
setoutputsize(size[,col]) | 設置獲取的最大緩沖區大小 |
ORM與SQLAlchemy
ORM(Object-Relational Mapping,對象關系映射)的作用實在關系型數據庫和業務實體對象之間做一個映射,這樣開發者在操作數據庫的數據時,就不需要再去和復雜的SQL語句打交道,只需要簡單的操作對象的屬性和方法。所有ORM必須具備3個方面的基本能力:映射技術、CURD操作和緩存技術。
ORM在卡發者和數據庫之間建立了中間層,把數據庫中的數據轉換成了Python中的對象實體,這樣即屏蔽不同數據庫之間的差異性,又使開發者可以非常方便的操作數據庫中的數據。當前SQLAlchemy是Python中最成熟的ORM框架,資源和文檔豐富。大多數Python Web框架對其都有很好的支持。
Dialect用於和數據API進行連接,根據配置文件的不同調用不同的數據庫API,從而實現對數據庫的操作:
MySQL-Python mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> pymysql mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>] MySQL-Connector mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname> cx_Oracle oracle+cx_oracle://user:[email protected]:port/dbname[?key=value&key=value...] |
連接數據庫:
In [1]: from sqlalchemy import create_engine In [2]: engine = create_engine(‘mysql+mysqlconnector:[email protected]:3306 ...: /test‘,echo=True)
創建表:
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
插入數據:
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(), [ #多條語句插入 ...: ... {‘user_id‘: 1, ‘email_address‘ : [email protected]}, ...: ... {‘user_id‘: 1, ‘email_address‘ : [email protected]}, ...: ... {‘user_id‘: 2, ‘email_address‘ : [email protected]}, ...: ... {‘user_id‘: 2, ‘email_address‘ : [email protected]}, ...: ... ]) 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‘: [email protected]}, {‘user_id‘: 1, ‘email_address‘: [email protected]}, {‘user_id‘: 2, ‘email_address‘: [email protected]}, {‘user_id‘: 2, ‘email_address‘: [email protected]}) 2017-05-19 18:07:29,204 INFO sqlalchemy.engine.base.Engine COMMIT Out[16]: <sqlalchemy.engine.result.ResultProxy at 0x7f3b8b4f2cf8>
查詢
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, [email protected]) (1, ‘jack‘, ‘Jack Jones‘, None, 2, 1, [email protected]) (1, ‘jack‘, ‘Jack Jones‘, None, 3, 2, [email protected]) (1, ‘jack‘, ‘Jack Jones‘, None, 4, 2, [email protected])
更新
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]: <sqlalchemy.engine.result.ResultProxy at 0x7f3b8b50ca58>
刪除
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]: <sqlalchemy.engine.result.ResultProxy at 0x7f3b8b4a3f28> 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]: <sqlalchemy.engine.result.ResultProxy at 0x7f3b8b50bb70>
本文出自 “隨風而飄” 博客,請務必保留此出處http://yinsuifeng.blog.51cto.com/10173491/1927669
Python數據庫編程