时间:2021-07-01 10:21:17 帮助过:2人阅读

图 21-1 数据库和应用程序之间的多层通讯.
第一个盒子一般是 C/C++ 程序, 你的程序通过 DB-API 兼容接口程序访问数据库.ORM 通过程序处理数据库细节来简化数据库开发.
21.2 Python 数据库应用程序
DB-API 是一个规范. 它定义了一系列必须的对象和数据库存取方式, 以便为各种各样的底层数 据库系统和多种多样的数据库接口程序提供一致的访问接口.
模块属性
DB-API 规范里的以下特性和属性必须提供.

数据属性
apilevel
apilevel 这个字符串(不是浮点数)表示这个 DB-API 模块所兼容的DB-API 最高版本号. 如"1.0", "2.0", 如果未定义, 则默认是 "1.0";
threadsafety
threadsafety 这是一个整数, 取值范围如下
?? 0:不支持线程安全, 多个线程不能共享此模块
?? 1:初级线程安全支持: 线程可以共享模块, 但不能共享连接
?? 2:中级线程安全支持 线程可以共享模块和连接, 但不能共享游标
?? 3:完全线程安全支持 线程可以共享模块, 连接及游标.
函数属性
connect 方法
connect 方法生成一个connect 对象, 我们通过这个对象来访问数据库. 符合标准的模块都 会实现 connect 方法.
数据库连接参数可以以一个 DSN 字符串的形式提供, 也可以以多个位置相关参数的形式提供 (如果你明确知道参数的顺序的话), 也可以以关键字参数的形式提供.

注意不是所有的接口程序都是严格按照规范实现的. MySQLdb 就使用了 db 参 数而不是规范推荐的 database 参数来表示要访问的数据库.

异常
兼容标准的模块也应该提供这些异常类.


连接对象
要与数据库进行通信, 必须先和数据库建立连接. 连接对象处理命令如何送往服务器, 以及如 何从服务器接收数据等基础功能.
连接成功(或一个连接池)后你就能够向数据库服务器发送请求, 得到响应.
方法
表21.5 连接对象没有必须定义的数据属性, 但是它至少应该定义这些方法.
一旦执行了 close() 方法, 再试图使用连接对象的方法将会导致异常.
对不支持事务的数据库或者虽然支持事务, 但设置了自动提交(auto-commit)的数据库系统来
说, commit()方法什么也不做. 如果你确实需要, 可以实现一个自定义方法来关闭自动提交行为.
由于 DB-API 要求必须实现此方法, 对那些没有事务概念的数据库来说, 这个方法只需要有一条
pass 语句就可以了.
类似 commit(), rollback() 方法仅对支持事务的数据库有意义. 执行完rollback(), 数据库
将恢复到提交事务前的状态. 根据 PEP249, 在提交commit()之前关闭数据库连接将会自动调用
rollback()方法.
对不支持游标的数据库来说, cursor()方法仍然会返回一个尽量模仿游标对象的对象. 这些是
最低要求. 特定数据库接口程序的开发者可以任意为他们的接口程序添加额外的属性, 只要他们愿 意.
DB-API 规范建议但不强制接口程序的开发者为所有数据库接口模块编写异常类. 如果没有提
供异常类, 则假定该连接对象会引发一致的模块级异常. 一旦你完成了数据库连接, 并且关闭了
游标对象, 你应该执行 commit() 提交你的操作, 然后关闭这个连接.
游标对象
一个游标允许用户 执行数据库命令和得到查询结果.
一个 Python DB-API 游标对象总是扮演游标的角色, 无论数据库是否真正支持游标.
数据库接口程序必须实现游标对象,才能保证无论使用何种后端数据库你的代码都不需要做任何改变.
游标对象最重要的属性是 execute*() 和 fetch*() 方法. 所有对数据库服务器的请求都由它
们来完成.对fetchmany()方法来说, 设置一个合理的arraysize 属性会很有用. 当然, 在不需要时
关掉游标对象也是个好主意. 如果你的数据库支持存储过程, 你就可以使用callproc() 方法.

