BaseModel的create_instance方法
注意:以下代码基于odoo四月份的trunk版,新版本中create_instance方法已经被_build_model方法替换,但是原理基本一样。
如果没有耐心看原理的,可以直接看结论:
1、 本文主要分析odoo模型对象初始化过程和继承过程。
2、 odoo中对象不是直接创建,而是调用类函数create_instance创建,并且由于在__new__方法中返回的是None,所以即使我们使用小括号创建对象也不会返回新对象。
3、 odoo可以多继承,当”_inherit”字段为列表时,就使用了odoo的对象多继承,如果是字符串,则只有一个父对象,但是并不建议使用多继承。
4、 如果使用对象多继承的时候,同名的方法和属性都是以第一个出现的父类中的方法和属性为准,继承后面不会覆盖前面。举个例子,基类BaseModel中的create方法不会被其他父类的create方法覆盖。
我们知道,odoo中自己实现的模型都是继承自BaseModel这个基类,同时BaseModel也实现了一系列模型的基本操作,如search、create、write等方法,我们在操作模型的时候可以直接调用父类的相应方法。create_instance就是BaseModel众多方法其中之一,但是这个方法非常特殊。首先这是一个类方法,如下图:
这意味着调用它的是类而不是对象实例。
首先该方法会判断对象是否有父类,即_inherit字段是否存在,(注意,里的父类是指odoo层面的父类,而不是python级别的父类),如果有父类,则会遍历所有父类(因为有可能是继承多个父类,如果你_inherit字段是列表类型而不是字符串时,就是多继承),遍历过程都会进行如下操作:
1、记录父类的__class__属性:
2、遍历如下列表,并且copy给子类:
遍历如下图所示,先定义一个空字典nattr,对每个属性都从父类中copy一份:
处理遍历时会对”_columns”、”_constraints”进行特殊处理,如果其他属性是字典类型,则直接把原属性更新到new中,如下图所示:
对_columns是在上面那行代码之前进行处理的,因为_columns也是字典类型,相当于对这个字段进行预处理之后再进行常规处理。如果_columns中的字段具有manual属性,则不继承该字段。
如果是列表属性,则直接扩展,但是attributes里面处理_constraints是list类型外其他的都是字典类型,而_constraints已经被特殊处理了,也就是说下面这行代码正常情况下是不会被调用到的。
最后生成一个新的type,如下图:
需要注意的是,虽然type的第三个参数dict中的nattr会覆盖同名的属性,但是nattr中的同名属性已经在前面的代码中被子类的同名属性覆盖了一次,所以不会造成影响。
此时这个cls已经具备了该父类的方法定义和属性了。然后不断重复上述动作,直到遍历完所有父类。遍历完所有父类之后,cls就具备了所有父类的方法。
需要注意的是由于cls本身是BaseModel,所以,当父类覆写了BaseModel方法的时候,继承对象并不会继承父类的覆写方法,只会继承父类新增的方法,同理,如果两个父类有相同的方法,子类会继承第一个父类的方法(继承方法调用基于先根序)。在我们实际编写模块时需要注意这一点。
当需要创建的对象的类型构建完成之后,程序会调用object的__new__方法创建对象,然后手动调用对象的__init__方法进行初始化操作,如下图:
在__init__方法中会把自己添加到registry中去,所以虽然该方法是由registry的load方法调用,但是对象注册到registry是由对象自身完成的。