科技行者

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

知识库

知识库 安全导航

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

使用 SQLObject 连接数据库与 Python

  • 扫一扫
    分享文章到微信

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

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

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

关键字: 软件

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

如果更改 PhoneNumber 对象的一个成员,则该更改被自动镜像映射至数据库:



清单 11. 更改被自动镜像映射至数据库


>>> print myPhone.owner
Leonard Richardson
>>> print myPhone.lastCall
None
>>> myPhone.owner = "Someone else"
>>> myPhone.lastCall = datetime.datetime.now()
>>> #Fetch the object fresh from the database.
>>> newPhone = PhoneNumber.select(PhoneNumber.q.id==1)[0]
>>> print newPhone.owner
Someone else
>>> print newPhone.lastCall
2005-05-22 21:20:24.630120


这只是一个警告:SQLObject 不允许更改对象的主键。通常最好是让 SQLObject 来管理表的 id 字段,即使是您不使用 SQLObject 时碰巧用另一个字段作为主键。

删除

删除特定行对象的方法是将其 ID 传递到其类的 delete() 方法中:



清单 12. 删除行对象


>>> query = PhoneNumber.q.id==1
>>> print "Before:", PhoneNumber.select(query).count()
Before: 1
>>> PhoneNumber.delete(myPhone.id)
>>> print "After:", PhoneNumber.select(query).count()
After: 0


验证和转换数据

到目前为止,一直用 14 字符美国格式传递电话号码。数据库模式被设计成接受这种格式的数字,这暗示着底层应用程序 —— 不管怎样 —— 都希望数字是这种格式。尽管如此,代码不能确保笨拙的用户或程序员漏洞不会导致未正确格式化的数据放入 number 字段中。

SQLObject 通过允许定义验证和转换入站数据的钩子方法来解决这个问题。可以为表中的每个字段定义一个方法。字段的钩子方法命名为 _set_[field name](),不管是作为 create 操作还是 update 操作的一部分,每当要为该字段设置一个值时,都会调用该方法。钩子方法应(可选)将入站值转换为可接受格式,然后设置该值。否则,它应抛出异常。要实际设置一个值,该方法需要调用 SQLObject 方法 _SO_set_(field name)

清单 4 展示了 PhoneNumber_set_number() 方法。如果电话号码完全没有格式化,比如 4155551212,则该方法将该数字格式化为 (415) 555-1212。否则,如果数字格式不正确,该方法会抛出 ValueError。正确格式化的电话号码 —— 或者是转换为正确格式的电话号码 —— 被正确传递给 SQLObject 的 _SO_set_number() 方法。



清单 13. PhoneNumber 的 _set_number () 方法


   import re
   def _set_number(self, value):
      if not re.match('\([0-9]{3}\) [0-9]{3}-[0-9]{4}', value):
         #It's not in the format we expect.
         if re.match('[0-9]{10}', value):
            #It's totally unformatted; add the formatting.
            value = "(%s) %s-%s" % (value[:3], value[3:6], value[6:])
         else:
            raise ValueError, 'Not a phone number: %s' % value
      self._SO_set_number(value)
      


定义表之间的关系

到目前为止,看到的所有操作都针对单个表:phone_number。但真正的数据库应用程序通常具有多个相关表。SQLObject 允许将表之间的关系定义为外键。作为演示,我们将一个小的数据库规范化(normalization)应用于上一示例,将 PhoneNumberowner 字段分割到单独的 person 表中。清单 14 所示的代码保存在名为 PhoneNumberII.py 的文件中。



清单 14. PhoneNumberll.py 的代码


import sqlobject
from Connection import conn


class PhoneNumber(sqlobject.SQLObject):
    _connection = conn
    number = sqlobject.StringCol(length=14, unique=True)
    owner = sqlobject.ForeignKey('Person')
    lastCall = sqlobject.DateTimeCol(default=None)


