如何将继承对象视为OpenERP中的子类?

时间:2013-02-01 11:59:14

标签: python openerp

这个问题可能比最初看起来更复杂。

假设我有一个父类Animal(我们在OpenERP中将其命名为animal.base)。我还有子类Lion(animal.lion)和Elephant(animal.elephant)。我需要能够创建一个带有many2one字段的视图,该字段可以引用Animal的任何子类。这似乎可以通过以下方式起作用:

class animal_lion(osv.osv):
    _name = 'animal.lion'
    _inherits = {'animal.base': 'base_id'}

    _columns = {
         ...
         'base_id': fields.many2one('animal.base', "Base ID")
    }

    def roar(self, cr, uid, context=None):
        print "rarrrrr"

现在,当我们创建animal.lion的实例时,我们可以看到它在引用animal.base的视图中可见。 (普通inherit = 'animal.base'不会这样做,FWIW。)

然而,现在让我们说我们需要使用这种动物的方法。由于many2one仅指animal.base,因此我们不知道用户在视图中选择了哪种动物。即使我们碰巧知道只选择狮子会,我们也不能调用roar,因为animal.base对象只会让我们调用自己定义的方法。我们可以尝试通过命名方法emit_sound并尝试覆盖Lion类中的方法来破解它。至少会运行(除了_inherit之外还添加_inherits),但它不会生成正确的Lion特定输出。我们需要的是一些方法来确定在基类x上的many2one中选择的特定实例的动态类型,其中多个子类在同一个类_inherits上指定x。想象一下虚构的方法get_subtype()。然后我们可以在视图的按钮处理程序中说出以下内容:

def perform(self, cr, uid, ids, context=None):
    this = self.browse(cr, uid, ids[0], context)
    subtype_name = this.my_many2one.get_subtype()
    subtype = self.pool.get(subtype_name)
    # will produce a roar if user picked a lion, else a meep
    subtype.emit_sound(cr, uid, context)

或者,是否有其他架构可用于完成相同的任务? (是的,我设计了这个例子,但它应该说明真正的问题。)[也许在每个子类型实例的字段中编码子类型名称? ]

我受限于OpenERP v5,但有兴趣知道任何版本的答案。

1 个答案:

答案 0 :(得分:4)

这里的关键是你希望你的base.animal在数据库中独立存在,作为所有动物的共同索引,因此这会使你的数据模型大大复杂化并迫使你使用记录级继承(通过_inherits)。

要解决动物的子类型,您应在type中添加明确的animal.base列,并始终正确设置,以便推断子类型记录。

# This static list could also be replaced by a function
ANIMALS = [
    ('lion', 'Lion'),
    ('elephant', 'Elephant'),
]
class animal_base(osv.osv):
    _name = 'animal.base'
    _columns = {
         ...
         'type': fields.selection(ANIMALS, 'Type'), 
    }

您的base.animals将自己存在于数据库中,并且可以拥有自己的视图,因为您正在使用记录级继承。 子类型(例如狮子)可以被视为每只动物的“装饰”,它实际上可能不是唯一的(base.lionbase.elephant记录都可能存在对于相同的base.animal记录,所以你应该在某处添加唯一性约束。

现在,您永远不应该让_inherit_inherits指向同一个父模型,这两个继承方案实际上是出于不同的目的,如OpenERP technical memento中所述。 相反,您可以在animal.base中使用大致看起来像perform方法的代理方法,除了他们需要找出除了类型之外的子记录的ID,例如:

def emit_sound(self, cr, uid, ids, context=None):
    for this in self.browse(cr, uid, ids, context):
       animal_registry = self.pool['animal.%s' % this.type]
       animal_ids = animal_registry.search(cr, uid, 
           [('base_id','=',this.id)], context)
       assert len(animal_ids) == 1, 'Chimera alert! ;-)'
       animal_registry.emit_sound(cr, uid, animal_ids, context)

当然,您可以通过多种方式对其进行优化,例如将函数字段添加到base.animal以自动执行更多此管道工作。


另一方面,如果你不真的需要base.animal存在于其他真实动物旁边,而只需要一种方法来选择一种形式的任意动物您可以尝试使用_inherit + _name的传统继承,base.animal是它们的抽象基类(实际上不保存任何记录)。 选择任意动物可以使用fields.reference来完成,您可以在其上过滤目标模型列表。 5.0 subscription模块包含an example with the doc_source field 请注意,fields.reference是混合的,不能与例如无缝集成。 browsereadsearch。它以'model,id'的形式存储为字符串,并且您需要在需要取消引用时手动拆分值 - 因此,如果您走这条路,请务必小心。它作为伪 - many2one集成的唯一位置是在客户端UI上,在其他任何地方它只是一个简单,愚蠢的字符串值。