odoo 开发入门教程系列-继承(Inheritance)
继承(Inheritance)
Odoo的一个强大方面是它的模块化。模块专用于业务需求,但模块也可以相互交互。这对于扩展现有模块的功能非常有用。例如,在我们的房地产场景中,我们希望在常规用户视图中直接显示销售人员的财产列表。
在介绍特定的Odoo模块继承之前,让我们看看如何更改标准CRUD(创建、检索,更新或删除)方法的行为
Python继承(Python Inheritance)
目标:
不能删除状态不为New、Canceled的房产
预期效果动画地址:https://www.odoo.com/documentation/14.0/zh_CN/_images/unlink.gif
房产收到报价时,房产状态应该改成‘Offer Received’
不能以低于现有报价的价格创建报价
预期效果动画地址:https://www.odoo.com/documentation/14.0/zh_CN/_images/create.gif
在我们的房地产模块中,我们从不需要开发任何特定的东西来执行标准的CRUD操作。Odoo框架提供了实现这些操作的必要工具。事实上,多亏经典的Python继承,我们的模型中已经包含了这样的操作:
from odoo import fields, models
class TestModel(models.Model):
_name = "test.model"
_description = "Test Model"
...
我们的 TestModel
类继承与Model
,该Model
类提供了 create()
, read()
, write()
和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
的内容和创建(creation)的上下文无关,但该装饰器对于其它CRUD方法来说不是必需的。
Python 3中, super()
等价于 super(TestModel, self)
。当你需要使用一条被修改后的结果集调用父方法时,可能需要使用后者。
危险提示
- 总是调用
super()
以避免中断流非常重要。只有少数非常特殊的情况才无需调用它。- 总是返回和父方法一致的数据。例如父方法返回一个
dict()
,你重写父方法时也要返回一个dict()
练习--添加业务逻辑到CRUD方法
- 如果房产记录状态不是
New
,Canceled
,则不让删除
提示:重写unlink()
,并记住self
可以是一个包含多条记录的结果集。
- 创建报价时,设置房产状态为‘Offer Received’,如果用户试图以低于已存在报价的金额创建报价时抛出错误。
提示: 可在vals
中获取property_id
字段,但是它是一个int
型。要实例化一个estate.property
对象,请使用self.env[model_name].browse(value)
(示例)
@api.model
def create(self, vals):
self.env['gamification.badge'].browse(vals['badge_id']).check_granting()
return super(BadgeUser, self).create(vals)
修改odoo14\custom\estate\views\estate_property_views.xml
去掉estate_property_view_tree
中<tree>
元素的editable="top"
属性(说明:为了方便执行报价创建操作)
修改odoo14\custom\estate\models\estate_property.py
@api.constrains('selling_price', 'expected_price')
def _check_selling_price(self):
# if record.selling_price < self.expected_price * 0.9:
# raise ValidationError("selling price can`t not lower then 90 percent of expected price")
pass
说明:为了方便实践操作,暂且不做售价校验
最末尾新增以下代码
def unlink(self):
for record in self:
if record.state not in ['New', 'Canceled']:
raise UserError('can`t delete property which status is New or Canceled')
return super().unlink()
修改odoo14\custom\estate\models\estate_property_offer.py
,导入UserError
from odoo.exceptions import UserError
最末尾添加一下代码
@api.model
def create(self, vals):
property = self.env['estate.property'].browse(vals['property_id'])
if vals.get('price') < property.best_price:
raise UserError('不能低于现有报价')
property.state = 'Offer Received'
return super().create(vals)
重启服务,刷新浏览器验证
删除非New
、Canceled
状态的房产,提示如下:
模块继承(Model Inheritance)
引用: 查看主题相关文档继承和扩展
我们希望在“Settings/Users & Companies/Users”表单视图中直接显示与销售人员关联的房产列表。为此,我们需要向res.users
模型添加一个字段,并调整其视图以显示它。
Odoo提供了两种继承机制来以模块化的方式扩展现有模型。
第一继承机制允许模块通过以下方式修改在另一个模块中定义的模型的行为:
向模型添加字段
覆盖模型中字段的定义
给模型添加约束
给模型添加方法
重写模型中的现有方法
第二种继承机制(委托)允许将模型的每个记录链接到父模型的记录,并提供对该父记录的字段的透明访问。
odoo中,第一种机制最常用。在我们的例子中,我们希望向现有模型添加一个字段,这意味着我们将使用第一种机制。例如:
from odoo import fields, models
class InheritedModel(models.Model):
_inherit = "inherited.model"
new_field = fields.Char(string="New Field")
这里可以找到将两个字段添加到模型中的示例
class AccountMoveLine(models.Model):
_inherit = 'account.move.line'
vehicle_id = fields.Many2one('fleet.vehicle', string='Vehicle')
need_vehicle = fields.Boolean(compute='_compute_need_vehicle',
help="Technical field to decide whether the vehicle_id field is editable")
def _compute_need_vehicle(self):
self.need_vehicle = False
按照惯例,每个继承的模型都在其自己的Python文件中定义。在我们的示例中为“models/inherited_model.py”。
练习--添加字段到用户模型
- 添加一下字段到
res.users
:
Field | Type |
---|---|
property_ids | One2many inverse of salesman_id to estate.property |
- 添加一个
domain
到该字段,这样以便仅显示可获取房产。
新增odoo14\custom\estate\models\estate_res_user.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from odoo import models, fields
class EstateResUser(models.Model):
_inherit = 'res.users'
property_ids = fields.One2many('estate.property', 'salesman_id', domain="[('salesman_id', '=', active_id)]")
修改odoo14\custom\estate\models\__init__.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
from . import estate_property
from . import estate_res_user # 本次新增
视图继承(View Inheritance)
参考: 主题关联文档可查看Inheritance.
目标: 在用户表单视图中显示与销售人员关联的avaliable房产列表其用户表单视图
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
将
xpath
的主体附加到匹配元素的末尾(个人理解,添加为匹配元素的子元素)replace
将匹配元素替换为
xpath
的主体,将新主体中出现的任何$0
节点替换为原始元素before
在匹配元素之前插入
xpath
的主体作为同级元素after
在匹配的元素之后插入
xpaths
的主体,作为同级元素attributes
使用
xpath
主体中的特定属性元素更改匹配元素的属性
当匹配单个元素时,可以直接在要查找的元素上设置position
属性。以下两种继承都有相同的结果
<xpath expr="//field[@name='description']" position="after">
<field name="idea_ids" />
</xpath>
<field name="description" position="after">
<field name="idea_ids" />
</field>
在这里可以找到视图继承扩展的示例
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<record id="view_move_form" model="ir.ui.view">
<field name="name">account.move.form</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='line_ids']//field[@name='account_id']" position="after">
<field name='need_vehicle' invisible='1'/>
<field name='vehicle_id' attrs="{'required': [('need_vehicle', '=', True), ('parent.move_type', '=', 'in_invoice')], 'column_invisible': [('parent.move_type', '!=', 'in_invoice')]}" optional='hidden'/>
</xpath>
<xpath expr="//field[@name='invoice_line_ids']//field[@name='account_id']" position="after">
<field name='need_vehicle' invisible='1'/>
<field name='vehicle_id' attrs="{'required': [('need_vehicle', '=', True), ('parent.move_type', '=', 'in_invoice')], 'column_invisible': [('parent.move_type', '!=', 'in_invoice')]}" optional='hidden'/>
</xpath>
</field>
</record>
</odoo>
练习--添加字段到用户视图
添加property_ids
字段到 base.view_users_form
中新建的notebook
页
提示: 可以在 这里找到继承用户视图的示例。
<?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.gamification</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<group name="messaging" position="inside">
<field name="karma"/>
</group>
</field>
</record>
</data>
</odoo>
新增odoo14\custom\estate\views\estate_res_users_views.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="estate_res_users_view_form" model="ir.ui.view">
<field name="name">estate.res.users.view.form</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='references']" position="after">
<page string="Real Estate Properties" name="RealEstateProperties">
<field name='property_ids'/>
</page>
</xpath>
</field>
</record>
</data>
</odoo>
修改odoo14\custom\estate\__manifest__.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
{
'name': 'estate',
'depends': ['base'],
'data':['security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
'views/estate_property_offer_views.xml',
'views/estate_menus.xml',
'views/estate_res_users_views.xml' # 本次新增
]
}
重启服务,验证效果
odoo 开发入门教程系列-继承(Inheritance)的更多相关文章
- WPF入门教程系列(一) 创建你的第一个WPF项目
WPF入门教程系列(一) 创建你的第一个WPF项目 WPF基础知识 快速学习绝不是从零学起的,良好的基础是快速入手的关键,下面先为大家摞列以下自己总结的学习WPF的几点基础知识: 1) C#基础语法知 ...
- WPF入门教程系列三——Application介绍(续)
接上文WPF入门教程系列二——Application介绍,我们继续来学习Application 三.WPF应用程序的关闭 WPF应用程序的关闭只有在应用程序的 Shutdown 方法被调用时,应用程序 ...
- 基于Nodejs生态圈的TypeScript+React开发入门教程
基于Nodejs生态圈的TypeScript+React开发入门教程 概述 本教程旨在为基于Nodejs npm生态圈的前端程序开发提供入门讲解. Nodejs是什么 Nodejs是一个高性能Ja ...
- WPF入门教程系列(二) 深入剖析WPF Binding的使用方法
WPF入门教程系列(二) 深入剖析WPF Binding的使用方法 同一个对象(特指System.Windows.DependencyObject的子类)的同一种属性(特指DependencyProp ...
- Android Studio JNI开发入门教程
Android Studio JNI开发入门教程 2016-08-29 14:38 3269人阅读 评论(0) 收藏 举报 分类: JNI(3) 目录(?)[+] 概述 在Andorid ...
- SeaJS入门教程系列之使用SeaJS(二)
SeaJS入门教程系列之使用SeaJS(二) 作者: 字体:[增加 减小] 类型:转载 时间:2014-03-03我要评论 这篇文章主要介绍了SeaJS入门教程系列之使用SeaJS,着重介绍了SeaJ ...
- 移动H5开发入门教程:12点webAPP前端开发经验
如果你是一名移动H5前端开发人员,25学堂的小编认为下面的分享的12点webAPP前端开发经验是你必须掌握的基础知识点.算是一篇移动H5开发入门教程吧! 1. viewport:也就是可视区域.对于桌 ...
- C#,ArcGIS Engine开发入门教程
C#,ArcGIS Engine开发入门教程 转自:http://blog.csdn.net/yanleigis/article/details/2233674 目录(?)[+] 五实现 一 加载A ...
- Silverlight,Windows 8应用开发实例教程系列汇总
Kevin Fan分享开发经验,记录开发点滴 Silverlight,Windows 8应用开发实例教程系列汇总 2012-06-18 01:05 by jv9, 2145 阅读, 3 评论, 收藏, ...
- ActiveMQ详细入门教程系列(一)
一.什么是消息中间件 两个系统或两个客户端之间进行消息传送,利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成.通过提供消息传递和消息排队模型,它可以在分布式环境下 ...
随机推荐
- 项目:表格打印(字符图网格进阶、rjust、列表中最长的字符串长度)
项目要求:编写一个名为 printTable()的函数,它接受字符串的列表的列表,将它显示在组织良好的表格中,每列右对齐. tableData = [['apples', 'oranges', 'ch ...
- 字符流---->字符过滤流 缓冲流 : -----> printWrite用法:和BufferedReader用法
printWrite用法:1.创建字符节点流FileWriter fw = new FileWriter("Files\\bufchar.txt");2创建字符过滤流 PrintW ...
- DNS解析原理(www.baidu.com)
QueryDns,py程序运行问题解决 关于远程访问数据库问题 这个我用NAVICAT或者是python程序连接都连不上他那个数据库(可能是数据库设定的权限没有开启?) 这个程序真的跑不起来,考虑自己 ...
- 基于Nginx以及web服务器搭建在线视频播放
安装Nginx Nginx官网下载地址 网址打开后如图 下载windows版本的Nginx,这里下载最新的1.18.0版本 Nginx在windows下的安装只需要将其解压缩即可.建议将解压后的目录移 ...
- rust 条件编译 Debug Release
#[cfg(debug_assertions)] macro_rules! debug { () => (std::println!()); ($($arg:tt)*) => ({ pri ...
- 12、jmeter逻辑控制器-临界区控制器
临界区:说白了就是不并发了 一个个的像独木桥 使用场景:比如提交一个数据 需要一个一个的提交 一个个的改 在数据库改操作的时候 需要用到 临界区控制器 案例:临界区控制器
- Asp.net zero项目框架和配置
- 例题1:shell脚本
题目总结: 1.搜索子域名的shell脚本 2.嗅探并抓去网页快照shell脚本 3.漏洞利用程序下载脚本 题目一:依次输入以下代码即可: 1.wget www.megacorpone.com 2. ...
- sql函数的用法
1.codename 作为翻译函数 CREATE DEFINER=`root`@`localhost` FUNCTION `codename`(`sys_code` varchar(20),`stat ...
- Markdown操作方法
Markdown学习 标题 三级标题 四级标题 字体 原本 hello,world! 斜体 hello,world! 加粗 hello,world! 斜体加粗 hello,world! 删除 hell ...