class Person(sqlobject.SQLObject):
    _idName='fooID'
    _connection = conn
    name = sqlobject.StringCol(length=255)
    #The SQLObject-defined name for the "owner" field of PhoneNumber
    #is "owner_id" since it's a reference to another table's primary
    #key.
    numbers = sqlobject.MultipleJoin('PhoneNumber', joinColumn='owner_id')


Person.createTable(ifNotExists=True)
PhoneNumber.createTable(ifNotExists=True)


PhoneNumber 类具有与旧类相同的成员,但它的 owner 成员是对 person 表的主键的引用,而不是对 phone_number 表中字符串列的引用。这使得表示具有两个电话号码的个人成为可能:

清单 15. 表示具有两个电话号码的个人


>>> from PhoneNumberII import PhoneNumber, Person
>>> me = Person(name='Leonard Richardson')
>>> work = PhoneNumber(number="(650) 555-1212", owner=me)
>>> cell = PhoneNumber(number="(415) 555-1212", owner=me)


Personnumbers 成员,一个 SQLObject MultipleJoin,使得基于 personphone_number 的连接进行查询变得容易:

清单 16. 基于 person 到 phone_number 的连接的查询


>>> for phone in me.phoneNumbers:
...     print phone.number
...
(650) 555-1212
(415) 555-1212


同样,SQLObject 允许使用 MultipleJoin 类进行多对多连接的查询。

将 SQLObject 用于现有表

SQLObject 的一个常见用途是为另一个应用程序创建的数据库提供 Python 接口。SQLObject 有多个特性可用于实现这一点。

数据库内省

如果正在使用数据库中已经存在的表,则不需要在 Python 中定义列。SQLObject 可以通过数据库内省来提取它需要的信息。例如,清单 17 中的代码保存在 PhoneNumberIII.py 中。

清单 17. PhoneNumberlll.py 的代码


import sqlobject
from Connection import conn
class PhoneNumber(sqlobject.SQLObject):
    _connection = conn
    _fromDatabase = True



该类将使用现有 phone_number 数据表的属性。您可以与它交互,就好像已经手动定义了该类及其所有列一样,如前面的示例所示。使用 SQLObject,只需要编写表定义一次 —— 用 SQL 还是用 Python 编写就取决于您了。

但是,该特性又带来了数据库的选择问题。例如,该特性完全不能用于 SQLite。它基本上能用于 MySQL,但不能提取外键关系。如果使用的是 MySQL,而且想要为表定义外键,则需要在从数据库中加载模式之后,编写代码定义这些字段。

命名约定

上一部分中的代码假设现有表符合 SQLObject 的命名约定(例如,表的主键字段名为 id,且列名中的词用下划线分隔)。表的命名约定在 Style 类中定义。

SQLObject 提供了一些与常见数据库命名约定对应的 Style 类。例如,如果列名类似 likeThis 而非 like_this,则可以使用 MixedCaseStyle

清单 19. 使用 MixedCaseStyle


import sqlobject
from sqlobject.styles import MixedCaseStyle
from Connection import conn
class PhoneNumber(sqlobject.SQLObject):
    _connection = conn
    _fromDatabase = True
    _style = MixedCaseStyle



如果没有预包装的 Style 类符合您的需要,那么您可以定义 Style 基类的子类,并定义自己的命名约定。在最坏的情况下,如果表的字段名分配得毫无道理,则可以逐个命名每个字段。

关于 SQLObject 限制

SQLObject 想让您用面向对象的方式而非关系方式进行思考。这有利于您的理解和您的编程生产率,但不利于性能。毕竟,数据库仍是关系型的。如何标记呼叫过的每个电话号码?使用 SQL,您将使用单个 UPDATE 命令。使用 SQLObject,您需要迭代通过整个结果集,并修改每个对象的 last_call 成员,这是非常低效的。

SQLObject 为开发人员时间牺牲了处理器时间。这通常是好的交易,但甚至在简单的应用程序中,您也可能需要下降一个级别到达 Python 数据库接口,为一些关键路径的操作编写原始 SQL。

查看本文来源

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

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

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