21.3 对象-关系管理器(ORM)

通过前一节我们知道,如今有很多种数据库系统,他们中的绝大多数都有Python接口,以方便你驾驭他们的能量。这些系统唯一的缺点是需要你懂得SQL。如果你喜欢折腾Python对象却讨厌SQL查询,又想使用关系型数据库作为你的数据存储的后端,你就完全具备成为一个ORM用户的天资。

21.3.1 考虑对象,而不是SQL

这些系统的创建者将绝大多数纯SQL层功能抽象为Python对象,这样你就无需编写SQL也能够完成同样的任务。如果你在某些情况下实在需要SQL,有些系统也允许你拥有这种灵活性。但绝大多数情况下,你应该尽量避免进行直接的SQL查询。

数据库的表被转换为Python类,它具有列属性和操作数据库的方法。让你的应用程序支持ORM非常类似使用那些标准的数据库接口程序。由于大部分工作由ORM代为处理,相比直接使用接口程序来说,一些事情可能实际需要更多的代码。令人欣慰的是,一点点额外的付出会回报你更高的生产率。

21.3.2 Python和ORM

如今最知名的Python ORM模块是SQLAlchemy和SQLObject。由于二者有着不同的设计哲学,我们会分别给出SQLAlchemy和SQLObject的例子。只要你能搞清楚这两种ORM的使用,转到其他的ORM将是相当简单的事。

其他的Python ORM包括pyDO/PyD02、PDO、Dejavu、Durus、QLime和ForgetSQL。一些大型的Web开发工具/框架也可以有自己的ORM组件,如WebWare MiddleKit和Django的数据库API。需要指出的是,知名的ORM并不意味着就是最适合你的应用程序的ORM。那些其他的ORM虽然没有纳入我们的讨论范围,但一样有可能是适合你的应用程序的选择。

21.3.3 雇员数据库举例

现在我们将shuffle应用程序ushuffle_db.py改造为使用SQLAlchemy和SQLObject实现。数据库后端仍然是MySQL。相对于直接使用原始SQL来讲,我们使用ORM时用类代替了函数,这样会更有对象的感觉。两个例子都使用了ushuffle_db.py中的NAMES集合和随机名字选择函数。这是为了避免将同样的代码到处复制粘贴,代码能够被有效重用是件好事情。

1.SQLAlchemy

与SQLObject相比,SQLAlchemy的接口在某种程度上更接近SQL,所以我们先从SQLAlchemy开始。SQLAlchemy的抽象层确实相当完美,而且在你必须使用SQL完成某些功能时,它提供了足够的灵活性。你会发现这两个ORM模块在设置及存取数据时使用的术语非常相似,代码长度也很接近,都比ushuffle_db.py少(包括共享的names列表和随机名字生成器)。

2.逐行解释

1 ~ 10行

和前面一样,第一件事是导入相关的模块和常量。我们提倡首先导入Python标准库模块,然后再导入第三方或扩展模块,最后导入本地模块这种风格。这些常量都是自解释的。

12 ~ 31行

12~31行是类的构造器,类似ushuffle_db.connect()。它确保数据库可用并返回一个有效连接(第18~31行)。这也是唯一能看到原始SQL的地方。这是一种典型的操作任务,不是面向应用的任务。

33 ~ 44行

这个try-except子句(33~40行)用来重新载入一个已有的表,或者在表不存在的情况下创建一个新表。最终我们得到一个合适的对象实例。

例21.2

这个user shuffle程序的主角是SQLAlchemy前端和MySQL数据库后端。

21.3 对象-关系管理器(ORM) - 图1

21.3 对象-关系管理器(ORM) - 图2

21.3 对象-关系管理器(ORM) - 图3

46 ~ 70行

