由于本章有包含很多基础知识,个人不会全部转化为自己的语言。直接机器翻译了(用斜体标注,机器翻译反而一字不落,我会过滤掉冗余的内容),虽然机翻,但会保证意思不会偏。

本章主要章节如下:

  • 定义模型展示及顺序
  • 添加字段
  • 配置带有小数点精度的float型字段
  • 添加货币字段
  • 添加关联字段
  • 添加层级关系
  • 添加模型约束
  • 添加字段字段
  • 在其他模型中调用关联字段
  • 使用引用调用关联信息
  • 使用继承添加特性
  • 继承抽象模型复用模型特性
  • 使用委托继承完整继承另一个模型

定义模型展示及顺序

模型具有定义其行为的结构属性。它们的前缀是

下划线。模型最重要的属性是_name,因为它定义了内部全局标识符。在内部,Odoo使用_name属性来创建一个数据库表。例如,如果您提供library.book,则Odoo ORM将在数据库中创建library_book表。这就是为什么在Odoo中,_name属性必须是唯一的。

  • _rec_name: 以模型为单位展示时,模型展示的field字段。
  • _order: 记录在展示时候的排序

准备

步骤

定义模型文件models/library_book.py

  1. 添加模型的描述
_description = 'Library Book'
  1. 添加排序
_order = 'date_release desc, name'
  1. 使用short_name作为模型的默认展示字段
_rec_name = 'short_name'
short_name = fields.Char('Short Title', required=True)
  1. 在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定义了基本的模型。

  1. 增量代码如下
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,
)
  1. 添加对应的视图
<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字段(配置小数点)

添加货币字段

添加关联字段

添加层级结构

添加约束验证

添加计算字段

展示存储在其他模型中的关联字段

通过关联字段添加动态关系

对于关系字段,我们需要事先确定关系的目标模型(或协同模型)。然而,有时,我们可能需要把这个决定留给用户,首先选择我们想要的模型,然后选择我们想要链接到的记录。这可以通过使用参考字段来实现。

准备

步骤

  1. 编辑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]
  1. 添加引用字段
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提供了三种方式的继承

  • 类的继承
  • 原型继承
  • 委托继承

准备

步骤

  1. 我们将为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' )
  1. 添加新增字段的计算函数
# ...
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的类属性。

  1. 添加library_book_copy.py文件。
  2. 编辑文件
from odoo import models, fields, api
class LibraryBookCopy(models.Model):
_name = "library.book.copy"
_inherit = "library.book"
_description = "Library Book's Copy"
  1. 添加新文件引用, models/init.py

原理

在使用_name及_inherit的类属性的时候,odoo将使用_name作为类名复制_inherit的模型。

新的模型将体现在数据库中,有单独的数据库表。以上为例,library_book_copy表。

原型继承是copy父类完整的内容,包括字段、属性及方法。如果将要调整这些内容,可直接定义即可。例如, library.book已经有了name_get函数,但是不符合我们的要求。我们可以在library.book.copy模型中直接新增一个name_get函数。

警告

如果_name使用了父类的名称,那么原型继承是不生效的,而是普通的继承。

更多

虽然官方提供了原型继承,但是应用场景很少。反而是通过委托继承,可以在不复制整个数据结构的情况下实现我们想要的功能。

使用委托继承实现复制另一个模型的特性

委托继承使用类属性_inherits,注意多了一个s。在某些场景下,相较于修改现有模型,创建一个新的模型并与老的模型进行关联反而是更好的选择。

委托继承与面向对象编程的理念更为贴近。它还支持多态继承,即可以同时从多个模型继承。

比如,我们有一个图书馆。会有很多的读书人来图书馆读书,这些人在我们这有一些基本的信息(姓名、电话等),其中又有一些人是图书馆会员。会员与普通用户都有姓名、电话等基础信息,但是又多了办理会员的日期、会员卡号等特有信息。

准备

步骤

  1. 添加新的模型, res.partner
