odoo16入门教程第十三章 继承

第十三章 继承

Odoo的强大之处在于它的模块化。模块专门用于满足业务需求,但模块也可以彼此交互。这对于扩展现有模块的功能非常有用。例如,在我们的房地产场景中,我们希望在常规用户视图中直接显示销售人员的属性列表。

但是在探索Odoo的模块继承之前,让我们来看看如何修改默认的增删改查的方法。

Python Inheritance

Note

Goal: at the end of this section:

  • 不能删除不是新增 或者取消的订单

Unlink

  • 当一个报价产生后,房产的状态应该变成收到报价。
  • 不能创建比当前最低报价更低的报价

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dRrfjS03-1685864095497)(https://www.odoo.com/documentation/16.0/_images/create.gif)]

在我们的房地产模块中,我们从来没做一些标准的增删改查的操作。 Odoo框架提供了必要的工具来做这些事。 实际上,这些动作已经通过Python类的继承包含在我们的模型中了。

from odoo import fields, models

class TestModel(models.Model):
    _name = "test.model"
    _description = "Test Model"

    ...

我们的TestModel类继承自Model,Model提供了 create(), read(), write() and unlink().

这些方法(以及其他定义在Model中的方法)可以被扩展添加特定的商业逻辑。

from odoo import fields, models

class TestModel(models.Model):
    _name = "test.model"
    _description = "Test Model"

    ...

    @api.model
    def create(self, vals):
        # Do some business logic, modify vals...
        ...
        # Then call super to execute the parent method
        return super().create(vals)

装饰器model()对create()方法是必要的,因为记录集的内容还不存在,self与上下文无关,他是它对其他CRUD方法不是必要的。

It is also important to note that even though we can directly override the unlink() method, you will almost always want to write a new method with the decorator ondelete() instead. Methods marked with this decorator will be called during unlink() and avoids some issues that can occur during uninstalling the model’s module when unlink() is directly overridden.

这段实在不好翻译? 不好意思,保留原文把

在python3中 super()等效于super(TestModel, self),后者有时是必要的,当你对一个修改过的记录集调用父类的方法时。

危险:

  • 很重要的事,永远要调用 super() 来避免打断流程.只有几种少数的情况你不需要调用它。
  • 确定永远要返回跟父类方法一致的数据,例如父类方法返回dict(),你重写的方法也要返回dict()

练习:

在CRUD方法中添加商业逻辑

  • 阻止对状态不是“新增”或者“取消”的房产执行删除操作
@api.ondelete(at_uninstall=False)
def _unlink_check_state(self):
    for rec in self:
        if rec.state in ("1",'2','3'):
            raise odoo.exceptions.UserError("不能删除已经有效的房产")

Tip: 用 ondelete() 装饰器写一个新方法,记住,self是一个记录集

  • 当一个报价产生后,设置房产的属性为 ‘Offer Received’. 并且不能创建比当前最低报价更低的报价
@api.model
def create(self, vals_list):
    property_obj = self.env['estate.property'].browse(vals_list["property_id"])
    list_price = property_obj.offer_ids.mapped("price")
    min_price =  0 if not list_price else min(list_price)
    if float(vals_list["price"])< min_price:
        raise odoo.exceptions.ValidationError("新报价不能低于当前最低价")
    property_obj.state = "1"
    return  super().create(vals_list)

Tip: 注意,property_id 字段在vals里是有效的,不过是一个整数,实例化estate.property 对象, 可以使用self.env[model_name].browse(value) (example)

   self.env['gamification.badge'].browse(vals['badge_id']).check_granting()

Model Inheritance

参考: 关于这个主题的文档在 Inheritance and extension.

在我们的房地产模块中,我们希望将房产列表通过设置->用户和公司->用户的form视图直接连接到销售代表。 要做到这一点,我们需要给res.users增加一个字段并且修改它的视图来显示。

Odoo提供了两种继承机制,以模块化的方式来拓展现存的模块。

第一种继承机制允许模块修改在另一个模块中定义的模型的行为:(同一张数据表)

  • 增加字段
  • 重新模型中字段的定义
  • 在模型中添加约束
  • 在模型中增加方法
  • 覆盖模型中现有的方法

第二种继承机制(委托)允许模型中每一条记录都连接到父模型的记录,并且对父模型记录提供了透明 的访问方法。(父子表)

Inheritance Methods

在odoo中,第一种机制到目前为止用的最多,我们想增加一个字段到现有的模型,意味着我们将使用第一种机制,例如:

from odoo import models,api,fields

class EstateUsers(models.Model):
    _inherit = "res.users"

    property_ids= fields.One2many("estate.property","salesperson")
    # 数据库的res_user表会增加这个字段
    test_field = fields.Char("测试字段")

一般来说,每个继承的模型都定义在自己的python文件中,在我们的例子中,它将在models/inherited_model.py.

练习

  • res.users增加下列字段:
FieldType
property_idsOne2many inverse of user_id to estate.property
  • 给字段增加一个domain让它只显示有效的房产
from odoo import models,api,fields

class EstateUsers(models.Model):
    _inherit = "res.users"

    property_ids= fields.One2many("estate.property","salesperson")
    test_field = fields.Char("测试字段")

下一小节让我们在视图中增加字段,并检查是否所有事情都工作的很好

View Inheritance

参考: 关于这个主题的文档在 Inheritance.

Note

Goal: 在这一小节结尾,用户的form视图将会连接到他所出售的房产

Users

Odoo没有修改现有视图(通过覆盖它们),而是提供了视图继承,其中子“扩展”视图应用于根视图的顶部。这些扩展既可以从父视图中添加内容,也可以从父视图中删除内容。

扩展视图通过inherit_id字段来引用父视图,相比单个视图,它的arch字段包含了几个xpath元素用来选择并修改父视图中的内容。

<record id="inherited_model_view_form" model="ir.ui.view">
    <field name="name">inherited.model.form.inherit.test</field>
    <field name="model">inherited.model</field>
    <field name="inherit_id" ref="inherited.inherited_model_view_form"/>
    <field name="arch" type="xml">
        <!-- find field description and add the field
             new_field after it -->
        <xpath expr="//field[@name='description']" position="after">
          <field name="new_field"/>
        </xpath>
    </field>
</record>
  • expr

    一个XPath表达式,用来选择父视图中的单个元素,如果匹配不到元素或者匹配到多个元素会引发错误。

  • position

    应用到匹配到的元素的操作:

    inside: 追加到元素body的末尾

    replace: 代替匹配的元素

    before: 作为兄弟节点插入到匹配元素前面

    after: 作为兄弟节点插入到匹配元素后面

当匹配到单个元素,position属性可以直接设置在匹配的元素上,下面两种方式的效果是一样的

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

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

这第二种不是更简单??!!

这里有一个视图继承的案例 here.

练习:

给用户视图增加字段

在base.view_users_form中新建一个notebook page 加入property_ids字段

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

<odoo>
    <data>

        <record id="res_users_view_form" model="ir.ui.view">
            <field name="name">res.users.view.form.inherit.estate</field>
            <field name="model">res.users</field>
            <field name="inherit_id" ref="base.view_users_form"/>
            <field name="arch" type="xml">
                <xpath expr="//page[@name='access_rights']" position="after">
                    <page string="销售记录">
                        <field name="property_ids"/>
                    </page>
                </xpath>
            </field>
        </record>
    </data>
</odoo>

Tip: 一个继承user视图的例子在这里 here.

继承由于其模块化的概念在Odoo中被广泛使用。请不要犹豫,阅读相应的文档以获取更多信息!

下一章,我们来学习如何跟其他模块交互。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值