odoo框架之模块继承

odoo框架之模块继承

开篇前准备

# 新建模块 library_member
# 目录结构
	library_member
		- models
			- __init__.py
		- views
		- __init__.py
		- __manifest__.py

一.原模型继承

​ in-place模型继承, _inherit 属性

# 原模型继承的作用:
	# 1. 为模型添加字段
    # 2.  修改已有字段
    
# 继承的结果
	# 1. 原模型继承,在原模型上添加修改,
    # 2. 重新指定_name属性,生成新的表

为模型添加字段

# 1.将models导入到library_member/__init__.py文件来
	from . import models
# 2.在library_member/models/__init__.py添加如下代码
	from . import library_book
# 3.创建library_book.py, 继承 library.book模型,
	from odoo import fields, models
	class Book(models.Model):
        # 声明继承,以及要继承的模型
        _inherit = 'library.book' 
        # 添加新的字段
        is_available = fields.Boolean('Is Available?')

修改已有字段

​ 继承模型时,可对已有字段叠加修改,也就是说仅需定义要增加或修改的字段属性。

# 目的:
   #为isbn字段添加一条提示,说明同时支持10位数的 ISBN(稍后会实现该功能)
   #为publisher_id字段添加数据库索引,以提升搜索效率
   
# 步骤
	# 1.编辑library_member/models/library_book.py,加入如下代码
    
class Book(models.Model):
	...
    # 重新定义isbn字段
    isbn = fields.Char(help="Use a valid ISBN-13 or ISBN-10.")
    # index 是添加索引
    publisher_id = fields.Many2one(index=True) 
	

二.修改视图和数据

视图继承

​ 表单、列表和搜索视图通过arch XML结构定义.

​ 定位到xml元素的位置后,对其进行修改. 视图中也存在inherit_id属性

<!-- 添加views/book_view.xml文件来继承 Partner 视图  -->
<?xml version="1.0"?>
<odoo>
    <record id="view_form_book_extend" model="ir.ui.view">
        <field name="name">Book: add Is Available? field</field>
        <field name="model">library.book</field>
        <field name="inherit_id" ref="library_app.view_form_book" />
        <field name="arch" type="xml">
            <field name="isbn" position="after">
                <field name="is_available" />
            </field>
        </field>
    </record>
</odoo>

​ 注解

### 同一个模块下,xml id 不要重复
# 1.  view_form_book_extend 是当前视图的id
# 2.  由于没有生成新的模型所以依旧依赖library.book
# 3.  inherit_id 字段,ref的值是 继承这个视图的xml id

# 4. position 指的是位置
# 5. is_available 是新增字段

节点定位的方式以及节点的位置

# 节点位置
    inside(默认值):# 在所选节点内添加内容,这一节点应是<group>或<page>一类的容器
    
	after:# 在选定节点之后向父节点添加内容
	
	before:# 在选定节点之前向父节点添加内容
	
	replace:# 替换所选节点。若使用空元素则会删除该元素。Odoo 之后还允许使用其它标记来包裹元素,通过在内容中使用$0来表示被替换的元素。
	
	attributes:# 修改匹配元素属性值。内容中应包含带有一个或多个<attribute name=”attr-name”>value</attribute>元素。如<attribute name=”invisible”>True</attribute>,若不带内容,如<attribute name=”invisible” />则 attribute 会从所选元素中删除。
	
	replace # 可删除 XML 元素,但应避免这么做。这么做会破坏其它依赖所删除节点、将其作为占位符添加元素的模块。一个替代方案是,让该元素不可见。
    
	 move #  子元素合并。效果是将子定位符目标节点移到父定位符目录位置。如:<field name="target_field" position="after"><field name="my_field" position="move"/></field>
filed 字段定位节点
<field name="target_field" position="after">
    	....
</field>


<!-- 当name出现重复时,无法精确定位到节点 -->
xpath 定位节点
# 使用xpath的原因
	# 1. 存在没有带唯一值的属性来用作 XML 节点选择器。在所选元素没有 name 属性时可能出现这一情况,如<group>、<notebook>或<page>视图元素。

	# 2. 另外就是有多个带有相同 name 属性的元素,比如在看板 QWeb 视图中相同字段可能在同一 XML 模板中被多次包含。
    
    # 3. xpath也有限制

# 例子:
<xpath expr="//field[@name='isbn']" position="after">
    <field name="is_available" />
</xpath>

修改数据

# 1. 普通数据记录不同于视图,它没有 XML arch 结构,也不能使用 XPath 来进行扩展。但还是可以通过替换字段值来进行修改。

# 2. <record id="x" model="y">数据加载元素实际是对 y 模型进行插入或更新操作。若不存在记录 x,则被创建,否则被更新/覆盖。其它模块中的记录可通过<module>.<identifier>全局标识符访问,因此可以在我们的模块中重写其它模块中已写入的数据

<!-- 我们将 User 安全组的名称修改为 Librarian,对应修改library_app.library_group_user记录。添加library_member/security/library_security.xml并加入如下代码: -->
<odoo>
    <!-- Modify Group name -->
    <record id="library_app.library_group_user" model="res.groups">
        <field name="name">Librarian</field>
    </record>
</odoo>

三.其他模型继承机制

原型继承拷贝功能_inherit

# 即 在继承模型中添加_name属性, 相当于拷贝了一个一模一样的表,添加上新增字段
# 缺点:
	# 1.数据结构冗余

代理继承内嵌模型(_inherits)

​ 定义: UML 中这种称作组合(composition)关系:父类无需子类即可存在,而子类必须要有父类才能存在。