class LibraryMember(models.Model):
_name = 'library.member'
_inherits = {'res.partner': 'partner_id'}
partner_id = fields.Many2one(
'res.partner',
ondelete='cascade')
  1. 添加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文件中。

  1. 添加虚拟类
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
  1. 编辑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】第四章、应用模型的更多相关文章

  1. 【odoo14】第十四章、CMS网站开发

    第十四章.CMS网站开发** Odoo有一个功能齐全的内容管理系统(CMS).通过拖放功能,你的最终用户可以在几分钟内设计一个页面,但是在Odoo CMS中开发一个新功能或构建块就不是那么简单了.在本 ...

  2. PRML读书会第四章 Linear Models for Classification(贝叶斯marginalization、Fisher线性判别、感知机、概率生成和判别模型、逻辑回归)

    主讲人 planktonli planktonli(1027753147) 19:52:28 现在我们就开始讲第四章,第四章的内容是关于 线性分类模型,主要内容有四点:1) Fisher准则的分类,以 ...

  3. 第四章、Django之模型层---创建模型

    目录 第四章.Django之模型层---创建模型 一.写models.py 第四章.Django之模型层---创建模型 一.写models.py from django.db import model ...

  4. 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...

  5. 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章  ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...

  6. 《Entity Framework 6 Recipes》中文翻译系列 (21) -----第四章 ASP.NET MVC中使用实体框架之在页面中创建查询和使用ASP.NET URL路由过虑

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 4.2. 构建一个搜索查询 搜索数据是几乎所有应用的一个基本功能.它一般是动态的,因 ...

  7. 精通Web Analytics 2.0 (6) 第四章:点击流分析的奇妙世界:实际的解决方案

    精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第四章:点击流分析的奇妙世界:实际的解决方案 到开始实际工作的时候了.哦耶! 在本章中,您将了解到一些最重要的网络分析报告,我将 ...

  8. MVC5+EF6 简易版CMS(非接口) 第四章:使用业务层方法,以及关联表解决方案

    目录 简易版CMS后台管理系统开发流程 MVC5+EF6 简易版CMS(非接口) 第一章:新建项目 MVC5+EF6 简易版CMS(非接口) 第二章:建数据模型 MVC5+EF6 简易版CMS(非接口 ...

  9. KnockoutJS 3.X API 第四章 表单绑定(11) options绑定

    目的 options绑定主要用于下拉列表中(即<select>元素)或多选列表(例如,<select size='6'>).此绑定不能与除<select>元素之外的 ...

  10. KnockoutJS 3.X API 第四章(13) template绑定

    目的 template绑定(模板绑定)使用渲染模板的结果填充关联的DOM元素. 模板是一种简单方便的方式来构建复杂的UI结构 . 下面介绍两种使用模板绑定的方法: 本地模板是支持foreach,if, ...

随机推荐

  1. DOCKER - 构建一个docker镜像并跑起来

    一.有个基础镜像 1.基础镜像的选择 当前市场有众多可选择的基础docker镜像,可参考: https://blog.csdn.net/nklinsirui/article/details/80967 ...

  2. hautoj 1268 小天使改名

    1268: 小天使改名 时间限制: 2 秒  内存限制: 128 MB提交: 437  解决: 123提交 状态 题目描述 小天使的b站帐号被大家发现啦.于是小天使决定改名,将他原有ID中的两个不同位 ...

  3. 1. mac 手动安装nodejs搭建vue环境

    为什么选择手动安装nodejs呢? 因为使用mac自动安装还要更新homebrew,还要安装xcode tool, 太费劲了,不如手动安装, 卸载起来也方便 再一个, 我是后台开发者, 对前端页面, ...

  4. tfrecords转图片存储

    import os import shutil import tensorflow as tf import time import sys import cv2 # 图片存放位置 PATH_RES ...

  5. Angular Learning Paths

    Angular Learning Paths Angular Expert refs https://app.pluralsight.com/search/?q=angular xgqfrms 201 ...

  6. html template tag

    html template tag const tagName = `emoji-element`; const template = document.createElement('template ...

  7. yarn create & npx & npm init

    yarn create & npx & npm init https://www.npmtrends.com/npm-vs-npx-vs-yarn demo https://www.n ...

  8. auto skip function args

    auto skip function args https://repl.it/@xgqfrms/auto-skip-function-args "use strict"; /** ...

  9. uniapp 万年历

    大量代码来至这里 <template> <view class="calendar-main"> <!-- 当前年月 --> <view ...

  10. 「NGK每日快讯」12.29日NGK第56期官方快讯!