这个问题可能比最初看起来更复杂。
假设我有一个父类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,但有兴趣知道任何版本的答案。
答案 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.lion
和base.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
是混合的,不能与例如无缝集成。 browse
,read
或search
。它以'model,id'
的形式存储为字符串,并且您需要在需要取消引用时手动拆分值 - 因此,如果您走这条路,请务必小心。它作为伪 - many2one
集成的唯一位置是在客户端UI上,在其他任何地方它只是一个简单,愚蠢的字符串值。