科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道应用软件使用 SQLObject 连接数据库与 Python

使用 SQLObject 连接数据库与 Python

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文展示了 SQLObject 如何与数据库交互,如何使用 SQLObject 编写数据库访问和数据验证代码,以及如何将它用于遗留或现有数据库。

作者: Leonard Richardson 来源:www-128.ibm.com  2007年9月15日

关键字: SQLObject 数据库 python 软件

  • 评论
  • 分享微博
  • 分享邮件

通过提供用于操作数据库表的类和对象,对象关系映射工具有助于提高生产率。Python 最好的对象关系映射工具是 SQLObject —— 一个开放源码项目,它几乎完成编程数据库所需的所有操作。本文介绍 SQLObject 及其功能。阅读本文后,您将能够不编写任何 SQL 代码而连接 Python 与数据库。

当面向对象编程范例满足大多数数据库的关系范例时,通常会看到对象关系映射。对象关系映射是这两个世界的桥梁。它允许您定义与数据库表对应的类。然后您可以使用这些类及其实例上的方法来与数据库交互,而不用编写 SQL。使用对象关系映射并不意味着不需要知道关系数据库如何工作,而是不必要编写 SQL,从而避免编程错误。

您可以找到一打以上的操作 SQL 数据库的开放源码 Python 包,这还没包括用于连接 Python 与特定数据库的特殊用途模块。SQLObject 是其中最好的模块。它是简单易用的完全对象关系映射包。SQLObject 几乎可以完成编程数据库所需的所有操作。

本文展示了 SQLObject 如何与数据库交互,如何使用 SQLObject 编写数据库访问和数据验证代码,以及如何将它用于遗留或现有数据库。这里假设您已经具备 Python 和关系数据库的知识。

安装和设置 SQLObject

SQLObject 具有一个 setup.py 文件,安装方式与其他任何 Python 包一样。如果您使用的是 Python V2.2,则还需要安装 mxDateTime Python 包(SQLObject 使用 Python V2.3 的内置 datetime 模块,如果该模块可用的话)。

要实际使用 SQLObject,需要设置数据库包以及这种数据库的 Python 接口。SQLObject 连接多种数据库,其中包括三个大的开放源码产品:MySQL、PostgreSQL 和无服务器 SQLite。

最后,需要为应用程序创建数据库。对于 SQLite,这意味着创建一个存储该数据库的文件。对于其他数据库,这意味着连接数据库服务器,执行 CREATE DATABASE 命令,并授权数据库用户对新数据库的一些访问,以便 SQLObject 可以使用该用户帐户来连接。

清单 1 展示了如何用 MySQL 创建新数据库。

清单 1. 用 MySQL 创建新数据库的代码


mysql> use mysql;
Database changed
mysql> create database sqlobject_demo;
Query OK, 1 row affected (0.00 sec)
mysql> grant all privileges on sqlobject_demo to 'dbuser'@'localhost' 
identified by 'dbpassword';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

连接数据库

需要编写的第一个 Python 代码是数据库连接代码。基于所使用的数据库,这是惟一需要编写不同代码的地方。

例如,如果想让应用程序使用 SQLite 数据库,则需要将数据库文件的路径写入位于 sqlobject.sqlite 包的 SQLite 连接构建器中。如果数据库文件不存在,QLObject 将告诉 SQLite 创建一个,代码如下:


import sqlobject
from sqlobject.sqlite import builder
conn = builder()('sqlobject_demo.db')


如果使用的是 MySQL 或带有服务器的其他数据库,则将数据库连接信息传递到连接构建器中。清单 2 提供了在上一节创建的 MySQL 数据库的示例。



清单 2. 传递 MySQL 数据库连接信息的代码


import sqlobject
from sqlobject.mysql import builder
conn = builder()(user='dbuser', passwd='dbpassword',
                 host='localhost', db='sqlobject_demo')
                 


不管连接哪种数据库,连接代码都应该放置在一个名称类似 Connection.py 的文件中,且该文件存储在一些通常可访问的位置中。这样,可以导入您定义的所有类,并使用已经构建的 conn 对象。conn 变量将包含所有与数据库相关的详细信息。

但是要注意,SQLObject 的一些特性不可用于 SQLite 或 MySQL。不能将数据库选择与连接之后编写的代码完全分离。(参阅 Using SQLObject with pre-existing tables 获得更多信息。)

定义模式

SQLObject 使得操作数据库表变得容易。看第一个简单的例子,考虑一个电话簿应用程序的由单个表组成的数据库模式,如表 1 所示。

表 1. phone_number 表的描述