​ 在图书项目中,我们要添加一个图书会员模型。会员有会员卡并通过会员卡借阅读书。我们要记录卡号,还要存储email 和地址这类个人信息。Partner 模型已包含联系和地址信息,所以最好是进行复用,而不去创建重复的数据结构。

# 为会员模型创建library_member/models/library_member.py文件并加入如下代码:
from odoo import fields, models

class Member(models.Model):
    _name = 'library.member'
    _description = 'Library Member'
    card_number = fields.Char()
    partner_id = fields.Many2one(
        'res.partner',
        delegate=True, # 复用users的
        ondelete='cascade',
        required=True)
### 代理继承可通过如下组合来进行替代:

	1.父模型中的一个 many-to-one 字段
	2.重载 create()方法自动创建并设置父级记录
	3.父字段中希望暴露的特定字段的关联字段
 # 有时这比完整的代理继承更为合适。例如res.company并没有继承res.partner,但使用到了其中好几个字段。
# 步骤
# 1. 在library_member/model/__init__.py导入新模型 library_member
	from . import library_book
	from . import library_member
    
# 2.创建library_member/security/ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_member_user,Member User Access,model_library_member,library_app.library_group_user,1,1,1,0
access_member_manager,Member Manager Access,model_library_member,library_app.library_group_manager,1,1,1,1

# 3.创建library_member/views/library_menu.xml
<odoo>
    <act_window id="action_library_member"
        name="Library Members"
        res_model="library.member"
        view_mode="tree,form" />
    <menuitem id="menu_library_member"
        action="action_library_member"
        parent="library_app.menu_library" />
</odoo>
# 4. 创建library_member/views/member_view.xml
<?xml version="1.0" ?>
<odoo>
    <record id="view_form_member" model="ir.ui.view">
        <field name="name">Library Member Form View</field>
        <field name="model">library.member</field>
        <field name="arch" type="xml">
            <form>
                <group>
                    <field name="name" />
                    <field name="email" />
                    <field name="card_number" />
                </group>
            </form>
        </field>
    </record>
    <record id="view_tree_member" model="ir.ui.view">
        <field name="name">Library Member List View</field>
        <field name="model">library.member</field>
        <field name="arch" type="xml">
            <tree>
                <field name="name" />
                <field name="card_number" />
            </tree>
        </field>
    </record>
</odoo>

使用 mixin类继承模型

​ mixin 是基于 models.Abstract 的抽象的模型(而不是models.Model),它在数据库中没有实际的体现,而是提供功能供其它模型复用(混合 mixed in)

​ Odoo 插件提供多种 mixin,最常的两种由 Discuss 应用(mail 模块)提供:

# 1. mail.thread提供在许多文档表单下方或右侧的消息面板功能,以及消息和通知相关逻辑。这在我们自己的模型中将经常会添加,下面就来一起学习下

# 2. mail.activity.mixin模型提供待办任务计划

### mail 模块现在通过mail.activity.mixin抽象模型提供Activities任务管理功能
# 步骤
# 1. 'depends': ['library_app', 'mail'], 添加依赖
# 2. mixin 类的继承通过_inherit属性完成,编辑 library_member.py
	class Member(models.Model):
    _name = 'library.member'
    _description = 'Library Member'
    _inherit = ['mail.thread', 'mail.activity.mixin']
# 3. 编辑 member_view.xml ,form内添加
<form>
	....
	<!-- mail mixin fields -->
	<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>

四.继承方法

​ 与python继承相同

# 在library_member/models/library_book.py文件中添加如下方法:
from odoo import api, fields, models

class Book(models.Model):
    ...
    
    def _check_isbn(self):
        self.ensure_one()
        isbn = self.isbn.replace('-', '')
        digits = [int(x) for x in isbn if x.isdigit()]
        if len(digits) == 10:
            ponderators = [1, 2, 3, 4, 5, 6, 7, 8, 9]
            total = sum(a * b for a, b in zip(digits[:9], ponderators))
            check = total % 11
            return digits[-1] == check
        else:
        	# 执行父类方法
            return super(Book,self)._check_isbn()

五.继承 Web 控制器和模板

继承网页控制器

  • 在控制器端添加对查询参数的支持,访问/library/books?available=1过滤出可借阅图书
  • 在模板端,添加一个图书不可用的表示
from odoo import http
from odoo.addons.library_app.controllers.main import Books

class BookExtended(Books):
    @http.route()
    def list(self, **kwargs):
        response = super().list(**kwargs)
        if kwargs.get('available'):
            Book = http.request.env['library.book']
            # 新增过滤条件 
            books = Book.search([('is_available', '=', True)])
            response.qcontext['books'] = books
        return response
    
# 注意:
	# 它至少需要一个简单的@http.route()装饰器来保持路径活跃。如果不带参数,将会保留父类中定义的路由。但也可以为@http.route() 装饰器添加参数,来重新定义或替换类路由。

继承 QWeb 模板

​ 与视图继承类似,定义继承模板,xpath定位节点

​ 添加library_member/views/book_list_template.xml文件并加入如下代码:

<odoo>
    <template id="book_list_extended"
        name="Extended Book List"
        inherit_id="library_app.book_list_template">
        <xpath expr="//span[@t-field='book.publisher_id']" position="after">
            <t t-if="not book.is_available">
                <b>(Not Available)</b>
            </t>
        </xpath>
    </template>
</odoo>

posted @ 2020-05-14 15:41  染指未来  阅读(604)  评论(0编辑  收藏  举报