这4个方法处理数据库核心功能:创建表(46~52行、插入数据(54~57行)、更新数据(59~64行)、删除数据(66~70行)。我们也有一个方法用来删除表。

21.3 对象-关系管理器(ORM) - 图4

不过,我们还是决定提供另一种授权处理方式(曾在第13章中介绍)。授权就是指一个方法调用不存在时,转交给另一个拥有此方法的对象去处理。参见第79~80行的解释。

72 ~ 77行

输出内容由dbDumpO方法完成。它从数据库中得到数据,就像ushuffle_db.py中那样对数据进行美化,事实上,这部分代码几乎完全相同。

79 ~ 80行

应该尽量避免为一个表创建一个drop()方法,因为这总是会调用table自身的drop()方法。同样,既然没有新增功能,那我们有什么必要创建另一个函数?无论属性查找是否成功,特殊方法getattr()总是会被调用。如果调用orm.drop()却发现这个对象并没有drop()方法,getattr(orm,’drop’)就会被调用。发生这种情况时,getattr()被调用,之后将这个属性名委托给self.users。解释器会发现self.users有一个drop属性并执行。

例21.3 SQLObject ORM示例(ushuffle_so.py)

这个user shuffle应用程序的主角前端是SQLObject,后端是MySQL数据库。

21.3 对象-关系管理器(ORM) - 图5

21.3 对象-关系管理器(ORM) - 图6

21.3 对象-关系管理器(ORM) - 图7

82 ~ 84行

最后一个方法是finish,它来提交整个事务。

86 ~ 114行

main()函数是整个应用程序的入口,它创建了一个MySQLAlchemy对象并通过它完成所有的数据库操作。这段脚本和ushuffle_db.py功能一样。你会注意到数据库参数db是可选的,而且在ushuffle_sa.py和即将碰到的ushuffle_so.py中,它不起任何作用。它只是一个占位符以方便你对这个应用程序添加其他的数据库支持(参见本章后面的习题)。

运行这段脚本,你会看到类似下面的输出:

21.3 对象-关系管理器(ORM) - 图8

21.3 对象-关系管理器(ORM) - 图9

3.逐行解释

1 ~ 10行

除了我们使用的是SQLObject而不是SQLAlchemy以外,导入模块和常量声明几乎与ushuffle_sa.py相同。

12 ~ 42行

类似我们的SQLAlchemy例子,类的构造器做大量工作以确保有一个数据库可用,然后返回一个连接。同样的,这也是你能在程序里看到SQL语句的唯一位置。我们这个程序,如果因为某种原因造成SQLObject无法成功创建用户表,就会陷入无限循环当中。

我们尝试能够聪明地处理错误,解决掉这个重建表的问题。因为SQLObject使用元类,我们知道类的创建幕后发生特殊事件,所以我们不得不定义两个不同的类,一个用于表已经存在的情况,一个用于表不存在的情况。代码工作原理如下。

1.尝试建立一个连接到一个已经存在的表。如果正常工作,成功(第23〜29行).

2.如果第一步不成功,则从零开始为这个表创建一个类,如果成功,成功(第31〜36行).

3.如果第二步仍不成功,我们的数据库可能遇到麻烦,那就重新创建一个新的数据库(第37〜40行).

4.重新开始新的循环。

希望程序最终能在第一步或第二步成功完成。当循环结束时,类似ushuffle_sa.py,我们得到合适的对象实例。

44 ~ 67行、77 ~ 78行

这些行处理数据库操作。我们在44〜47行创建了表,并在77行删掉了表。在49〜52行插入数据,在54〜60行更新数据,在62〜67行删除了数据。78行调用了finish()方法来关闭数据库连接。我们不能像SQLAlchemy那样使用授权删表代理,因为SQLObject的删表代理名为dropTable()而不是drop().

69 ~ 75行

使用dbDump()方法,我们从数据库中得到数据,并将它显示在屏幕上。

80 ~ 108行

又到了main()函数。它工作的方式非常类似ushuffle_sa.py。同样,构造器的db参数仅仅是一个占位符,用以支持其他的数据库系统(参阅本章最后的习题).

当你运行这段脚本时,你的输出可能类似这样:

21.3 对象-关系管理器(ORM) - 图10

21.3 对象-关系管理器(ORM) - 图11

21.3.4 总结

关于如何在Python中使用关系型数据库,希望我们前面介绍的东西对你有用。当你应用程序的需求超出纯文本或类似DBM等特殊文件的能力时,有多种数据库可供选择,别忘了还有一个完全由Python实现的真正的免安装维护和管理的真实数据库系统。你能在下面找到多种Python数据库接口程序和ORM系统。我们也建议你研究一下互联网上的DB-SIG的网页和邮件列表。类似其他的软件开发领域,只不过Python更简单易学,用户体验更好。