字段 类型 说明
id Int 主键
number String “(###) ###-####”字符串格式;应该惟一
owner String 这是谁的号码?
last_call Date 用户最后一次呼叫该号码是什么时候?

取决于您的 SQL 风格,该表的 SQL 类似如下:


CREATE TABLE phone_number (
  id INT PRIMARY KEY AUTO_INCREMENT,
  number VARCHAR(14) UNIQUE,
  owner VARCHAR(255),  
  last_call DATETIME,
  notes TEXT
)


使用 SQLObject,不需要编写该 SQL 代码。通过定义 Python 类来定义该数据表。该代码将进入名为 PhoneNumber.py 的文件中,如清单 3 所示。



清单 3. PhoneNumber.py


import sqlobject
from Connection import conn

class PhoneNumber(sqlobject.SQLObject):
    _connection = conn
    number = sqlobject.StringCol(length=14, unique=True)
    owner = sqlobject.StringCol(length=255)
    lastCall = sqlobject.DateTimeCol(default=None)
PhoneNumber.createTable(ifNotExists=True)


在此使用了先前定义的 conn 变量。每个表类需要将对数据库连接的引用存储在它的 _connection 成员中。该信息隐式用于对该类的表的所有数据库访问中。因此,不必担心 SQL 或任何特殊数据库,因为代码可以按照抽象关系模式来表示。

定义表的类还有一组定义表字段的成员。SQLObject 提供了 StringColBoolCol 等等 —— 一个类对应一种数据库字段类型。

createTable() 方法第一次运行时,SQLObject 将创建一个名为 phone_number 的表。然后,它将只使用该表,因为您将 ifNotExists 设置为 True 来调用该方法。

最后注意,无需在 PhoneNumber 中为 id 字段创建字段对象。因为 SQLObject 总是需要该字段对象,所以它总会创建一个。

处理旧 CRUD

著名的缩写词 CRUD 代表对数据库行进行的四种操作:Create、Read、Update 和 Delete。定义了与数据库表对应的类之后,SQLObject 将对表行的操作表示为对类及其实例的操作。

创建

要创建数据库行,需创建对应类的实例,代码如下:


>>> from PhoneNumber import PhoneNumber
>>> myPhone = PhoneNumber(number='(415) 555-1212', 
owner='Leonard Richardson')


现在 phone_number 表有一个存储我的姓名的行。PhoneNumber 构造函数将表列的值作为关键字参数。它使用您提供的数据创建一行 phone_number。如果由于某种原因,数据不能进入数据库,则构造函数抛出一个异常。当电话号码无效时,会发生如下情况:



清单 4. 无效的电话号码


>>> badPhone = PhoneNumber()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
...
TypeError: PhoneNumber() did not get expected keyword argument number


如果电话号码已经在数据库中,将会看到:



清单 5. 电话号码已经在数据库中


>>> duplicatePhone = PhoneNumber(number="(415) 555-1212")
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
...
TypeError: PhoneNumber() did not get expected keyword argument owner


读取(和查询)

SQLObject 类的实例的所有字段可用作成员。这与其他一些将数据库行当作字典的数据库映射工具相反。因此,对于数据库行中的每个字段,PhoneNumber 对象都具有一个成员。



清单 6. 可用作成员的字段


>>> myPhone.id
1
>>> myPhone.owner
'Leonard Richardson'
>>> myPhone.number
'(415) 555-1212'
>>> myPhone.lastCall == None
True


但如何检索数据库中已经存在的 PhoneNumber 对象呢?需要对数据库执行查询来获取。这就是 SQLObject 的查询构造工具包(该软件包最有趣的特性之一)发挥作用的地方。它允许将 SQL 查询表示为 Python 对象链。如果您熟悉 Java™ 编程语言的对象关系包的话,它与可以对 Torque 中 Criteria 对象执行的操作一样。

您定义的每个 SQLObject 类都有一个 select() 方法。该方法接受一个定义查询的对象,返回与该查询匹配的项列表。例如,下面这个方法调用返回包含数据库中第一个电话号码的列表:



清单 7. select() 方法


>>> PhoneNumber.select(PhoneNumber.q.id==1)
<sqlobject.main.SelectResults object at 0xb7b76cac>
>>> PhoneNumber.select(PhoneNumber.q.id==1)[0]
<PhoneNumber 1 number='(415) 555-1212' lastCall=None 
owner='Leonard Richardson'>


PhoneNumber.q.id 指明想要对 phone_number 表的 id 字段运行查询。SQLObject 过载比较操作符(==!=<>= 等等)来执行除布尔表达式之外的查询。表达式 PhoneNumber.q.id==1 是与 id 字段值为 1 的每行相匹配的查询。更多示例如下:



清单 8. 更多示例


>>> PhoneNumber.select(PhoneNumber.q.id < 100)[0]
<PhoneNumber 1 number='(415) 555-1212' lastCall=None 
owner='Leonard Richardson'>
>>> PhoneNumber.select(PhoneNumber.q.owner=='Leonard Richardson').count()
1
>>> PhoneNumber.select(PhoneNumber.q.number.startswith('(415)')).count()
1


可以使用 SQLObject 的 ANDOR 函数来组合查询子句:

清单 9. 用于组合查询子句的 AND 和 OR 函数


>>> from sqlobject import AND, OR
>>> PhoneNumber.select(AND(PhoneNumber.q.number.startswith('(415)'),
>>>                        PhoneNumber.q.lastCall==None)).count()
1

下列查询获取一年中呼叫过的所有人以及从未呼叫过的所有人:

清单 10. 一年中呼叫过的所有人以及从未呼叫过的所有人


>>> import datetime
>>> oneYearAgo = datetime.datetime.now() - datetime.timedelta(days=365)
>>> PhoneNumber.select(OR(PhoneNumber.q.lastCall==None,
...                       PhoneNumber.q.lastCall < oneYearAgo)).count()
1

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章