第八章 嵌套集模型

问题的起源

接触过odoo一段时间后的同学肯定会注意到,odoo种有这么一种数据库结构的存在,典型的有产品分类(product.template)、库位(stock.location)和业务伙伴(res.partner)。这几种数据对象的共性就是拥有上下级式的结构。

写过后端业务代码的同学肯定熟悉,我们在处理这种遗传式的结构时通常采用的一种解决方案就是添加一个parent字段,用于指明该记录的父记录是哪一个。这样做的好处就是很容易查询到记录的上级记录,追溯祖先记录也很方便,递归查找即可。但是这种方式的弊端就是在查找子记录的时候,我们很容易查询下一级的子记录,但是查找孙记录却只能递归子记录,试想如果这个层级如果很多,那么肯定会出现性能问题。

parent_left和parent_right就是为了解决这个问题而产生的一种思路,学名嵌套集模型,是由SQL领域的大神Joe Celko发明的。下面来简单介绍一下嵌套集模型的概念。

嵌套集模型

假设我们有两个节点A和B,A是B的父节点,那么嵌套模型的定义如下:

A.parent_left < B.parent_left, B.parent_right < A.parent_right

也就是说,A的parent_left和parent_right要把B完全包含在内,才算是A的子节点。

按照这种说法,假设我们有A,B,C,D,E五个节点,层级结构如下:

因此 A(1,10) B(2,5),C(3,4),D(6,9),E(7,8)

这样的好处就是,查找节点A的所有子孙节点,一句简单的sql就可以完成。

select * from table where parent_left >1 and parent_right < 10;

避免了由于递归查询造成的性能损耗。

odoo中的集成

12.0 版本之前,odoo对于嵌套模型的实现是通过parent_left和parent_right字段实现的,关于这点可以参考odoo11版本中的product.category和stock.location模型。

从12.0版本开始,odoo抛弃了之前的实现,由parent_path取代了parent_left和parent_right。

parent_path中存储的是当前节点到根节点的路径,以/分割。典型的一个数据结构示例如下:

这是一个更为高效地parent_of/child_of的实现。

我们要定义一个实现了嵌套集模型的模型,首先要在模型的类属性中指定_parent_name字段,并且将_parent_store设置为True,以标识将该字段存储到数据库中以提高筛选效率。

class DemoClass(models.Model):
    _name = 'demo.class'

    _parent_name = "parent_id"
    _parent_store = True

    ...

然后定义三个字段,parent_id、parent_path和child_id,parent_id的字段名称可以自己定义,但是要在_parent_name中标明,parent_path和child_id不可以变更。

    parent_id = fields.Many2one('demo.class', string="父级分类")
    parent_path = fields.Char(index=True)
    child_id = fields.One2many(
        "demo.class", "parent_id", string="子分类")

parent_path字段最好添加索引以加快查询速度。

这样就完成了上下级分类模型的建立,安装模块后可以在数据库中查看到这些字段的作用。

results matching ""

    No results matching ""