类型对象和构造器
通常两个不同系统的接口要求的参数类型是不一致的,譬如python 调用c 函数时Python 对象和 C 类型之间就需要数据格式的转换,
对于 Python DB-API 的开发者来说, 你传递给数据库的参数是字符串形式的, 但数据库会根 据需要将它转换为多种不同的形式. 以确保每次查询能被正确执行.
一个 Python 字符串可能被转换为一个 VARCHAR, 或一个TEXT, 或一个BLOB, 或一
个原生 BINARY 对象, 或一个DATE 或TIME 对象. 一个字符串到底会被转换成什么类型? 必须小心
的尽可能以数据库期望的数据类型来提供输入, 因此另一个DB-API 的需求是创建一个构造器以生成
特殊的对象, 以便能够方便的将Python 对象转换为合适的数据库对象.
SQL 的 NULL 值被映射为 Pyhton 的 NULL 对象, 也就是 None.

关系数据库

http://python.org/topics/database/modules.html
数据库和Python:接口程序
1 import sqlite3 2 from os import makedirs, removedirs, unlink 3 from os.path import dirname, exists 4 5 DB_FILE = ‘sqlite_test/test.db‘ 6 DB_DIR = dirname(DB_FILE) 7 8 if not exists(DB_FILE): 9 if not exists(DB_DIR): 10 makedirs(DB_DIR) 11 dbfile = file(DB_FILE, ‘wb‘) 12 dbfile.close() 13 14 cxn = sqlite3.connect(DB_FILE) 15 cur = cxn.cursor() 16 cur.execute(‘CREATE TABLE users(login VARCHAR(8), uid INTEGER)‘) 17 cur.execute(‘INSERT INTO users VALUES("john", 100)‘) 18 cur.execute(‘INSERT INTO users VALUES("jane", 101)‘) 19 20 cur.execute(‘SELECT login, uid FROM users‘) 21 for eachUser in cur.fetchall(): 22 print eachUser 23 24 cur.execute(‘DROP TABLE users‘) 25 cur.close() 26 cxn.commit() 27 cxn.close() 28 unlink(DB_FILE) 29 removedirs(DB_DIR)sqlite3test.py
1 #!/usr/bin/env python 2 3 import os 4 from random import randrange as rrange 5 6 COLSIZ = 10 7 RDBMSs = {‘s‘: ‘sqlite‘, ‘m‘: ‘mysql‘, ‘g‘:‘gadfly‘} 8 DB_EXC = None 9 10 def setup(): 11 return RDBMSs[raw_input(‘‘‘ 12 Choose a database system: 13 14 (M)ySQL 15 (G)adfly 16 (S)qlite 17 18 Enter choice:‘‘‘).strip().lower()[0]] 19 20 def connect(db, dbName): 21 global DB_EXC 22 dbDir = ‘%s_%s‘ % (db, dbName) 23 24 if db == ‘sqlite‘: 25 try: 26 import sqlite3 27 except ImportError, e: 28 try: 29 from pysqlite2 import dbapi2 as sqlite3 30 except ImportError, e: 31 return None 32 33 DB_EXC = sqlite3 34 if not os.path.isdir(dbDir): 35 os.mkdir(dbDir) 36 cxn = sqlite3.connect(os.path.join(dbDir, dbName)) 37 38 elif db == ‘mysql‘: 39 try: 40 import MySQLdb 41 import _mysql_exceptions as DB_EXC 42 except ImportError, e: 43 return None 44 45 try: 46 cxn = MySQLDb.connect(db=dbName) 47 except _mysql_exceptions.OperationalError, e: 48 cxn = MySQLdb.connect(user=‘root‘) 49 try: 50 cxn.query(‘DROP DATABASE %s‘ % dbName) 51 except DB_EXC.OperationalError, e: 52 pass 53 cxn.query(‘CREATE DATABASE %s‘ % dbName) 54 cxn.query("GRANT ALL ON %s.* to ‘‘@‘localhost‘" % dbName) 55 cxn.commit() 56 cxn.close() 57 cxn = MySQLdb.connect(db=dbName) 58 59 elif db == ‘gadfly‘: 60 try: 61 from gadfly import gadfly 62 DB_EXC = gadfly 63 except ImportError, e: 64 return None 65 66 try: 67 cxn = gadfly(dbName, dbDir) 68 except IOError, e: 69 cxn = gadfly() 70 if not os.path.isdir(dbDir): 71 os.mkdir(dbDir) 72 cxn.startup(dbName, dbDir) 73 else: 74 return None 75 return cxn 76 77 def create(cur): 78 try: 79 cur.execute(‘‘‘ 80 CREATE TABLE users ( 81 login VARCHAR(8), 82 uid INTEGER, 83 prid INTEGER) 84 ‘‘‘) 85 except DB_EXC.OperationalError, e: 86 drop(cur) 87 create(cur) 88 89 drop = lambda cur: cur.execute(‘DROP TABLE users‘) 90 91 NAMES = ( 92 (‘aaron‘, 8312), (‘angela‘, 7603), (‘dave‘, 7306), 93 (‘davina‘,7902), (‘elliot‘, 7911), (‘ernie‘, 7410), 94 (‘jess‘, 7912), (‘jim‘, 7512), (‘larry‘, 7311), 95 (‘leslie‘, 7808), (‘melissa‘, 8602), (‘pat‘, 7711), 96 (‘serena‘, 7003), (‘stan‘, 7607), (‘faye‘, 6812), 97 (‘amy‘, 7209), 98 ) 99 100 def randName(): 101 pick = list(NAMES) 102 while len(pick) > 0: 103 yield pick.pop(rrange(len(pick))) 104 105 def insert(cur, db): 106 if db == ‘sqlite‘: 107 cur.executemany("INSERT INTO users VALUES(?, ?, ?)", 108 [(who, uid, rrange(1, 5)) for who, uid in randName()]) 109 elif db == ‘gadfly‘: 110 for who, uid in randName: 111 cur.execute("INSERT INTO users VALUES(?, ?, ?)", 112 (who, uid, rrange(1,5))) 113 elif db == ‘mysql‘: 114 cur.executemay("INSERT INTO users VALUES(%s, %s, %s)", 115 [(who, uid, rrange(1,5)) for who, uid, in randName()]) 116 117 getRC = lambda cur: cur.rowcount if hasattr(cur, ‘rowcount‘) else -1 118 119 def update(cur): 120 fr = rrange(1,5) 121 to = rrange(1,5) 122 cur.execute( 123 "UPDATE users SET prid=%d WHERE prid=%d" % (to, fr)) 124 return fr, to, getRC(cur) 125 126 def delete(cur): 127 rm = rrange(1,5) 128 cur.execute(‘DELETE FROM users WHERE prid=%d‘ % rm) 129 return rm, getRC(cur) 130 131 def dbDump(cur): 132 cur.execute(‘SELECT login, uid, prid FROM users‘) 133 print ‘\n%s%s%s‘ % (‘LOGIN‘.ljust(COLSIZ), 134 ‘USERID‘.ljust(COLSIZ), ‘PROJ#‘.ljust(COLSIZ)) 135 for data in cur.fetchall(): 136 print ‘%s%s%s‘ % tuple([str(s).title().ljust(COLSIZ)137 for s in data]) 138 def main(): 139 db = setup() 140 print ‘*** Connecting to %r database‘ % db 141 cxn = connect(db, ‘test‘) 142 143 if not cxn: 144 print ‘ERROR: %r not supported, exiting‘ % db 145 return 146 cur = cxn.cursor() 147 148 print ‘\n*** Creating users table‘ 149 create(cur) 150 151 print ‘\n*** Inserting names into table‘ 152 insert(cur, db) 153 dbDump(cur) 154 155 print ‘\n*** Randomly moving folks‘, 156 fr, to, num = update(cur) 157 print ‘from on group (%d) to another (%d)‘ % (fr, to) 158 print ‘\t(%d users moved)‘ % num 159 dbDump(cur) 160 161 print ‘\n*** Randomly choosing group.‘, 162 rm, num = delete(cur) 163 print ‘(%d) to delete‘ % rm 164 print ‘\t(%d users removed)‘ % num 165 dbDump(cur) 166 167 print ‘\n*** Drop users table‘ 168 drop(cur) 169 cur.close() 170 cxn.commit() 171 cxn.close() 172 173 if __name__ == ‘__main__‘: 174 main()shuffle_db.py 21.3 对象-关系管理器(ORMs) 考虑对象,而不是SQL




21 数据库编程 - 《Python 核心编程》
标签: