文章目录
1.1 同级目录下创建store_member目录作为school_store的扩展模块
1.2 在根目录下的__manifest__.py文件中指明依赖模块
在Odoo中,继承是一种重要的开发概念,它允许你修改或扩展现有模型(Model)、视图(View)或控制器(Controller)而无需修改原始代码。
- 修改现有模型的行为: 通过继承现有模型,你可以修改模型的方法、添加新字段、更改默认值等,而无需修改原始模型的代码。这使得在不破坏现有功能的情况下进行定制变得更加容易。
- 添加新字段: 继承可以用于在现有模型中添加新的字段。这使得你能够将自定义数据集成到标准模型中,以满足特定需求。
- 修改视图: 通过继承现有视图,你可以修改视图的结构、添加新的元素,或者重定义现有元素的行为。这使得你能够调整用户界面以满足定制需求。
- 重载控制器方法: 在Odoo中,你可以通过继承控制器并重载其方法来修改现有业务逻辑。这使得你能够自定义处理特定请求的方式。
一、模型继承
1、模块继承前的准备工作
1.1 同级目录下创建store_member目录作为school_store的扩展模块
使用scaffold 来快速创建目录骨架
odoo scaffold <module_name> <path_to_target_dir>
1.2 在根目录下的__manifest__.py文件中指明依赖模块
使用depends来指明依赖模块
{
'name':'Store Members',
'description':'是否为可用',
'author':'lizewen',
'depends':['school_store'],
'application':False,
'data': [],
}
2、经典继承
只使用 _inherit 属性,可对模型执行有效的更改。
可以看作是在父类模型中插入了一个扩展。
from odoo import fields,models
class Member(models.Model):
_inherit='school.store'
is_available=fields.Boolean('Is Available?')
2.1 继承父类模型的方法
继承模型时,可对已有字段做出增量修改。也就是只需要定义要修改或添加的属性。
在继承类中继承方法,我们要使用相同方法名重新定义该方法。
使用super()来调用父类已实现的方法
在这个列子中,调用了父类的_check_mem()方法
super()._check_mem()
self.ensure_one()方法介绍:
用于确保当前对象集合只包含一个记录的方法。这个方法通常在Odoo的模型(Model)中的方法中使用,主要用于执行一些需要确保只有一个记录存在的操作。
主要应用场景:
- 检索唯一记录: 当一个方法需要检索模型中的唯一一条记录时,可以使用
self.ensure_one()
。如果存在多条记录,将引发异常。
def get_unique_record(self):
self.ensure_one()
# 执行获取唯一记录的操作
- 执行唯一记录的操作: 当一个方法执行的操作只适用于唯一一条记录时,可以使用
self.ensure_one()
来确保只有一个记录存在
def process_unique_record(self):
self.ensure_one()
# 执行仅适用于唯一记录的操作
- 验证唯一性: 在一些情况下,需要验证某些字段的唯一性,确保不会存在重复值。在这种情况下,可以使用
self.ensure_one()
来确保只有一个记录存在
@api.constrains('field_name')
def _check_unique_field(self):
self.ensure_one()
# 执行验证唯一字段的逻辑
2.2 使用修改的方式来继承父类的视图
inherit_id属性来引用所要继承 的视图。
通过ref属性指向视图 的外部标识符定位所继承的视图。
使用position=”after”来声明位置。
具体实现模板:
ref:这里需要填写视图的ID
视图ID如何查看:技术-用户界面-视图
在after后面的也就是修改的字段,在模型中被定义的字段
<odoo>
<record id="view_form_book_extend" model='ir.ui.view'>
<field name="name">经典继承之视图继承</field>
<field name="model">school.store</field>
<field name="inherit_id" ref="这里写视图的XMLID" />
<field name="arch" type="xml">
<field name="name" position="after">
<field name="is_available" />
</field>
</field>
</record>
</odoo>
3、原型继承
使用 _inherit 和 _name 属性。
此时你会获得一份继承模型的副本
这个新模型只是有自己的功能以及父类的功能,并且自己的功能不会添加到父类中
新模型与父类模型相互独立。
不受父类模型修改的影响。
有自己独立的数据表。
from odoo import fields,models
class Member(models.Model):
_name = 'store.member'
_inherit='school.store'
is_available=fields.Boolean('Is Available?')
4、代理继承
代理继承属于内嵌模型
创建新模型记录也会创建并连接所代理的模型记录
继承模型中不存在的代理字段模型可进行读写操作,类似于关联的计算字段
通过代理机制,嵌套模型的所有字段像父模型字段一样自动可用
代理继承仅用于数据层面,不适用于逻辑层。
没有继承所继承模型的任意方法,但可以使用点号来访问,成为点号标记。
在实际开发中,一般会偏向代理继承,因为不拷贝就可以复用数据结构。
这个例子中在store.member中嵌入了res.partner,在新建记录时,会自动创建一个关联的partner而且通过该字段引用
from odoo import fields,models
class menber(models.Model):
_name='store.member'
_description='Store Member'
partner_id=fields.Many2one(
'res.partner',
delegate=True,
ondelete='cascade',
required=True)
5、mixin类
面对多个父类模型继承时,_inherit的值不是单个名字,而是一个模型列表
这可以用于多个模型混合加入一个模型,这样我们可以多次使用同一模型的功能
而mixin类广泛使用了这种模式。
mixin类像是一种功能的容器,可以复用、实现通用功能,可添加到其他模型中,一般不单独使用
mixin类是基于models.AbstractModel的抽象模型。
在源代码中搜索models.AbstractModel就可以找到。
常用的mixin:
mail.thread:
为模型添加了跟踪记录和关注者管理功能,可以在模型中跟踪记录、添加关注者,并发送电子邮件通知给关注者。
mail.activity.mixin
该 Mixin 为模型添加了待办事项功能,可以在模型中创建、更新和完成待办事项,并发送电子邮件通知给相关用户。
website.published.mixin
该 Mixin 为模型添加了网站发布功能,可以在网站上发布模型记录,并提供 SEO 和社交分享功能。
sale.order.line.make.procurement.mixin
该 Mixin 为销售订单行添加了自动生成采购需求的功能,可以在销售订单行中创建与其相关的采购需求。
product.template.sale.mixin
该 Mixin 为产品模板添加了销售信息,可以在产品模板中定义销售价格、定价策略和相关产品等。
5.1 对模型添加消息聊天窗口和计划活动
步骤:
- 添加提供mixin的插件模型依赖,即mail
在__manifest__.py 文件添加对mail插件的依赖
"depends":["mail"],
- 继承mail.thread和mail.activity.mixin这两个mixin类
在模型文件中继承mixin类
class Member(models.Model):
_name ='store.member'
_description ='Store Member'
_inherit =["mail.thread","mail.activity.mixin"]
- 在表单视图中添加字段
<odoo>
<record id="view_form_member" model="ir.ui.view">
<field name="name">Store Member Form View </field>
<field name="model">store.member</field>
<field name="arch" type="xml">
<form>
<header>
</header>
<sheet>
<group>
<group>
</group>
</group>
</sheet>
<!-- mail mixin -->
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers" />
<field name="activity_ids" widget="mail_activity" />
<field name="message_ids" widget="mail_thread" />
</div>
</form>
</field>
</record>
</odoo>
<record id="view_sale_order_form_inherit" model="ir.ui.view">
<field name="name">sale.order.form.inherit</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<field name="partner_id" position="attributes">
<attribute name="string">New Partner Label</attribute>
</field>
</field>
</record>
二、视图和数据继承
1、视图继承
继承视图,我们需要定位到所要继承的节点, 然后声明所做的修改。
除string属性外的任意XML元素和属性均可用于选取继承点使用的节点,字符串属性会在生成视图期间翻译成用户所使用的语言,因此不能作为节点选择器。
position
属性是在视图继承过程中用来指定插入或替换位置的关键属性。position
属性告诉Odoo在目标视图中的哪个位置进行插入、替换或删除元素。它通常与 <field>
标签或其他视图元素一起使用。
用position属性声明继承操作,参数如下:
inside:在所选节点内添加内容,这一节点应是<group>或<page>一类的容器
after:在选定节点之后向父节点添加内容
before:在选定节点之前向父节点添加内容
replace:替换所选节点
attributes:修改匹配元素的属性值
move:效果是将子定位符目标节点移到父定位符的目标位置
<record id="view_sale_order_form_inherit" model="ir.ui.view">
<field name="name">sale.order.form.inherit</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<field name="partner_id" position="attributes">
<attribute name="string">New Partner Label</attribute>
</field>
</field>
</record>
1.1 使用XPath选取继承点
XPath(XML Path Language)是一种用于在XML文档中选择节点的查询语言。在Odoo中,XML视图继承时,XPath常用于选择继承点(被继承的目标)和定义修改的位置。
- 选择整个视图:
如果要继承整个视图,可以使用 /
来选择根节点。
<xpath expr="/" position="attributes">
<!-- Your modifications here -->
</xpath>
- 选择具体的字段或元素:
使用 //
或 /path/to/element
来选择具体的字段或元素。
<xpath expr="//field[@name='partner_id']" position="after">
<!-- Your modifications here -->
</xpath>
或
<xpath expr="/form/sheet/field[@name='partner_id']" position="after">
<!-- Your modifications here -->
</xpath>
- 选择特定位置:
使用 position
属性来指定插入、替换或删除的位置
<xpath expr="//field[@name='partner_id']" position="after">
<!-- Your modifications here -->
</xpath>
<xpath expr="//field[@name='amount_untaxed']" position="attributes">
<!-- Your modifications here -->
</xpath>
<xpath expr="//field[@name='carrier_id']" position="replace">
<!-- Your modifications here -->
</xpath>
注:
如果XPath表达式匹配到了多个元素,仅会选取第一个作为扩展目标。所以表达式应越精确 越好,使用唯一属性。name属性最易于确保找到精确元素作为扩展点。因此在创建视图XML元素时添加唯一标识符就非常重要。
1.2 xpath语法规则
节点选择:
/
: 从文档的根节点开始选择节点。//
: 选择节点的任意后代节点。.
: 选择当前节点。..
: 选择当前节点的父节点。节点过滤:
[@attribute='value']
: 通过属性值过滤节点。
- 例如:
//book[@category='fiction']
选择所有category
属性为fiction
的book
元素。[position()]
: 选择具有特定位置的节点。
- 例如:
//book[position()=1]
选择文档中第一个book
元素。通配符:
*
: 匹配任意元素节点。@*
: 匹配任意属性节点。轴(Axis):
ancestor
: 选择所有祖先节点。parent
: 选择父节点。child
: 选择所有子节点。descendant
: 选择所有后代节点。following
: 选择当前节点之后的所有节点。preceding
: 选择当前节点之前的所有节点。运算符:
and
: 逻辑与。or
: 逻辑或。not
: 逻辑非。位置路径:
/path/to/element
: 指定节点的路径。
- 例如:
/bookstore/book/title
选择文档中所有bookstore
元素下的book
元素下的title
元素。函数:
name()
: 返回当前节点的名称。text()
: 返回当前节点的文本内容。contains(string, substring)
: 检查一个字符串是否包含另一个字符串。
- 例如:
//book[contains(@title, 'XML')]
选择所有title
属性中包含字符串'XML'的book
元素。数字位置:
[position()=1]
: 选择具有特定位置的节点。[last()]
: 选择最后一个节点。
2、数据继承
普通数据记录也可被继承,在实际应用,通常是重写已有值。
这时我们只需定位到需写入的记录,以及更新的字段和值。无需使用XPath表达式,因为我们并不是像对视图那样修改XML arch结构。
具体的继承结构为:
<record id="x" model="y">
数据加载元素执行对y模型的插入或更新操作:若不存在记 录x,则创建,否则被更新/覆盖。
例子:
对权限组修改名称
<odoo>
<!-- 修改权限组名称 -->
<record id="school_store.school_store_user" model="res.groups">
<field name="name">storeAdmin</field>
</record>
</odoo>
三、网页继承
1、继承网页控制器
什么是网页控制器
网页控制器(Web Controller)是一种用于处理Web请求和生成Web响应的机制。Web控制器允许你创建自定义的Web页面、处理用户输入、执行业务逻辑,并返回渲染后的Web页面或JSON响应。应关注展示逻辑,不处理业务逻辑,业务逻 辑在模型方法中处理。
最基本的网页控制器示例:
from odoo import http
class MyWebController(http.Controller):
@http.route('/my/custom/page', auth='public', website=True)
def my_custom_page(self, **kwargs):
return http.request.render('my_module.my_template')
MyWebController
类继承自http.Controller
。@http.route
装饰器用于定义Web路由。/my/custom/page
: 这是Web页面的URL路径。auth='public'
: 指定该页面是公开访问的,无需身份验证。website=True
: 表示这是一个网站页面。
my_custom_page
方法是处理Web请求的方法。**kwargs
: 允许接收任意数量的关键字参数。return http.request.render('my_module.my_template')
: 返回一个由模板渲染的Web响应。
如何继承网页控制器:
你需要继承哪个控制器你就需要将其的controllers.main引入
红色部分为默认写法不用动,你只需要将黑色部分换成其模型的文件目录,紫色为所取得名字
from odoo.addons.website_sale.controllers.main import WebsiteSale
from odoo import http
from odoo.addons.website_sale.controllers.main import WebsiteSale
class CustomWebsiteSale(WebsiteSale):
@http.route(['/shop/custom_page'], type='http', auth="public", website=True)
def custom_page(self, **kwargs):
return http.request.render('your_module.custom_template')
注:
需要在根目录下得__init__.py文件中引入controllers以及models
需要在controllers的目录下的__init__.py文件中引入main
2、继承QWeb模板
QWeb(Quick Web)是Odoo中的一种模板引擎,用于在服务器端生成动态的网页内容。QWeb采用类似于XML的语法,并与Odoo的模型、视图、控制器等其他组件深度集成,使得开发者可以方便地创建和管理Web界面。
以下是QWeb的一些主要特点和用法:
XML语法:
- QWeb使用XML语法,这种语法旨在使模板易于阅读和理解。
- 例如:
<div t-if="condition">Content to display if condition is true</div>
模板标签:
- QWeb引入了一系列模板标签,允许开发者在模板中执行逻辑、迭代、条件渲染等操作。
- 例如:
<t t-foreach="records" t-as="record">
<div t-field="record.field_name"/>
</t>
条件渲染:
- 使用
t-if
和t-else
可以在模板中实现条件渲染。 - 例如
<div t-if="condition">Content to display if condition is true</div>
<div t-else>Content to display if condition is false</div>
循环迭代:
- 使用
t-foreach
可以在模板中对列表或集合进行迭代。 - 例如:
<t t-foreach="records" t-as="record">
<div t-field="record.field_name"/>
</t>
动态内容:
- 使用
t-esc
可以在模板中显示动态计算的内容。 - 例如:
<p>Hello <t t-esc="user.name"/></p>
模板继承:
- QWeb支持模板的继承,允许开发者创建一个基础模板,然后在子模板中继承并扩展它。
- 例如:
<t t-extend="website.layout">
<t t-append="website.content">
<!-- Additional content for the website layout -->
</t>
</t>
与模型字段的集成:
- QWeb可以与Odoo的模型字段深度集成,通过
t-field
或t-options
等属性直接与模型字段进行交互。 - 例如:
<input t-field="record.field_name"/>
模板注释:
- 使用
<!-- ... -->
注释可以在模板中添加注释。 - 例如:
<!-- This is a comment -->
如何继承QWeb模板
QWeb继承是一个<template>元素,使用额外inherit_id属性来标识待继承的QWeb模板
<!-- views.xml -->
<template id="view_id" name="View Name" inherit_id="your_module.base_template">
<t t-extend="your_module.child_template"/>
</template>