Python DB-API
许多人使用 Python 是因为,像其他脚本语言一样,它是一种可移植的、平台独立的通用语言,可以执行与数据库供应商提供的以数据库为中心的专有 4GL 工具相同的任务。像 4GL 工具一样,Python 让您能够轻松编写访问、显示和更新数据库中信息的程序。与许多 4GL 不同,Python 还为您提供了各种其他功能,例如解析 HTML、建立套接字连接和加密数据。
Python DB-API 的可能应用包括
许多网站动态构建页面以显示用户请求的信息,例如从目录中提供的产品或从图书馆中可用的文档中进行选择。这样做需要 CGI 脚本,这些脚本可以从数据库中提取所需信息并将其渲染为 HTML。
一个 Intranet 应用程序可以使用 Tkinter 模块和 DB-API 来为浏览本地数据库(例如应收账款或客户列表)提供图形用户界面。
Python 程序可用于通过计算数据的统计属性来分析数据。
Python 程序可以为修改数据库的程序形成测试框架,以验证是否维护了特定的完整性约束。
现在有很多商业和免费数据库可用,它们中的大多数都提供结构化查询语言 (SQL) 用于检索和添加信息(请参阅“资源”)。然而,尽管大多数数据库都通用 SQL,但执行 SQL 操作的细节各不相同。编写 Python 数据库模块的个人发明了自己的接口,并且由此产生的不同 Python 模块的激增导致了问题:它们没有两个是完全相同的,因此如果您决定切换到新的数据库产品,则必须重写所有检索和插入数据的代码。
为了解决这个问题,成立了一个数据库特别兴趣小组 (SIG)。对将 Python 用于给定应用程序感兴趣的人们会组成自己的 SIG:他们在 Internet 邮件列表上会面,讨论主题、交流想法、编写代码(并调试它),最终生成成品。(听起来很像 Linux 内核的开发过程,不是吗?)
经过一番讨论,数据库 SIG 制定了一个关系数据库的一致接口规范——DB-API。 感谢此规范,您只需学习一个接口。 将代码移植到不同的数据库产品要简单得多,通常只需要更改几行代码。
在数据库 SIG 之前编写的数据库模块仍然存在,并且与规范不符——mSQL 模块是最常用的模块。 这些模块最终将被重写以符合 DB-API; 这只是维护者找到时间去做的问题。
关系数据库由一个或多个表组成。 每个表分为列和行。 列包含相似类型的项目,例如客户 ID 或价格,而行包含单个数据项,每列都有一个值。 单行也称为元组或关系,这就是术语“关系数据库”的由来。
对于示例数据库,我们将使用一个小表,该表旨在跟踪一系列研讨会的参与者。(请参阅列表 1。)Seminars 表列出了正在提供的研讨会;示例行是(1,Python Programming,200,15)。每行包含一个唯一的标识 ID 号(在本例中为 1)、研讨会的标题(Python Programming)、其成本(200 美元)以及仍然空缺的位置数量(15)。Attendees 表列出了每位参与者的姓名、他或她希望参加的研讨会以及费用是否已支付。如果有人想参加多个研讨会,则会有多行带有该人的姓名,每行都有不同的研讨会编号和付款状态。
下面的示例使用 soliddb 模块,该模块支持从 Python 访问 SOLID 数据库。SOLID 是 Solidtech 的产品,Bradley Willson 在 1997 年 9 月的 LJ 中对其进行了评测。我不想介绍 CGI 或 Tkinter 编程,因此此处仅介绍访问数据库的命令,方式与直接在 Python 解释器中键入的方式相同。
首先,程序必须首先导入适当的 Python 模块,以连接到正在使用的数据库产品。 按照惯例,所有符合 Python DB-API 的数据库模块的名称都以“db”结尾,例如 soliddb 和 oracledb。
下一步是创建一个表示数据库连接的对象。 该对象的名称与模块相同。 打开连接所需的信息及其格式因数据库而异。 通常,它包括用户名和密码,以及如何查找数据库服务器的一些指示,例如 TCP/IP 主机名。 如果您使用的是 SOLID 的免费试用版,则 UNIX 管道是连接到服务器的唯一可用方法,因此代码是
>>> import soliddb >>> db = soliddb.soliddb('UPipe SOLID', 'amk', 'mypassword') >>> db <Solid object at 809bf10>
接下来,您应该创建一个游标对象。 游标对象充当给定 SQL 查询的句柄; 它允许检索结果的一行或多行,直到处理完所有匹配的行。 对于一次不需要多个查询的简单应用程序,没有必要使用游标对象,因为数据库对象支持与游标对象相同的所有方法。 在下面的示例中,我们将特意使用游标对象。(有关 SQL 入门的更多信息,请参阅 Reuven Lerner 在 1997 年 10 月的 LJ 中撰写的 At the Forge。)
游标对象提供一个 execute() 语句,该语句接受一个包含要执行的 SQL 语句的字符串。 这反过来会导致数据库服务器创建一组与查询匹配的行。
结果通过调用名称以 fetch 开头的方法来检索,如果不再有要检索的行,则返回一个或多个匹配的行或“None”。 fetchone() 方法始终返回单行,而 fetchmany() 返回少量行,fetchall() 返回所有匹配的行。
例如,要列出所有正在提供的研讨会,请执行以下操作
>>> cursor = db.cursor() >>> # List all the seminars >>> cursor.execute('select * from Seminars') >>> cursor.fetchall( [(4, 'Web Commerce', 300.0, 26), (1, 'Python Programming', 200.0, 15), (3, 'Socket Programming', 475.0, 7), (2, 'Intro to Linux', 100.0, 32), ]
行表示为元组,因此返回的第一行是
(4, 'Web Commerce', 300.0, 26)请注意,行不是按排序顺序返回的;为此,查询必须略有不同(只需添加 order by ID)。 因为它们返回多行,所以 fetchmany() 和 fetchall() 方法返回元组列表。 也可以使用 fetchone() 方法手动迭代结果,并循环直到它返回“None”,如本例所示,该示例列出了研讨会 1 的所有参与者
>>> cursor.execute ( 'select * from Attendees where seminar=1') >>> while (1): ... attendee = cursor.fetchone() ... if attendee == None: break ... print attendee ... ('Albert', 1, 'no') ('Beth', 1, 'yes') ('Elaine', 1, 'yes')SQL 还允许您编写对多个表进行操作的查询,如以下查询所示,该查询列出了 Albert 将要参加的研讨会
>>> cursor.execute("""select Seminars.title ... from Seminars, Attendees ... where Attendees.name = 'Albert' ... and Seminars.ID = Attendees.seminar""") >>≫ cursor.fetchall() [('Python Programming',), ('Web Commerce',)]既然我们可以从数据库中获取信息,现在是时候开始通过添加新信息来修改它了。 更改是通过使用 SQL insert 和 update 语句进行的。 与查询一样,SQL 语句被传递给游标对象的 execute() 方法。
在展示如何添加信息之前,需要注意一个微妙之处,即当任务需要多个不同的 SQL 命令才能完成时,会发生这种情况。 考虑向给定的研讨会添加参与者。 这需要两个步骤。 在一个步骤中,必须将一行添加到 Attendees 表中,给出此人的姓名、他们将参加的研讨会的 ID 以及他们是否已付款。 在另一步骤中,此研讨会的 places_left 值应减一,因为少了一个人的空间。 SQL 无法组合两个命令,因此这需要两次 execute() 调用。 但是,如果发生某些情况并且第二个命令未执行怎么办?也许是因为计算机崩溃、网络中断或 Python 程序中存在拼写错误? 数据库现在不一致:已添加参与者,但该研讨会的 places_left 列现在是错误的。
大多数数据库都提供事务作为解决此问题的方法。 事务是一组命令:要么全部执行,要么都不执行。 程序可以发出多个 SQL 命令作为事务的一部分,然后 提交 它们(即,告诉数据库同时应用所有这些更改)。 或者,程序可以确定出现问题并回滚事务而不进行更改。
对于支持事务的数据库,Python 接口在创建游标时会默默地启动一个事务。 commit() 方法提交使用该游标所做的更新,rollback() 方法放弃它们。 然后,每个方法都会启动一个新的事务。 某些数据库没有事务,而是简单地在执行时应用所有更改。 在这些数据库上,commit() 不执行任何操作,但您仍然应该调用它,以便与那些确实支持事务的数据库兼容。
列表 2 是一个 Python 函数,它试图通过在执行完两个操作后提交事务来正确完成所有这些操作。 调用此函数很简单
addAttendee('George', 4, 'yes')
我们可以通过检查研讨会 #4 的列表并列出其参与者来验证是否执行了更改。 这会产生以下输出
Seminars: 4 'Web Commerce' 300.0 25 Attendees: Albert 4 no Dale 4 yes Felix 4 no George 4 yes请注意,如果多个进程或线程尝试同时执行此函数,则此函数仍然存在错误。 数据库编程可能非常复杂。
有了这个标准化的接口,编写各种 Python 程序作为数据库的易于使用的前端并不困难。
Andrew Kuchling 在华盛顿特区的 Magnet Interactive 担任网站开发人员。他过去的项目之一是一个相当大的商业网站,该网站是使用 Python 在 Illustra 数据库之上实现的。可以通过电子邮件 akuchling@acm.org 与他联系。