[转] 强大的sqlalchemy
sqlalchemy 的文档可谓典范,谁叫作者还开发着模板语言(myghty、mako)呢,呵呵。其实 sqlalchemy 的文档就是用 myghty 写的。
不过系统复杂了,功能多了,再好的文档也会让人迷路。最近用了用 sqlalchemy ,对这一点感受颇深,故把临时想到的几个比较常用的功能摘录如下,提纲挈领,既为自己整理一下思路,也让新手一窥 sqlalchemy 的精华。
Eager Loading
Join,本是关系数据库中多么常见的操作,怎奈 django 的 orm 就是不支持,SQLObject 的做法也很不如人意。
Association Object
many-to-many 关系都是通过增加一个中间表来实现,映射到对象后,这个中间表就不需要我们再操心了,会隐式地进行处理。
不过对于多个实体两两之间多对多关系,往往另外再增加一个关联对象会更方便。
这样的例子其实也不少,比如:user-bookmark-tags、产品-元件-元件供应商(这是一次期末考试题目里面的 ;-)
Deferred Column Loading
比如文章表里面的 body 字段通常比较大,在获取文章列表时这个字段就不必取出来了。甚至如果你有某个字段存的是文件的话,这个功能就更加重要了。
这本是个不起眼的小功能,不过上次看到 javaeye 中有一贴说到大名鼎鼎的 Hibernate 都对这个功能实现得这么痛苦后,我蓦然发现 sa 真的很 nb。呵呵,托了动态语言的福了吧。
Mapping a Class with Table Inheritance
如何把对象间继承关系映射到关系数据库,sqlalchemy 提供三种方式:
single table inheritance 所有子类型都放在一个表中;
concrete table inheritance 每一种子类型存在独立的表中;
multiple table inheritance 父子类型都存在独立的表中,查询的时候进行连接;
显然最后一种是冗余最少的,不过查询的时候要做一次连接操作,如何选择还是看具体情况了。
Mapping a Class against Arbitary Selects
将对象映射到任意的 select,其实也就是任意的 sql 子查询。
这功能太强大了,有了这个后,我们就可以骄傲地宣称,(几乎)没有什么是 sqlalchemy 做不了的了!
Identity Map
session 在 sqlalchemy 中是一个非常重要的概念,session 跟踪对象的修改情况,跟踪对象之间的关联,智能判断数据库操作执行的顺序等等。
Identity Map 是 session 中一个容易让人掉入陷阱的概念,你可以把它想象成一个以数据表主键为key的cache。每次从数据库查询后,如果 sqlalchemy 发现 Identity Map 中已经有了相同主键的实例,那就不会重新生成实例了。因为如果存在多个实例会带来许多问题,比如多个实例分别修改并保存时就会产生混乱。
偶尔 Identity Map 也会产生一些意想不到的行为,比如 ticket 458 ,不过理解了 Identity Map 的机理后,也就没什么问题了。
值得一提的是,Mapper Options 有一个 always_refresh 参数,如果把它设为True,则对该 mapper 的任何查询操作都会自动使用从数据库中查询到的数据覆盖 Identity Map 中已有的实例,这样要是对旧实例做过什么还没保存的修改的话,就都没了。所以要慎用!
Cascade rules
最后这个也是很有用的功能,举个例子来说吧,user 和 article 有一对多的关系,现在删除一个 user,是否应该把相关的 article 也删了呢,要 article 还有其他的依赖关系呢?这些决定当然是要根据实际的需求来,而控制这些行为的方法就是通过 relation 的 cascade 参数,具体取值及其含义看文档去吧。
总滴来说,本文只是个提纲的作用,具体还得去看文档,看示例,看unittest。
最后还想说两句的就是,大家之所以选择 ORM ,主要原因是逃离 SQL,然而我感觉不能掌握 SQL 是不能(很好)掌握sqlalchemy的。至少要对关系数据库的这些概念了解,理解 SQL 就是理解关系数据库。只有这样才能利用sqlalchemy将关系数据库发挥到极致!
使用 sqlalchemy 的好处就是不用写 sql 了,屏蔽不同dbms之间SQL语法的区别,同时又让你在需要的时候能够利用到不同 DBMS 提供的一些独特特性,让你以对象的方式管理数据库访问代码,提高代码重用性!