【odoo14】第四章、应用模型
由于本章有包含很多基础知识,个人不会全部转化为自己的语言。直接机器翻译了(用斜体标注,机器翻译反而一字不落,我会过滤掉冗余的内容),虽然机翻,但会保证意思不会偏。
本章主要章节如下:
- 定义模型展示及顺序
- 添加字段
- 配置带有小数点精度的float型字段
- 添加货币字段
- 添加关联字段
- 添加层级关系
- 添加模型约束
- 添加字段字段
- 在其他模型中调用关联字段
- 使用引用调用关联信息
- 使用继承添加特性
- 继承抽象模型复用模型特性
- 使用委托继承完整继承另一个模型
定义模型展示及顺序
模型具有定义其行为的结构属性。它们的前缀是
下划线。模型最重要的属性是_name,因为它定义了内部全局标识符。在内部,Odoo使用_name属性来创建一个数据库表。例如,如果您提供library.book,则Odoo ORM将在数据库中创建library_book表。这就是为什么在Odoo中,_name属性必须是唯一的。
- _rec_name: 以模型为单位展示时,模型展示的field字段。
- _order: 记录在展示时候的排序
准备
步骤
定义模型文件models/library_book.py
- 添加模型的描述
_description = 'Library Book'
- 添加排序
_order = 'date_release desc, name'
- 使用short_name作为模型的默认展示字段
_rec_name = 'short_name'
short_name = fields.Char('Short Title', required=True)
- 在form中添加shot_name字段
<field name="short_name"/>
完整的library_book.py文件如下:
from odoo import models, fields
class LibraryBook(models.Model):
_name = 'library.book'
_description = 'Library Book'
_order = 'date_release desc, name'
_rec_name = 'short_name'
name = fields.Char('Title', required=True)
short_name = fields.Char('Short Title', required=True)
date_release = fields.Date('Release Date')
author_ids = fields.Many2many('res.partner', string='Authors')
完整的form视图内容如下:
<form>
<group>
<group>
<field name="name"/>
tags"/>
<field name="author_ids" widget="many2many_
</group>
<group>
<field name="short_name"/>
<field name="date_release"/>
</group>
</group>
</form>
通过UI更新模块
或者通过命令行升级
python3 odoo-bin -u my_library
原理
第一步是为模型的定义添加一个更加用户友好的标题。这不是强制性的,但可以由某些附加组件使用。例如,在创建新记录时,邮件附加模块中的跟踪功能将其用于通知文本。有关更多详细信息,请参阅第23章,在Odoo中管理电子邮件。如果您不使用模型的描述,在这种情况下,Odoo将在日志中显示警告。
默认情况下,Odoo使用内部id值(自动生成的主键)对记录进行排序。但是,这可以更改,这样我们就可以使用我们选择的字段,方法是提供一个带有字符串的_order属性,该字符串包含以逗号分隔的字段名列表。
字段名后面可以跟desc关键字,以便按降序排序。
小贴士
只有存储在数据库中的field才能进行排序,对于computed的字段是不支持排序的。
_order是缩减版的SQL ORDER BY,他不支持NULLS FIRST等。
模型记录从其他记录引用时使用表示。例如,值为1的用户标识字段表示管理员用户。在窗体视图中显示时,Odoo将显示用户名,而不是数据库ID。简而言之,_rec_name是Odoo GUI用来表示该记录的记录的显示名称。默认情况下,使用名称字段。实际上,这是_rec_name属性的默认值,这就是为什么在我们的模型中有一个name字段比较方便的原因。在我们的例子中图书馆.bookmodel有一个name字段,因此,默认情况下,Odoo将使用它作为显示名。我们想在步骤3中更改此行为;我们使用了short_name作为_rec_name。之后,library.book模型的显示名从form视图的名称name改为short_name,Odoo GUI将使用short_name的值来表示记录。
警告
若模型中没有name字段也没有配置_rec_name,那么将展示记录的模型名称及记录ID(library.book, 1)
我们新增了short_name的字段,其实是在数据库表中新增了一列,同时我们需要在视图中展示相应的字段。
更多
自odoo8之后,计算字段magic_display字段被默认添加到模型中。他的值是通过nage_get()的模型方法生成的。
name_get()方法默认是通过_rec_name属性去生成展示的名称的。如果你想自己实现展示的名称,可以重写name_get()函数。函数必须返回包含由记录ID和Unicode字符串组成的元组的列表。
举例:
def name_get(self):
result = []
for record in self:
rec_name = "%s (%s)" % (record.name, record.date_ release)
result.append((record.id, rec_name))
return result
添加数据字段
准备
步骤
还是my_library模块为例,models/library_book.py定义了基本的模型。
- 增量代码如下
from odoo import models, fields
class LibraryBook(models.Model):
# ...
short_name = fields.Char('Short Title')
notes = fields.Text('Internal Notes')
state = fields.Selection(
[('draft', 'Not Available'),
('available', 'Available'),
('lost', 'Lost')],
'State')
description = fields.Html('Description')
cover = fields.Binary('Book Cover')
out_of_print = fields.Boolean('Out of Print?')
date_release = fields.Date('Release Date')
date_updated = fields.Datetime('Last Updated')
pages = fields.Integer('Number of Pages')
reader_rating = fields.Float(
'Reader Average Rating',
digits=(14, 4), # Optional precision decimals,
)
- 添加对应的视图
<form>
<group>
<group>
<field name="name"/>
tags"/>
<field name="author_ids" widget="many2many_
<field name="state"/>
<field name="pages"/>
<field name="notes"/>
</group>
<group>
<field name="short_name"/>
<field name="date_release"/>
<field name="date_updated"/>
avatar"/>
<field name="cover" widget="image" class="oe_
<field name="reader_rating"/>
</group>
</group>
<group>
<field name="description"/>
</group>
</form>
下面的代码定义了字段常用的属性,可以先有个印象
short_name = fields.Char('Short Title',translate=True, index=True)
state = fields.Selection(
[('draft', 'Not Available'),
('available', 'Available'),
('lost', 'Lost')],
'State', default="draft")
description = fields.Html('Description', sanitize=True, strip_ style=False)
pages = fields.Integer('Number of Pages',
groups='base.group_user',
states={'lost': [('readonly', True)]},
help='Total book page count', company_dependent=False)
原理
- Char: 字符串类型
- Text: 跨行字符串类型
- Selection: 选择列表类型。
重要提醒
Selection类型是可以使用数字作为key的,但是0在odoo中是作为未设置当前字段存在的。因此,如果key中使用了0,那么可能存在意想不到的坑。
- Html跟text类似,可存储HTML的富文本内容。
- Binary: 可以存储图片及文档。
- Boolean: True/False
- Date: 存储日期。可使用fields.Date.today()作为默认值。
- Datetime: 是作为navi的UTC时间存储(不包含时区信息)。可通过fields.Date.now()设置默认值。
- Integer: 整型。
- Float: 浮点型。可设置小数点精度。
- Monetary: 货币类型。
在定义字段的时候,除了以上的类型外,还包含了一些属性。 - string: 是字段展示的名称,如果没有设置,则会取字段名称(将_替换为空格)。
- translate: 设置为true,说明该字段是可翻译的。
- default: 设置默认值。可以是一个具体的值,也可以是一个函数。default=_compute_default。
- help: 是该字段在页面以tooltips展示的帮助信息
- groups: 该字段隶属于哪些权限组。如果没有,则默认跟随模型权限组。
- states: states允许用户界面根据state字段的值动态设置readonly、required和invisible属性的值。因此,它需要一个状态字段存在并在表单视图中使用(即使它是不可见的)。state属性的名称在Odoo中是硬编码的,不能更改。
- copy: 字段的值是否跟随记录的copy到新的记录。对于非关系型字段及many2one的字段,默认是True;对于One2many字段默认是False。
- index: 设置为True,将在数据库表中创建该字段的键,可加快搜索。
- readonly: UI页面上该字段表现为只读。
- required: 该字段是当前模型必备字段。
- company_operator标志位表示为不同的公司存储不同的值。
- group_operator: 当记录以类似于sql中的group by进行操作时,该字段通过哪种运算方式计算结果。常用的运算方式有count, count_distinct, array_agg, bool_and, bool_or, max, min, avg, and sum。Integer、float及货币类型默认采用sum计算方式。
- sanitize:用于HTML字段,用于清除html可能包含的不安全标识。
如果你想进一步控制HMTL的清洗,可通过如下属性: - sanitize_tags=True,移除不属于白名单的标签。(白名单定义在odoo/tools/ mail.py)
- sanitize_attributes=True, 移除不属于白名单的属性。
- sanitize_style=True, 移除不属于白名单的样式。
- strip_style=True, 移除所有的样式。
- strip_class=True, 移除所有的类属性。
更多
添加float字段(配置小数点)
添加货币字段
添加关联字段
添加层级结构
添加约束验证
添加计算字段
展示存储在其他模型中的关联字段
通过关联字段添加动态关系
对于关系字段,我们需要事先确定关系的目标模型(或协同模型)。然而,有时,我们可能需要把这个决定留给用户,首先选择我们想要的模型,然后选择我们想要链接到的记录。这可以通过使用参考字段来实现。
准备
步骤
- 编辑models/library_book.py文件
from odoo import models, fields, api
class LibraryBook(models.Model):
# ...
@api.model
def _referencable_models(self):
models = self.env['ir.model'].search([
('field_id.name', '=', 'message_ids')])
return [(x.model, x.name) for x in models]
- 添加引用字段
ref_doc_id = fields.Reference( selection='_referencable_models',
string='Reference Document')
原理
引用字段与m2o字段相似,他们都允许用户自己选择关联的模型。
目标模型通过selection属性进行选择,selection必须是包含两个元素元组的列表,第一个元素是内部标识,第二个标识是展示的内容。
例如:
[('res.users', 'User'), ('res.partner', 'Partner')]
但是,我们可以使用最常见的模型,而不是提供固定的列表。为了简单起见,我们使用了所有具有消息传递功能的模型。使用可引用的模型方法,我们动态地提供了一个模型列表。
我们的方法是通过提供一个函数来浏览所有可以被引用的模型记录,从而动态地构建一个将提供给selection属性的列表。虽然这两种形式都是允许的,但是我们在引号中声明了函数名,而不是直接引用不带引号的函数。这是更灵活的,它允许引用的函数只在稍后的代码中定义,例如,这在使用直接引用时是不可能的。
函数运行在模型层面,因此需要用@api.model装饰器。
虽然这个特性看起来不错,但它会带来很大的执行开销。显示大量记录的引用字段(例如,在列表视图中)会造成沉重的数据库负载,因为每个值都必须在单独的查询中查找。与常规关系字段不同,它也无法利用数据库引用完整性。
通过继承为模型添加新特性
odoo可以实现对归属于其他模块的模型功能进行扩展,而不去动原有的代码。可以添加字段、方法、以及修改以存在的字段、方法。
odoo提供了三种方式的继承
- 类的继承
- 原型继承
- 委托继承
准备
步骤
- 我们将为respartner用户添加关联的图书
class ResPartner(models.Model):
_inherit = 'res.partner'
_order = 'name'
authored_book_ids = fields.Many2many(
'library.book', string='Authored Books')
count_books = fields.Integer( 'Number of Authored Books',
compute='_compute_count_books' )
- 添加新增字段的计算函数
# ...
from odoo import api # if not already imported
# class ResPartner(models.Model):
# ...
@api.depends('authored_book_ids')
def _compute_count_books(self):
for r in self:
r.count_books = len(r.authored_book_ids)
原理
我们通过_inherit属性实现对于已有模块的继承。新增的字段将直接体现在原有模型上。
已有字段也可以进行增量修改。可以对原模型中的函数进行重写或修改(可通过super调用原有模型的函数)。
通过继承实现模型的copy
原型继承,对现有模块完整的复制。
准备
步骤
原型继承会用到_name及_inherit的类属性。
- 添加library_book_copy.py文件。
- 编辑文件
from odoo import models, fields, api
class LibraryBookCopy(models.Model):
_name = "library.book.copy"
_inherit = "library.book"
_description = "Library Book's Copy"
- 添加新文件引用, models/init.py
原理
在使用_name及_inherit的类属性的时候,odoo将使用_name作为类名复制_inherit的模型。
新的模型将体现在数据库中,有单独的数据库表。以上为例,library_book_copy表。
原型继承是copy父类完整的内容,包括字段、属性及方法。如果将要调整这些内容,可直接定义即可。例如, library.book已经有了name_get函数,但是不符合我们的要求。我们可以在library.book.copy模型中直接新增一个name_get函数。
警告
如果_name使用了父类的名称,那么原型继承是不生效的,而是普通的继承。
更多
虽然官方提供了原型继承,但是应用场景很少。反而是通过委托继承,可以在不复制整个数据结构的情况下实现我们想要的功能。
使用委托继承实现复制另一个模型的特性
委托继承使用类属性_inherits,注意多了一个s。在某些场景下,相较于修改现有模型,创建一个新的模型并与老的模型进行关联反而是更好的选择。
委托继承与面向对象编程的理念更为贴近。它还支持多态继承,即可以同时从多个模型继承。
比如,我们有一个图书馆。会有很多的读书人来图书馆读书,这些人在我们这有一些基本的信息(姓名、电话等),其中又有一些人是图书馆会员。会员与普通用户都有姓名、电话等基础信息,但是又多了办理会员的日期、会员卡号等特有信息。
准备
步骤
- 添加新的模型, res.partner
class LibraryMember(models.Model):
_name = 'library.member'
_inherits = {'res.partner': 'partner_id'}
partner_id = fields.Many2one(
'res.partner',
ondelete='cascade')
- 添加member特有的字段
# class LibraryMember(models.Model):
# ...
date_start = fields.Date('Member Since')
date_end = fields.Date('Termination Date')
member_number = fields.Char()
date_of_birth = fields.Date('Date of birth')
原理
我们通过_inherits实现对res.partner对象的委托继承,这是一个key-value的字典。key是继承模型的类名,value是当前模型关联到继承模型的字段。
当我们对新模型创建记录时,会现在res.partner、library.member中分别创建一条记录,并通过partner_id进行关联。
委托继承只是对字段的继承,并不包含函数。
更多
关于委托继承,有个简写方式。即在m2o中添加delegate=True属性,去掉_inherits的类属性。上面的例子可以写成
class LibraryMember(models.Model):
_name = 'library.member'
partner_id = fields.Many2one('res.partner', ondelete='cascade', delegate=True)
date_start = fields.Date('Member Since')
一个典型的委托继承是用户模型,res.users,继承自res.partner。也就是説我们在res.users中看到的一些字段其实是partner中的。
传统继承和原型继承都是可以为模型添加新的特性,但是效率偏低。
使用抽象模型复用模型特性
有时我们有一个特性,向同时添加到好几个模型中。抽象模型是可以实现我们想要的特性,然后被其他几个模型继承。
举个例子,我们将实现一个简单的归档特性。它将活动字段添加到模型中(如果它还不存在),并提供一个存档方法来切换活动标志。这是因为活动是一个魔法场。如果默认情况下存在于模型中,则active=False的记录将从查询中过滤掉。
准备
步骤
归档特性一般会有自己的模块,至少是自己的python文件。此处为了简单,就放在library_book.py文件中。
- 添加虚拟类
class BaseArchive(models.AbstractModel):
_name = 'base.archive'
active = fields.Boolean(default=True)
def do_archive(self):
for record in self:
record.active = not record.active
- 编辑library.book类以继承archive模型。
class LibraryBook(models.Model):
_name = 'library.book'
_inherit = ['base.archive']
# ...
原理
抽象模型基于models.AbstractModel创建。他与普通的models.Model功能基本类似,只是他并不在数据库中创建相应的数据表。他的存在就是为了让其他模型继承用的。
当一个模型定义了_inherit属性,那么他将继承该收藏模型所有的字段、属性及方法。
注意,此处_inherit的值是列表。
其实,_inhiret有两种形式。列表代表继承自多个模型,单独的字符串是继承自一个模型。
更多
最值得一提的抽象模型是mail.thread,它定义在mail(Discuss)模块中。它为模型添加了讨论的特性。我们可以在模型form视图下方看到消息。
还有一个模型,models.TransientModel。它跟model.Model类似,只是它存储的数据是暂时的,odoo会有定时任务清理掉。抛掉这个不同,瞬态模型跟常规模型一样。
瞬态模型在用户交互比较复杂的场景下比较有帮助,比如wizards(向导)。将在第八章详细介绍。
【odoo14】第四章、应用模型的更多相关文章
- 【odoo14】第十四章、CMS网站开发
第十四章.CMS网站开发** Odoo有一个功能齐全的内容管理系统(CMS).通过拖放功能,你的最终用户可以在几分钟内设计一个页面,但是在Odoo CMS中开发一个新功能或构建块就不是那么简单了.在本 ...
- PRML读书会第四章 Linear Models for Classification(贝叶斯marginalization、Fisher线性判别、感知机、概率生成和判别模型、逻辑回归)
主讲人 planktonli planktonli(1027753147) 19:52:28 现在我们就开始讲第四章,第四章的内容是关于 线性分类模型,主要内容有四点:1) Fisher准则的分类,以 ...
- 第四章、Django之模型层---创建模型
目录 第四章.Django之模型层---创建模型 一.写models.py 第四章.Django之模型层---创建模型 一.写models.py from django.db import model ...
- 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...
- 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章 ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (21) -----第四章 ASP.NET MVC中使用实体框架之在页面中创建查询和使用ASP.NET URL路由过虑
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 4.2. 构建一个搜索查询 搜索数据是几乎所有应用的一个基本功能.它一般是动态的,因 ...
- 精通Web Analytics 2.0 (6) 第四章:点击流分析的奇妙世界:实际的解决方案
精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第四章:点击流分析的奇妙世界:实际的解决方案 到开始实际工作的时候了.哦耶! 在本章中,您将了解到一些最重要的网络分析报告,我将 ...
- MVC5+EF6 简易版CMS(非接口) 第四章:使用业务层方法,以及关联表解决方案
目录 简易版CMS后台管理系统开发流程 MVC5+EF6 简易版CMS(非接口) 第一章:新建项目 MVC5+EF6 简易版CMS(非接口) 第二章:建数据模型 MVC5+EF6 简易版CMS(非接口 ...
- KnockoutJS 3.X API 第四章 表单绑定(11) options绑定
目的 options绑定主要用于下拉列表中(即<select>元素)或多选列表(例如,<select size='6'>).此绑定不能与除<select>元素之外的 ...
- KnockoutJS 3.X API 第四章(13) template绑定
目的 template绑定(模板绑定)使用渲染模板的结果填充关联的DOM元素. 模板是一种简单方便的方式来构建复杂的UI结构 . 下面介绍两种使用模板绑定的方法: 本地模板是支持foreach,if, ...
随机推荐
- Logstash 日志收集(补)
收集 Tomcat 日志 安装 Tomcat # 安装 jdk [root@web01 ~]# rpm -ivh jdk-8u181-linux-x64.rpm # 下载 [root@web01 ~] ...
- codeforces 1016C - Vasya And The Mushrooms 【构造 + 思维】
题目链接:戳这里 题意:从(1,1)出发,一遍把格子走完,每个格子只能走一次.问怎么走总和最大. 解题思路:画图可知,总共就3种走法的混合. dw: 样例1的走法 up: 样例1反过来的走法 lp: ...
- js arrow function return object
js arrow function return object bug filterData: { type: Object, default: () => {}, required: true ...
- Google coding Style Guide : Google 编码风格/代码风格 手册/指南
1 1 1 https://github.com/google/styleguide Google 编码风格/代码风格 手册/指南 Style guides for Google-originated ...
- js & sort array object
js & sort array object sort array object in js https://flaviocopes.com/how-to-sort-array-of-obje ...
- windows10 WSL
搭建WSL linux下的home目录,映射windows的目录地址 用户家目录 ➜ ~ pwd /home/ajanuw C:\Users\ajanuw\AppData\Local\Packages ...
- Flutter 可选择的Text
Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, ...
- 死磕Spring之IoC篇 - 文章导读
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- PHP中间件
定义 首先什么是php的中间件? 根据zend-framework中的定义: 所谓中间件是指提供在请求和响应之间的,能够截获请求,并在其基础上进行逻辑处理,与此同时能够完成请求的响应或传递到下一个中间 ...
- 使用Docker快速搭建Nginx+PHP-FPM+MySQL+phpMyAdmin环境
一.概述 环境介绍 操作系统:centos 7.6 docker版本:19.03.8 ip地址:192.168.31.34 本文将介绍如何使用单机部署Nginx+PHP-FPM环境 二.Nginx+P ...