模型继承

Odoo提供了两种继承机制,用于以模块化方式扩展现有模型。

第一个继承机制允许一个模块修改另一个模块中定义的模型行为:

  • 向模型添加字段,
  • 覆盖模型上字段的定义,
  • 向模型添加约束,
  • 向模型添加方法,
  • 覆盖模型上的现有方法。

第二种继承机制(委托)允许将模型的每个记录链接到父模型中的记录,并提供对父记录的字段的透明访问。

视图继承

不需要修改现有的视图(通过覆盖它们),Odoo提供了视图继承,子视图从根视图扩展视图,并且可以从父视图添加或删除内容。

扩展视图使用inherit_id字段引用其父视图,而不是单个视图,它的arch字段由任意数量的xpath元素组成,这些元素选择和更改其父视图的内容:

<!-- improved idea categories list -->
<record id="idea_category_list2" model="ir.ui.view">
    <field name="name">id.category.list2</field>
    <field name="model">idea.category</field>
    <field name="inherit_id" ref="id_category_list"/>
    <field name="arch" type="xml">
        <!-- find field description and add the field
             idea_ids after it -->
        <xpath expr="//field[@name='description']" position="after">
          <field name="idea_ids" string="Number of ideas"/>
        </xpath>
    </field>
</record>

expr
在父视图中选择单个元素的XPath表达式。如果不匹配元素或不匹配元素,则引发错误
position
应用于匹配元素的操作:

  • inside
    xpath在匹配元素的末尾附加的主体
  • replace
    xpath的主体替换匹配的元素,用$0原始元素替换新主体中出现的任何节点
  • before
    xpath在匹配元素之前将的正文作为同级插入
  • after
  • xpaths在匹配的元素之后将的正文作为同级插入
    attributes
    变造使用特殊的匹配元素的属性attribute的元素的xpath的身体

匹配单个元素时,position可以直接在要查找的元素上设置属性。下面的两个继承将给出相同的结果。

<xpath expr="//field[@name='description']" position="after">
    <field name="idea_ids" />
</xpath>

<field name="description" position="after">
    <field name="idea_ids" />
</field>

“更改现有内容”练习
使用模型继承,修改现有的Partner模型以添加一个 instructor布尔字段和与学期伙伴关系相对应的many2many字段。
使用视图继承,在伙伴表单视图中显示此字段。
(这是引入开发人员模式检查视图,找到其外部id以及放置新字段的位置的机会。)

  1. 创建一个openacademy/models/partner.py文件,并在__init__.py中将其导入。
  2. 创建一个openacademy/views/partner.xml文件,并将其添加到__manifest__.py清单中。

openacademy/init.py

# -*- coding: utf-8 -*-
from . import controllers
from . import models
from . import partner

openacademy/manifest.py

        # 'security/ir.model.access.csv',
        'templates.xml',
        'views/openacademy.xml',
        'views/partner.xml',
    ],
    # only loaded in demonstration mode
    'demo': [

openacademy/partner.py

# -*- coding: utf-8 -*-
from odoo import fields, models

class Partner(models.Model):
    _inherit = 'res.partner'

    # Add a new column to the res.partner model, by default partners are not
    # instructors
    instructor = fields.Boolean("Instructor", default=False)

    session_ids = fields.Many2many('openacademy.session',
        string="Attended Sessions", readonly=True)

openacademy/views/partner.xml

<?xml version="1.0" encoding="UTF-8"?>
 <odoo>

        <!-- Add instructor field to existing view -->
        <record model="ir.ui.view" id="partner_instructor_form_view">
            <field name="name">partner.instructor</field>
            <field name="model">res.partner</field>
            <field name="inherit_id" ref="base.view_partner_form"/>
            <field name="arch" type="xml">
                <notebook position="inside">
                    <page string="Sessions">
                        <group>
                            <field name="instructor"/>
                            <field name="session_ids"/>
                        </group>
                    </page>
                </notebook>
            </field>
        </record>

        <record model="ir.actions.act_window" id="contact_list_action">
            <field name="name">Contacts</field>
            <field name="res_model">res.partner</field>
            <field name="view_mode">tree,form</field>
        </record>
        <menuitem id="configuration_menu" name="Configuration"
                  parent="main_openacademy_menu"/>
        <menuitem id="contact_menu" name="Contacts"
                  parent="configuration_menu"
                  action="contact_list_action"/>

</odoo>

在Odoo中,搜索域是对记录条件进行编码的值。域是用于选择模型记录子集的条件列表。每个条件都是一个三元组,其中包含一个字段名称、一个运算符和一个值。

例如,当在产品模型上使用时,以下域会选择单价超过1000的所有服务:

[('product_type', '=', 'service'), ('unit_price', '>', 1000)]

默认情况下,标准和隐式的组合在一起。逻辑运算符&(AND),|(OR)和!(NOT)可用于显式组合条件。它们被用于前缀位置(运算符插入在其参数之前,而不是参数之间)。例如,选择“服务或单价不在1000到2000之间的产品”:

['|',
    ('product_type', '=', 'service'),
    '!', '&',
        ('unit_price', '>=', 1000),
        ('unit_price', '<', 2000)]

一个domain参数可以被添加到相关的领域,限制有效记录试图选择在客户端界面记录时的关系。

“关系领域领域”练习
在选择Session的讲师时,只有讲师(instructor设置为的合作伙伴True)应该可见。

openacademy/models.py

    duration = fields.Float(digits=(6, 2), help="Duration in days")
    seats = fields.Integer(string="Number of seats")

    instructor_id = fields.Many2one('res.partner', string="Instructor",
        domain=[('instructor', '=', True)])
    course_id = fields.Many2one('openacademy.course',
        ondelete='cascade', string="Course", required=True)
    attendee_ids = fields.Many2many('res.partner', string="Attendees")

声明为文字列表的域在服务器端评估,不能在右侧引用动态值,声明为字符串的域在客户端评估,并允许在右侧使用字段名称

“更复杂的域”练习
创建新的合作伙伴类别“教师/级别1”和“教师/级别2”。会话的讲师可以是任何级别的讲师或老师。

  1. 修改会话模型的域
  2. 进行修改openacademy/view/partner.xml以访问 合作伙伴类别:

openacademy/models.py

    seats = fields.Integer(string="Number of seats")

    instructor_id = fields.Many2one('res.partner', string="Instructor",
        domain=['|', ('instructor', '=', True),
                     ('category_id.name', 'ilike', "Teacher")])
    course_id = fields.Many2one('openacademy.course',
        ondelete='cascade', string="Course", required=True)
    attendee_ids = fields.Many2many('res.partner', string="Attendees")

openacademy/views/partner.xml

                  parent="configuration_menu"
                  action="contact_list_action"/>

        <record model="ir.actions.act_window" id="contact_cat_list_action">
            <field name="name">Contact Tags</field>
            <field name="res_model">res.partner.category</field>
            <field name="view_mode">tree,form</field>
        </record>
        <menuitem id="contact_cat_menu" name="Contact Tags"
                  parent="configuration_menu"
                  action="contact_cat_list_action"/>

        <record model="res.partner.category" id="teacher1">
            <field name="name">Teacher / Level 1</field>
        </record>
        <record model="res.partner.category" id="teacher2">
            <field name="name">Teacher / Level 2</field>
        </record>

</odoo>