2

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

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

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。(Plaininherit = 'animal.base'不会这样,FWIW。)

但是,现在假设我们需要使用这种动物的方法。由于 many2one 仅指animal.base,我们不知道用户在视图中选择了哪种动物。即使我们碰巧知道只有狮子会被选中,我们也不能调用roar,因为animal.base对象只会让我们调用自己定义的方法。emit_sound我们可以尝试通过命名方法并尝试覆盖 Lion 类中的方法来绕过它。这至少会运行(一旦你添加一个_inherit除了_inherits),但它不会产生正确的 Lion 特定输出。需要某种方法来确定在基类的 many2one 中选择的特定实例的动态类型,x其中多个子类_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,但有兴趣知道任何版本的答案。

4

1 回答 1

4

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

为了解析动物的子类型,您应该在 中添加一个显式typeanimal.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将自己存在于数据库中并且可以拥有自己的视图,因为您使用的是记录级继承。子类型(例如狮子)可以被视为每个动物的“装饰”,它实际上可能不是唯一的(同一记录可能同时存在 a和记录base.lion),因此您应该在某处添加唯一性约束。base.elephantbase.animal

现在,你永远不应该同时拥有_inherit_inherits指向同一个父模型,这两个继承方案实际上是为了不同的目的,正如 OpenERP技术备忘录中所解释的那样。相反,您可以使用代理方法,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.0subscription模块包含一个带有doc_source字段的示例。
当心,fields.reference是一种不能与例如browse,readsearch. 它以字符串形式存储为'model,id'并且您必须在需要取消引用时手动拆分该值 - 所以如果您走那条路要小心。它作为伪集成的唯一地方many2one是在客户端 UI 上,在其他任何地方它只是一个普通的、愚蠢的字符串值。

于 2013-02-01T15:48:52.453 回答