环境
odoo-14.0.post20221212.tar
ORM API学习总结/学习教程
模型(Model)
Model字段被定义为model自身的属性
from odoo import models, fields
class AModel(models.Model):
_name = 'a.model.name'
field1 = fields.Char()
警告
字段的名称和方法的名称不能相同,最后定义的方法、函数名称会覆盖前面定义的相同名称。
默认的,字段的标签(Lable,即用户可见字段名称)为对应字段名称开头字母改成大写后的值,可通过
string
字段属性改成修改字段Label
field2 = fields.Integer(string="Field Label")
可通过
default
,定义默认值:
name = fields.Char(default="a value")
默认值也可以通过函数获取:
def _default_name(self):
return 'Title'
name = fields.Char(default=lambda self: self._default_name())
API
BaseModel
class odoo.models.BaseModel
[
源代码]
Odoo模型的基类。Odoo mode可通过继承一下类来创建Model:
系统为每个数据库自动实例化每个模型一次。这些实例表示每个数据库上的可用模型,取决于该数据库上安装的模块。每个实例的实际类都是从创建和继承相应模型的Python类构建的。
每个模型实例都是一个“记录集(recordset)”,即模型记录的有序集合。记录集由
browse()
,
search()
或字段访问等方法返回。记录没有显式的表示:单条记录表示为一条记录的记录集。
要创建不需要实例化的类,可以将
_register
属性设置为
False
_auto= False
是否应该创建数据库表。如果设置为
False
, 应该重写
init()
来创建数据库表。默认设。针对
Model
和
TransientModel
自动设置为
False
,针对
AbstractModel
自动设置为
False
。可通过继承
AbstractModel
来创建不需要任何数据表的模型
_log_access
ORM是否自动生成和更新
Access Log fields
。默认
_auto
的值。
_table= None
模型对应的数据库表的名称。如果
_auto
设置为
True
的话。
_sequence= None
用于ID字段的SQL序列
_sql_constraints= []
sql约束,格式:
[(name, sql_def, message)]
_register= True
registry visibility
_abstract= True
是否为抽象模型
_transient= False
是否为
transient
模型
_name= None
模型名称(以 点分式命名的模块名称,比如
estate.users
)
_description= None
模块描述,非整数名称
_inherit= None
继承的Python模型:需要继承模型的名称(
_name
属性值)或者名称列表(
list
类型)
_inherits= {}
(不太理解)
dictionary {‘parent_model’: ‘m2o_field’} mapping the _name of the parent business objects to the names of the corresponding foreign key fields to use:
_inherits = {
'a.model': 'a_field_id',
'b.model': 'b_field_id'
}
implements composition-based inheritance: the new model exposes all the fields of the inherited models but stores none of them: the values themselves remain stored on the linked record.
警告
if multiple fields with the same name are defined in the
_inherits
-ed models, the inherited field will correspond to the last one (in the inherits list order).
_rec_name= None
用于标记记录的字段,默认值:
name
_order= 'id'
用于搜索结果的默认排序字段
_check_company_auto= False
执行
write
和
create
, 对拥有
check_company=True
属性的关联字段调用
_check_company
以确保公司一致性
_parent_name= 'parent_id'
用作父字段的many2one字段
_parent_store= False
设置为
True
以计算
parent_path
字段。与
parent_path
字段一起,设置记录树结构的索引存储,以便使用
child_of
和
parent_of
域运算符对当前模型的记录进行更快的分层查询
_date_name= 'date'
用于默认日历视图的字段
_fold_name= 'fold'
用于确定看板视图中折叠组的字段
AbstractModel
odoo.models.AbstractModel
[
源代码]
odoo.models.BaseModel
的别名
Model
class odoo.models.Model
[
源代码]
常规数据库持久化Odoo模型的主要父类。
通过继承此类来创建Odoo模型的:
class user(Model):
...
系统将为安装了该类模块的每个数据库实例化一次类
TransientModel
class odoo.models.TransientModel
[
源代码]
用于临时记录的父类模型,旨在暂时保持,并定期进行清理
TransientModel
具有简化的访问权限管理,所有用户都可以创建新记录,并且只能访问他们创建的记录。超级用户可以无限制地访问所有
TransientModel
记录。
字段(Fields)
class odoo.fields.Field
[
源代码]
字段拥有以下属性
string
(str) – 用户看到的字段的标签;如果未设置,ORM将采用类中的字段名开头字母改成大写后的
help
(str) – 用户看到的字段的提示条(设置该属性后,当鼠标悬停在字段标签上方时,会自动浮现提示条,显示该属性的文字内容)。
invisible
– 字段是否可见。默认为
False
,即可见
readonly
(bool) – 字段在用户界面是否只读,默认值
False
,仅对UI起作用
required
(bool) – 字段在用户界面是否必填,默认
False
。这通过在数据库层面为列添加
NOT NULL
约束来实现
index
(bool) – 是否为字段添加索引。注意:对不存储、虚拟字段不起作用。默认值:
False
default
(值或者可调用对象) – 设置字段的默认值。可以是静态值,或者以结果集为入参,返回某个值的函数。使用
default=None
舍弃该字段的默认值。
states
(dict) –将
state
值映射到UI属性-值对列表的字典映射,简单说就是允许用户界面依据
state
字段的值来动态设置对应字段的UI属性,因此,它要求存在一个
state
字段并在视图中使用(即使是隐藏的),
state
属性的名称是在odoo硬编码且不允许修改的,可用属性有:
readonly
,
required
,
invisible
。例如
states={'done':[('readonly',True)]}
,表示当
state
值为
done
时,将用户界面
states
所在字段在设置为只读(仅针对UI层面)
用法举例:
state = fields.Selection([
('draft', 'To Submit'),
('cancel', 'Cancelled'),
('confirm', 'To Approve'),
('refuse', 'Refused'),
('validate1', 'Second Approval'),
('validate', 'Approved')
], string='Status', readonly=True, copy=False, default='confirm')
date_from = fields.Datetime(
'Start Date', readonly=True, index=True, copy=False,
states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
groups
(str) – 值为逗号分隔的组XML ID列表,如
groups='base.group_user,base.group_system'
,可限制字段只能被给定组用户访问。
company_dependent
(bool) –
字段值是否依赖于当前公司,如果设置为
True
,则表示依赖当前公司,即字段值和公司绑定。这个属性的作用就是让同一字段,可以根据不同公司,存储不同的值,假设一个用户属于多个公司,他在不同公司的职务也不一样,此时就可以设置该属性为
True
。
该值未存储在当前模型表中。它注册为
ir.property
,也就是说它的值存储在
ir_property
表中,通过查询该表来获取该字段的值。
copy
(bool) – 当记录重复时,该字段值是否被拷贝(在使用 ORM
copy()
方法复制并生成新记录时,不复制该字段的值)。 (针对普通字段,默认值为:
True
,针对
one2many
和计算字段,包括属性字段(
property fields
,个人理解注册
ir.property
的字段)和关系字段,默认值为
False
store
(bool) – 该字段是否存储到数据库,针对计算字段,默认值为
False
,其它字段默认为
True
group_operator
(str) –
在当前字段上分组时,供
read_group()
使用的聚合函数
支持的聚合函数:
array_agg
: 值,包括空值,连接成一个数组
count
: 记录数
count_distinct
: 不重复记录数
bool_and
: 如果所有值都为真,则为真,否则为假
bool_or
: 如果至少有一个值为真,则为真,否则为假
max
: 所有值的最大值
min
: 所有值的最小值
avg
:所有值的平均值(算术平均值)
sum
: 所有值的总和
group_expand
(str) –
用于在当前字段上分组时用于扩展
read_group
结果的函数
@api.model
def _read_group_selection_field(self, values, domain, order):
return ['choice1', 'choice2', ...] # available selection choices.
@api.model
def _read_group_many2one_field(self, records, domain, order):
return records + self.search([custom_domain])
基础字段
class odoo.fields.Boolean
[
源代码]
bool
的封装
class odoo.fields.Char
[
源代码]
基本字符串字段,长度有限,通常在客户端显示为单行字符串
参数:
size
(int) – 为该字段可存储最大值
trim(bool)
– 说明该值是否被修剪(默认情况下,
True
)。请注意,修剪操作仅由 Web 客户端应用。
translate
(bool 或者可调用对象)
– 启用字段值的翻译;用于
translate=True
整体翻译字段值;
translate
也可以是可调用的,从而使得
translate(callback,value)
通过使用
callback(term)
来检索术语的翻译来翻译
value`
class odoo.fields.Float
[
源代码]
float
的封装
精度数字由可选的
digitals
属性给出。
参数
digits
(
tuple
(
int
,
int
), 或者
str
) – 一个元组
(total, decimal)
或者引用
DecimalPrecision
记录的字符串
digits=(8,2) 表示总的8位,小数点占2位
Float类为此提供了一些静态方法:
round()
以给定精度对浮点值进行舍入。
is_zero()
检查浮点值在给定精度下是否等于零。
compare()
按给定精度比较两个浮点值。
例子:
fields.Float.round(self.product_uom_qty, precision_rounding=self.product_uom_id.rounding)
fields.Float.is_zero(self.product_uom_qty, precision_rounding=self.product_uom_id.rounding)
field.Float.compare(self.product_uom_qty, self.qty_done, precision_rounding=self.product_uom_id.rounding)
比较助手出于历史目的使用
__cmp_
语义,因此使用此助手的正确惯用方式如下:
如果result==0,则第一个和第二个浮点数相等,如果result<0,第一个浮点数小于第二个,如果result>0,第一个浮动点数大于第二个浮动点数
class odoo.fields.Integer
[
源代码]
int
的封装
高级字段
class odoo.fields.Binary
[
源代码]
封装二进制内容(比如一个文件)。
参数:
- attachment
(bool)
– 字段是否存储为
ir_attachment
还是该model表的一列(默认为:
True`,即存储为前者。
class odoo.fields.Html
[
源代码]
html代码内容的封装
参数:略
class odoo.fields.Image
[
源代码]
图片的封装,扩展
Binary
如果图像大小大于像素的
max_width/max_height
限制,则通过保持纵横比将图像大小调整到该限制。
参数:
max_width
(int ) – 图像的最大宽度(默认值:
0
,无限制)
max_height
( int) – 图像的最大高度(默认值:
0
,无限制)
verify_resolution
( bool) – 是否应验证图像分辨率以确保它不会超过最大图像分辨率(默认值:
True
。最大图像分辨率请参阅
odoo.tools.image.ImageProcess
(默认值:
50e6
)。
参数
如果没有指定
max_width
/
max_height
或者设置为0,且
verify_resolution
为
False
,则不会验证字段内容,此时应该使用
Binary
字段。
class odoo.fields.Monetary
[
源代码]
封装以给定
res_currency
表示的浮点值。
小数精度和货币符号取自
currency_field
属性。
参数:
- currency_field
(str) –拥有表示该货币字段的
res_currency
的
Many2one
字段名称(默认:
'currency_id'
)
class odoo.fields.Selection
[
源代码]
封装不同值之间的互斥选择。
说明:
Selection
字段的可选值,存储在
public.ir_model_fields_selection
表中,通过
field_id
字段通过
public.ir_model_fields
表进行
-- 查询Selection字段ID
SELECT id FROM public.ir_model_fields
where model = 'stock.quality' and name='state'
-- 查询Selection字段可选值
select * from public.ir_model_fields_selection where field_id = 13028; -- 13028为Selection字段ID
参数:
selection
(list(tuple(str, str)) 或者可调用对象 或者 str)) – 指定字段的可选值。其值为包含2元组的列表,或者返回前者模型方法,或者方法名称
selection_add
(list(tuple(str, str)) –
在重写字段的情况下,提供
selection
的扩展。它是一个包含二元组
(value, label)
或者单元组
(value,)
的列表,其中,单元组中的
value
必须作为
value
出现在
selection
列表中的元组中。新值插入顺序和原有
selection
中元组顺序保持一致:
selection = [('a', 'A'), ('b', 'B')]
selection_add = [('c', 'C'), ('b',)]
> result = [('a', 'A'), ('c', 'C'), ('b', 'B')]
ondelete
–
为带有
selection_add
的任何重写字段提供回退机制。这是一个将
selection_add
中的每个选项映射到回退操作的dict。
此回退操作将应用于其
selection_add
选项映射到该操作的所有记录。
这些操作可以是以下任一操作:
set null
默认情况下,具有此选项的所有记录的选择值都将设置为False。
cascade
–具有此选项的所有记录将与选项本身一起删除。
set default
-具有此选项的所有记录都将设置为字段定义的默认值
<callable>
-一个可调用对象,其第一个也是唯一的参数将是包含指定的Selection选项的记录集,用于自定义处理
selection
属性选择是强制性的,除非是
related
或扩展的字段
class odoo.fields.Text
[
源代码]
类似
Char
,用于更长的内容,没有大小,通常展示为多行文本框。
参数:
translate
(bool 或者可调用对象) – 同
Char
Date(time) 字段
当将一个值赋值给
Date
/
Datetime
字段时,以下选择是合法的:
date
或
datetime
对象.
- 正确格式的字符:
Date
字段采用
YYYY-MM-DD
Datetime
字段采用
YYYY-MM-DD HH:MM:SS
False
或者
None
.
Date
和
Datetime
字段类拥有以下辅助函数,用于尝试转换为兼容类型:
示例
解析来自外部的日期/日期时间:
fields.Date.to_date(self._context.get('date_from'))
Date
/
Datetime
比较最佳实践:
Date
字段只能和date对象比较
Datetime
字段只能和datetime对象比较
Datetime
字段在数据库中存储为不带时区的时间戳,并以UTC时区存储。因为这样可使Odoo数据库独立于托管服务器系统的时区。时区转换完全由客户端管理。
Common operations with dates and datetimes such as addition, subtraction or fetching the start/end of a period are exposed through both
Date
and
Datetime
. These helpers are also available by importing
odoo.tools.date_utils
.
class odoo.fields.Date
源代码
Python
date
对象的封装
static add(value, *args, **kwargs)
返回
value
和
relativedelta
之和
参数
value
– 初始
date
或
datetime
args
– 传递给
relativedelta
的位置参数
kwargs
– 传递给
relativedelta
的关键词参数
返回
date/datetime结果对象
示例:
from odoo.fields import Date
print(Date.add(datetime.now(), years=1)) # 输出形如:2024-01-03
# 常见参数:
# years, months, days, leapdays, weeks, hours, minutes, seconds, microseconds
static subtract(value, *args, **kwargs)
[
源代码]
返回
value
和
relativedelta
之差
参数
value
– 初始
date
或者
datetime
args
– 传递给
relativedelta
位置参数
kwargs
– 传递给
relativedelta
的关键词参数
返回
date/datetime
结果对象
static context_today(record, timestamp=None)
[
源代码]
按客户端时区以适合
date
字段的格式返回当前日期
注解
该方法可能用于计算默认值
static end_of(value, granularity)
[
源代码]
从日期或日期时间获取时间段的结束
参数
value
– 初始
date
或
datetime
granularity
– 字符串表示的时间段类型, 可以是
year
,
quarter
,
month
,
week
,
day
或者
hour
。
返回
与指定时段的起始对应的
date/datetime
对象
示例:
print(datetime.now()) # 2023-01-03 10:12:32.332208
print(Date.end_of(datetime.now(), 'year')) # 输出形如:2023-12-31 23:59:59.999999
print(Date.end_of(datetime.now(), 'month')) # 输出形如:2023-01-31 23:59:59.999999
static start_of(value, granularity)
[
源代码]
从日期或日期时间获取时间段的开始
参数
value
– 初始
date
或
datetime
granularity
– 字符串表示的时间段类型, 可以是
year
,
quarter
,
month
,
week
,
day
或者
hour
。
返回
与指定时段的起始对应的
date/datetime
对象
示例:
print(datetime.now()) # 2023-01-03 10:18:57.071276
print(Date.start_of(datetime.now(), 'year')) # 输出形如:2023-01-01 00:00:00
print(Date.start_of(datetime.now(), 'month')) # 输出形如:2023-01-01 00:00:00
print(Date.start_of(datetime.now(), 'hour')) # 输出形如:2023-01-03 10:00:00
static to_date(value)
[
源代码]
尝试转换
value
为
date
对象
警告
如果value为
datetime
对象,它将被转换为
date
对象,且所有日期时间特定信息(HMS, TZ, …)都会丢失。
static to_string(value)
[
源代码]
将
date
或者
datetime
对象转为字符串
示例:
print(Date.to_string(datetime.now())) # 输出形如:2023-01-03
static today(*args)
[
源代码]
返回当前日期
示例:
print(Date.today()) # 格式形如:2023-01-03
class odoo.fields.Datetime
[
源代码]
Python
datetime
对象的封装
static context_timestamp(record, timestamp)
[
源代码]
返回转换为客户端时区的给定时间戳。
注解
此方法不是用作默认初始值设定项,因为
datetime
字段在客户端显示时会自动转换。对于默认值,应使用
now()
static add(value, *args, **kwargs)
[
源代码]
参考
Date.add
static subtract(value, *args, **kwargs)
[
源代码]
参考
Date.subtract
static end_of(value, granularity)
[
源代码]
参考
Date.end_of
static start_of(value, granularity)
[
源代码]
参考
Date.start_of
static to_string(value)
[
源代码]
参考
Date.to_string
static today(args)
[
源代码]
返回当天,午夜 (00:00:00)
示例:
from odoo.fields import Datetime
print(Datetime.today()) # 输出形如:2023-01-03 00:00:00
print(Datetime.now()) # 输出当前时间 2023-01-03 12:33:00
static to_datetime(value)
[
源代码]
将ORM
value
转为
datetime
值
关系字段(Relational Fields)
class odoo.fields.Many2one
[
源代码]
Many2one
字段的值是大小为0(无记录)或1(单个记录)的记录集。
参数:
- comodel_name
(str) – 目标模型的名称,
comodel_name
是必选参数,除非是相关或扩展字段(不太理解,原文:name of the target model
Mandatory
except for related or extended fields)
- domain
– 用于设置客户端侧候选值的可选 domain (domain 或者字符串)
- context
(dict) – 处理该字段时供客户端使用的上下文
- ondelete
(str) – 当引用的记录被删除时,怎么处理:可选值有:
'set null'
,
'restrict'
,
'cascade'
- auto_join
(bool) – 是否在搜索该字段时生成
JOIN
(默认:
False
)
- delegate
(bool) – 将其设置为
True
以标记可通过当前模型访问目标模型的字段(对应
_inherits
)
- check_company
(bool) – 标记需要在
_check_company()
中校验的字段。取决于字段属性,添加一个默认的公司domain
class odoo.fields.One2many
[
源代码]
One2many
字段的值为
comodel_name
中所有满足条件的记录的结果集,而目标模型中的
inverse_name
则等价于当前记录。
参数:
- comodel_name
(str) – 目标模型的名称
- inverse_name
(str) – 目标模型中反向
Many2one
字段名称,根据该字段反向查询记录
- domain
– 用于设置客户端候选值的条件 (domain 或者字符串),可选
- context
(dict) – 处理该字段时供客户端使用的上下文
- auto_join
(bool) – 是否在搜索该字段时生成
JOIN
(默认:
False
)
- limit
(int) – 读取时用的可选限制
comodel_name
和
inverse_name
参数是必选参数,除非是相关或者扩展字段
class odoo.fields.Many2many
[
源代码]
Many2many
字段的值为一个结果集。
参数:
- comodel_name
– 目标模型的名称,必选参数,除非是关联或者扩展字段
- relation
(str) – 数据库中存储关系的表名,可选参数。
- column1
(str) –
relation
表中引用"这些"记录的列名,可选参数
- column2
(str) –
relation
表中引用"那些"记录的列名,可选参数
relation
,
column1
和
column2
参数可选。 如果未给定,自动根据模型名称生成,提供的不同的
model_name
和
comodel_name
。
注意,ORM不支持在给定模型,使用同样的
comodel
,创建多个省略了
relation
参数的字段,因为这些字段将使用相同的表。ORM阻止两个
Many2many
字段使用相同的
relation
参数,除非:
- 两个字段都使用相同的模型,
comodel
并显示指定
relation
参数,否则
- 至少有一个字段属于携带
_auto = False
的模型
参数:
- domain
– 用于设置客户端候选值的条件 (domain 或者字符串),可选
- context
(dict) – 处理该字段时供客户端使用的上下文
- check_company
(bool) – 标记需要在
_check_company()
中校验的字段。取决于字段属性,添加一个默认的公司条件
- limit
(int) – 读取时用的可选限制
注意:
odoo不会在当前模型对应表中为
One2many
,
Many2many
类型的属性建立对应的表字段,但会为
Many2one
类型的属性建立对应表字段,针对
Many2many
类型的属性,odoo会建立一张辅助表,表名默认格式为
model1_table_name_model2_table_name_rel
,该表拥有两列,一列为当前模型表主键ID(
model1_table_name_id
),一列为关系字段关联模型表的主键ID(
model2_table_name_id
),这样通过两表记录ID就可以查询所需记录了
伪关系字段
class odoo.fields.Reference
[
源代码]
伪关系字段(数据库中没有FK)。该字段值存储为数据库中遵循模式
"res_model,res_id"
的字符串。
class odoo.fields.Many2oneReference
[
源代码]
该字段的值存储为数据库中的一个整数。与
odoo.fields.Reference
字段相反,必须在
Char
类型字段中指定模型,其中,该字段的名称必须在当前
Many2oneReference
字段中的
model_field
属性中指定
参数:
model_field
(str) – 存储模型的字段名称。
计算字段
可以使用
compute
参数计算字段(而不是直接从数据库中读取)
它必须将计算值分配给字段
。如果它使用其他字段的值,则应使用
depends()
指定这些字段
from odoo import api
total = fields.Float(compute='_compute_total')
@api.depends('value', 'tax')
def _compute_total(self):
for record in self:
record.total = record.value + record.value * record.tax
当使用子字段时,依赖可使用分点路径:
@api.depends('line_ids.value')
def _compute_total(self):
for record in self:
record.total = sum(line.value for line in record.line_ids)
默认情况下,不存才计算字段。他们在请求时被计算并返回。 设置
store=True
将在数据库中存储计算及字段并启动开启字段搜索。
也可以通过设置
search
参数开启在计算字段上的搜索。该参数值为一个返回
搜索条件
的方法名称 。
upper_name = field.Char(compute='_compute_upper', search='_search_upper')
def _search_upper(self, operator, value):
if operator == 'like':
operator = 'ilike'
return [('name', operator, value)]
在对模型进行实际搜索之前处理domain时调用该搜索方法。它必须返回与条件
field operator value
等效的domain
计算字段默认值。为了允许对计算字段进行设置,使用
inverse
参数。该参数值为反向计算并设置相关字段的函数的名称:
document = fields.Char(compute='_get_document', inverse='_set_document')
def _get_document(self):
for record in self:
with open(record.get_document_path) as f:
record.document = f.read()
def _set_document(self):
for record in self:
if not record.document: continue
with open(record.get_document_path()) as f:
f.write(record.document)
可以用同一方法同时计算多个字段,只需对所有字段使用同一方法并设置所有字段
discount_value = fields.Float(compute='_apply_discount')
total = fields.Float(compute='_apply_discount')
@api.depends('value', 'discount')
def _apply_discount(self):
for record in self:
# compute actual discount from discount percentage
discount = record.value * record.discount
record.discount_value = discount
record.total = record.value - discount
警告
虽然可以对多个字段使用相同的计算方法,但不建议对
reverse
方法使用相同的方法。
在
reverse
的计算过程中,所有使用所述inverse的字段都受到保护,这意味着即使它们的值不在缓存中,也无法计算它们。
如果访问了这些字段中的任何一个字段,且并且其值不在缓存中,ORM将简单的为这些字段返回默认值
False
。这意味着这些
inverse
字段的值(触发
inverse
方法的值除外)可能不会给出正确的值,这可能会破坏
inverse
方法的预期行为
计算字段的一种特殊情况是
相关
(代理)字段,它提供当前记录上子字段的值。它们是通过设置
related
参数来定义的,与常规计算字段一样,它们可以存储:
nickname = fields.Char(related='user_id.partner_id.name', store=True)
related
字段的值是通过遍历一系列关系字段并读取所访问模型上的字段来给出的。要遍历的字段的完整序列由
related
属性指定
如果未重新定义某些字段属性,则会自动从源字段中复制这些属性:
string
、
help
、
required
(仅当序列中的所有字段都是必需的时)、
groups
、
digits
、
size
、
translate
、
cleaning”、“selection
、
comodel_name
、
domain
和
context
。所有无语义属性都从源字段复制。
默认的, related字段:
像计算字段那样,添加
store=True
以存储
related
字段。当其依赖被修改时,会自动重新计算
related
字段。
小技巧
如果不希望在任何依赖项更改时重新计算
related
字段,则可以指定精确的字段依赖项:
nickname = fields.Char(
related='partner_id.name', store=True,
depends=['partner_id'])
# nickname仅在partner_id被修改时才会被重新计算,而不会在partner名称被修改时重新计算
警告
不可以在
related
字段依赖项中包含
Many2many
或者
One2many
字段
related
可以用于引用另一个模型中的
One2many
或
Many2many
字段,前提是通过当前模型的一个
Many2one
关系来实现的。
One2many
和
Many2many
不被支持,无法正确的汇总结果:
m2o_id = fields.Many2one()
m2m_ids = fields.Many2many()
o2m_ids = fields.One2many()
# Supported
d_ids = fields.Many2many(related="m2o_id.m2m_ids")
e_ids = fields.One2many(related="m2o_id.o2m_ids")
# Won't work: use a custom Many2many computed field instead
f_ids = fields.Many2many(related="m2m_ids.m2m_ids")
g_ids = fields.One2many(related="o2m_ids.o2m_ids")
自动生成的字段
访问日志字段
如果启用
_log_access
,自动设置并更新这些字段。当未用到这些字段时,以禁用它以阻止创建或更新表中这些字段。
默认的
_log_access
被设置为
_auto
的值。
odoo.fields.create_date
创建记录时存储创建时间,
Datetime
类型
odoo.fields.create_uid
存储记录创建人,
Many2one
to a
res.users
odoo.fields.write_date
存储记录最后更新时间,
Datetime
类型
odoo.fields.write_uid
存储记录最后更新人,
Many2one
to a
res.users
.
警告
必须对
odoo.models.TransientModel
模型开启
_log_access
保留字段名称
除了自动字段之外,还有一些字段名是为预定义行为保留的。当需要相关行为时,应在模型上定义它们:
odoo.fields.name
_rec_name
的默认值,用于在需要代表性“命名”的上下文中显示记录。
odoo.fields.Char
类型
odoo.fields.active
切换记录的全局可见性,如果
active
设置为
False
,则记录在大多数搜索和列表中不可见。
odoo.fields.Boolean
类型
odoo.fields.state
对象的声明周期阶段,供
fields.[Selection
的
states
属性使用
odoo.fields.parent_id
_parent_name
的默认值,用于以树结构组织记录,并在domain中启用
child_of
和
parent_of
运算符。
Many2one
字段。
odoo.fields.parent_path
当
_parent_store
设置为
True
时,用于存储反映[
_parent_name
]树结构的值,并优化搜索domain中的
child_of
和
parent_of
运算符。必须使用
index=True
声明才能正确操作。
odoo.fields.Char
类型
odoo.fields.company_id
用于Odoo多公司行为的主字段名。供
:meth:~Odoo.models._check_company
用于检查多公司一致性。定义记录是否在公司之间共享(没有值)还是仅由给定公司的用户访问。
Many2one
:类型:
res_company
记录集(Recordset)
与模型和记录的交互是通过记录集执行的,记录集是同一模型的记录的有序集合。
警告
与名称所暗示的相反,记录集当前可能包含重复项。这在未来可能会改变。
在模型上定义的方法是在记录集上执行的,方法的
self
是一个记录集:
class AModel(models.Model):
_name = 'a.model'
def a_method(self):
# self can be anything between 0 records and all records in the
# database
self.do_operation()
对记录集进行迭代将产生新的单条记录的记录集,这与对Python字符串进行迭代产生单个字符的字符串非常相似:
def do_operation(self):
print(self) # => a.model(1, 2, 3, 4, 5)
for record in self:
print(record) # => a.model(1), then a.model(2), then a.model(3), ...
字段访问
记录集提供了一个“Active Record” 接口:模型字段可直接作为记录的属性直接读取和写入。
注解
当访问潜在多条记录的记录集上的非关系字段时,使用
mapped()
,该函数返回一个列表:
total_qty = sum(self.mapped('qty')) # mapped返回一个列表,形如[2,4,5]
字段值也可以像字典项一样访问。设置字段的值会触发对数据库的更新:
>>> record.name
Example Name
>>> record.company_id.name
Company Name
>>> record.name = "Bob"
>>> field = "name"
>>> record[field]
Bob
警告
- 尝试读取多条记录上的字段将引发非关系字段的错误。
- 访问一个关系字段(
Many2one
,
One2many
,
Many2many
),总是返回记录集,如果未设置字段的话,则返回空记录集。
记录缓存和预取
Odoo为记录的字段维护一个缓存,这样,不是每个字段的访问都会发出数据库请求。
以下示例仅为第一条语句查询数据库:
record.name # 第一次访问从数据库获取值
record.name # 第二次访问从缓存获取值
为了避免一次读取一条记录上的一个字段,Odoo会按照一些启发式方法预取个记录和字段,以获得良好的性能。一旦必须在给定记录上读取字段,ORM实际上会在更大的记录集上读取该字段,并将返回的值存储在缓存中以供后续使用。预取的记录集通常是通过迭代获得记录的记录集。此外,所有简单的存储字段(布尔值、整数、浮点值、字符、文本、日期、日期时间、选择、many2one)都会被提取;它们对应于模型表的列,并在同一查询中高效地获取。
考虑以下示例,其中
partners
为包含1000条记录的记录集。如果不进行预取,循环将对数据库进行2000次查询。使用预取,只进行一次查询
for partner in partners:
print partner.name # first pass prefetches 'name' and 'lang'
# (and other fields) on all 'partners'
print partner.lang
预取也适用于辅助记录:当读取关系字段时,它们的值(即记录)将被订阅以供将来预取。访问这些辅助记录之一将预取同一模型中的所有辅助记录。这使得以下示例仅生成两个查询,一个用于合作伙伴,另一个用于国家/地区:
countries = set()
for partner in partners:
country = partner.country_id # first pass prefetches all partners
countries.add(country.name) # first pass prefetches all countries
方法修饰器
Odoo API模块定义了Odoo环境和方法修饰符
odoo.api.autovacuum(method)
[
源代码]
修饰一个方法,使其由日常vacuum cron作业(模型
ir.autovacuum
)调用。这通常用于垃圾收集之类的不需要特定cron作业的任务
odoo.api.constrains(*args)
[
源代码]
装饰一个约束检查器
每个参数必须是校验使用的字段名称:
@api.constrains('name', 'description')
def _check_description(self):
for record in self:
if record.name == record.description:
raise ValidationError("Fields name and description must be different")
当记录的某个命名字段被修改时调用装饰器函数。
如果校验失败,应该抛出
ValidationError
警告
@constrains
仅支持简单的字段名称,不支持并忽略点分名称(关系字段的字段,比如
partner_id.customer
)
@constrains
仅当修饰方法中声明的字段包含在
create
或
write
调用中时才会触发。这意味着视图中不存在的字段在创建记录期间不会触发调用。必须重写
create
,以确保始终触发约束(例如,测试是否缺少值)
odoo.api.depends(*args)
[
源代码]
返回一个装饰器,该装饰器指定
compute
方法的字段依赖关系(对于新型函数字段)。参数支持是由点分隔的字段名序列组成的字符串:
pname = fields.Char(compute='_compute_pname')
@api.depends('partner_id.name', 'partner_id.is_company')
def _compute_pname(self):
for record in self:
if record.partner_id.is_company:
record.pname = (record.partner_id.name or "").upper()
else:
record.pname = record.partner_id.name
有的也可能传递一个函数作为参数,这种情况下,依赖通过调用 在这种情况下,通过使用字段的模型调用函数来提供依赖项
odoo.api.depends_context(*args)
[
源代码]
返回一个修饰符,该修饰符指定非存储的“compute”方法的上下文依赖项。每个参数都是上下文字典中的键:
price = fields.Float(compute='_compute_product_price')
@api.depends_context('pricelist')
def _compute_product_price(self):
for product in self:
if product.env.context.get('pricelist'):
pricelist = self.env['product.pricelist'].browse(product.env.context['pricelist'])
else:
pricelist = self.env['product.pricelist'].get_default_pricelist()
product.price = pricelist.get_products_price(product).get(product.id, 0.0)
所有依赖项都必须是可哈希的。以下键具有特殊支持:
company
(上下文中的值或当前公司id),
uid
(当前用户ID和超级用户标记),
active_test
(
env.context
或者
field.context
中的值).
odoo.api.model(method)
[
源代码]
修饰一个record-style的方法,其中
self
是一个空记录集,但其内容不相关,只有模型相关,可以理解为不会创建对应数据库记录的模型对象。模型层面的操作需要添加此修饰器,相当于类静态函数
@api.model
def method(self, args):
...
odoo.api.model_create_multi(method)
[
源代码]
修饰一个以字典列表为参数,并创建多条记录的方法。可能仅通过一个字典或者字典列表调用该方法:
record = model.create(vals)
records = model.create([vals, ...])
odoo.api.onchange(*args)
[
源代码]
返回一个修饰器来修饰给定字段的onchange方法。
在出现字段的表单视图中,当修改某个给定字段时,将调用该方法。在包含表单中存在的值的伪记录上调用该方法。该记录上的字段赋值将自动返回客户端。
每个参数必须是字段名:
@api.onchange('partner_id')
def _onchange_partner(self):
self.message = "Dear %s" % (self.partner_id.name or "")
return {
'warning': {'title': "Warning", 'message': "What is this?", 'type': 'notification'},
}
如果类型设置为通知(
notification
),则警告将显示在通知中。否则,它将作为默认值显示在对话框中
警告
@onchange
仅支持简单的字段名称,不支持并自动忽略点分名称(关系字段的字段,比如
partner_id.tz
)
危险
由于
@onchange
返回伪记录的记录集,对上述记录集调用任何一个CRUD方法(
create()
,
read()
,
write()
,
unlink()
)都是未定义的行为,因为它们可能还不存在于数据库中。相反,只需像上面的示例中所示那样设置记录的字段或调用
update()
方法
警告
one2many
或者
many2many
字段不可能通过
onchange
修改其自身。这是客户端限制 - 查看
#2693
odoo.api.returns(model, downgrade=None, upgrade=None)
[
源代码]
为返回
model
实例的方法返回一个修饰器
参数
model
– 模型名称,或者表示当前模型的
'self'
downgrade
– 一个用于转换record-style的
value
为传统风格输出的函数
downgrade(self, value, *args, **kwargs)
upgrade
– 一个用于转换传统风格(traditional-style)的
value
为record-style的输出的函数
upgrade(self, value, *args, **kwargs)
参数
self
,
*args
和
**kwargs
以record-style方式传递给方法
修饰器将方法输出适配api风格:
id
,
ids
或者
False
对应传统风格,而记录集对应记录风格:
@model
@returns('res.partner')
def find_partner(self, arg):
... # return some record
# output depends on call style: traditional vs record style
partner_id = model.find_partner(cr, uid, arg, context=context)
# recs = model.browse(cr, uid, ids, context)
partner_record = recs.find_partner(arg)
注意,被修饰的方法必须满足那约定。
这些修饰器是自动
继承的
:重写被修饰的现有方法的方法将被相同的
@return(model)修饰
环境(Environment)
Environment
存储ORM使用的各种上下文数据:数据库游标(用于数据库查询)、当前用户(用于访问权限检查)和当前上下文(存储任意元数据)。环境还存储缓存。
所有记录集都有一个环境,它是不可变的,可以使用
env
访问,并提供对以下的访问:
- 当前用户 (
user
)
- 游标 (
cr
)
- 超级用户标识(
su
)
- 或者上下文 (
context
)
>>> records.env
<Environment object ...>
>>> records.env.user
res.user(3)
>>> records.env.cr
<Cursor object ...)
>>> self.env.context # 返回字典数据,等价于 self._context
{'lang': 'en_US', 'tz': 'Europe/Brussels'}
>>> self._context
{'lang': 'en_US', 'tz': 'Europe/Brussels'}
从其他记录集创建记录集时,将继承环境。环境可用于获取其他模型中的空记录集,并查询该模型:
>>> self.env['res.partner']
res.partner()
>>> self.env['res.partner'].search([['is_company', '=', True], ['customer', '=', True]])
res.partner(7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74)
Environment.ref(xml_id, raise_if_not_found=True)
[
源代码]
返回与给定
xml_id
对应的记录。
Environment.lang
返回当前语言代码。返回类型str
Environment.user
返回当前用户(作为一个实例)。返回类型
res_users
Environment.company
返回当前公司(作为一个实例)
如果未在上下文 (
allowed_company_ids
)中指定, 返回当前用户的主公司(If not specified in the context(
allowed_company_ids
), fallback on current user companies)
警告
在sudo模式下没有应用健康检查!在sudo模式下,用户可以访问任何公司,即使不是在他允许的公司。
这允许触发公司间修改,即使当前用户无权访问目标公司
Environment.companies
返回用户启用的公司的记录集。
如果未在上下文 (
allowed_company_ids
)中指定, 返回当前用户的主公司(If not specified in the context(
allowed_company_ids
), fallback on current user companies)
警告
在sudo模式下没有应用健康检查!在sudo模式下,用户可以访问任何公司,即使不是在他允许的公司。
这允许触发公司间修改,即使当前用户无权访问目标公司
修改环境
Model.with_context([context][, **overrides])
-> records[
源代码]
返回附加到扩展上下文的此记录集的新版本。
扩展上下文是提供的合并了
overrides
的
context
,或者是合并了
overrides
当前
context
# current context is {'key1': True}
r2 = records.with_context({}, key2=True)
# -> r2._context is {'key2': True}
r2 = records.with_context(key2=True)
# -> r2._context is {'key1': True, 'key2': True}
需要注意的是,上下文是和记录集绑定的,修改后的上下文并不会在其它记录集中共享。
Model.with_user(user)
[
源代码]
以非超级用户模式返回附加到给定用户的此记录集的新版本,即传入一条用户记录并返回该用户的环境,除非
user
是超级用户(按照约定,超级用户始终处于超级用户模式)
Model.with_company(company)
[
源代码]
返回具有已修改上下文的此记录集的新版本,这样:
result.env.company = company
result.env.companies = self.env.companies | company
警告
当当前用户使用未经授权的公司时,如果不是在sudoed环境中访问该公司,则可能会触发
AccessError
Model.with_env(env)
[
源代码]
返回附加到所提供环境的此记录集的新版本。
警告
新环境将不会从当前环境的数据缓存中受益,因此稍后的数据访问可能会在从数据库重新获取数据时产生额外的延迟。返回的记录集具有与
self
相同的预取对象。
Model.sudo([flag=True])
[
源代码]
根据
flag
,返回启用或禁用超级用户模式的此记录集的新版本。超级用户模式不会更改当前用户,只是绕过访问权限检查。
警告
使用
sudo
可能会导致数据访问跨越记录规则的边界,可能会混淆要隔离的记录(例如,多公司环境中来自不同公司的记录)。
这可能会导致在多条记录中选择一条记录的方法产生不直观的结果,例如获取默认公司或选择物料清单。
注解
因为必须重新评估记录规则和访问控制,所以新的记录集将不会从当前环境的数据缓存中受益,因此以后的数据访问可能会在从数据库重新获取时产生额外的延迟。返回的记录集具有与
self
相同的预取对象。
SQL执行
环境上的
cr
属性是当前数据库事务的游标,允许直接执行SQL,无论是对于难以使用ORM表达的查询(例如复杂join),还是出于性能原因
self.env.cr.execute("some_sql", params)
由于模型使用相同的游标,并且
Environment
保存各种缓存,因此当在原始SQL中更改数据库时,这些缓存必须失效,否则模型的进一步使用可能会变得不连贯。在SQL中使用
CREATE
、
UPDATE
或
DELETE
,但不使用
SELECT
(只读取数据库)时,必须清除缓存。
注解
可以使用
invalidate_cache()
执行缓存的清理
Model.invalidate_cache(fnames=None, ids=None)
[
源代码]
修改某些记录后,使记录缓存无效。如果
fnames
和
ids
都为
None
,则清除整个缓存。
参数:
fnames
–已修改字段的列表,
None
表示所有字段
ids
–修改的记录ID的列表,
None
表示所有记录
警告
执行原始SQL绕过ORM,从而绕过Odoo安全规则。请确保在使用用户输入时对查询进行了清洗,如果确实不需要使用SQL查询,请使用ORM实用程序。
常用ORM方法Common ORM methods
创建/更新(Create/update)
Model.create(vals_list)
→ records[
源代码]
为模型创建新记录
使用字典列表
vals_list
中的值初始化新记录,如果需要,使用
default_get()
中的值
参数
vals_list
(list) --模型字段的值,作为字典列表:
[{'field_name':field_value,…},…]
为了向后兼容,
vals_list
可以是一个字典。它被视为单个列表
[vals]
,并返回一条记录。有关详细信息请参见
write()
返回
创建的记录
引发
AccessError
–
如果用户对请求的对象没有创建权限
如果用户尝试绕过访问规则在请求的对象上创建
ValidationError
– 如果用户尝试为字段输入不在选择范围内的无效值
UserError
–如果将在对象层次结构中创建循环,操作的一个结果(例如将对象设置为其自己的父对象)
Model.copy(default=None)
[
源代码]
使用默认值更新拷贝的记录
self
Model.default_get(fields_list)
→ default_values[
源代码]
返回
fields_list
中字段的默认值。默认值由上下文、用户默认值和模型本身决定
注解
不考虑未请求的默认值,不需要为名称不在
fields_list
中的字段返回值。
Model.name_create(name)
→ record[
源代码]
通过调用
create()
创建新记录,调用时
create()
时只提供一个参数值:新记录的显示名称。
新记录将使用适用于此模型的任何默认值初始化,或通过上下文提供。
create()
的通常行为适用
参数
name
– 要创建记录的显示名称
返回类型
元组
返回
创建的记录的
name_get()
成对值
Model.write(vals)
[
源代码]
使用提供的值更新当前记录集中的所有记录
参数:
vals
(dict) –需要更新的字段及对应的值,比如:
{'foo': 1, 'bar': "Qux"}
,将设置
foo
值为
1
,
bar
为
"Qux"
,如果那些为合法的话,否则将触发错误。
需要特别注意的是,需要更新的字段越多,更新速度越慢(笔者实践时发现的,但是没验证是否和字段类型有关,特别是关系字段,关系字段的更新可能会调用对应模型的
write
方法,该方法如果被重写了,也可能会导致耗时的增加,总的来说,遵守一个原则,仅更新需要更新的字段)
引发
AccessError
–
如果用户对请求的对象没有创建权限
如果用户尝试绕过访问规则在请求的对象上创建
ValidationError
– 如果用户尝试为字段输入不在选择范围内的无效值
UserError
–如果将在对象层次结构中创建循环,操作的一个结果(例如将对象设置为其自己的父对象)(官方原文:if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
对于数字型字段(
odoo.fields.Integer
,
odoo.fields.Float
) ,值必须为对应类型
对于
odoo.fields.Boolean
, 值必须为
bool
类型
对于
odoo.fields.Selection
, 值必须匹配选择值(通常为
str
,有时为
int
)
对于
odoo.fields.Many2one
,值必须为记录的数据库标识
其它非关系字段,使用字符串值
危险
出于历史和兼容性原因,
odoo.fields.Date
和
odoo.fields.Datetime
字段使用字符串作为值(写入和读取),而不是
date
或
datetime
。这些日期字符串仅为UTC格式,并根据
odoo.tools.misc.DEFAULT_SERVER_DATE_FORMAT
和
odoo.tools.miisc.DEFAULT_SERVER _DATETIME_FORMAT
进行格式化
odoo.fields.One2many
和
odoo.fields.Many2many
使用特殊的“命令”格式来操作存储在字段中/与字段关联的记录集。
这种格式是一个按顺序执行的三元组列表,其中每个三元组都是要对记录集执行的命令。并非所有命令都适用于所有情况。可能的命令有:
(0, 0, values)
从提供的
values
字典创建新记录,形如
(0, 0, {'author': user_root.id, 'body': 'one'})
。
(1, id, values)
使用
values
字典中的值更新id值为给定
id
值的现有记录。不能在
create()
中使用。
(2, id, 0)
从记录集中删除id为指定
id
的记录,然后(从数据库中)删除它
不能在
create()
中使用。
(3, id, 0)
从记录集中删除id为指定
id
的记录,但不删除它。不能在
create()
中使用。
(4, id, 0)
添加一条id为指定
id
的已存在记录到记录集
(5, 0, 0)
从结果集移除所有记录, 等价于显示的对每条记录使用命令
3
。 不能在
create()
中使用。
(6, 0, ids)
根据
ids
列表,替换所有已存在记录, 等价于使用命令
(5, 0, 0)
,随后对
ids
中的每个id使用命令
(4, id, 0)
。实践发现,针对One2many字段,如果
ids
对应记录的
Many2one
字段没存储当前模型主键ID值时,无法使用该命令。
实际使用时,这些命令可以组合使用,如下,给
fieldName
设置值时,会先指定命令
5
,在执行命令
0
Model.write({'fieldName': [(5, 0, 0), (0, 0, dict_value)]})
Model.flush(fnames=None, records=None)
[
源代码]
处理所有待定的计算(在所有模型上),并将所有待定的更新刷新到数据库中(Process all the pending computations (on all models), and flush all the pending updates to the database)。
搜索/读取(Search/Read)
Model.browse([ids])
→ records[
源代码]
在当前环境中查询
ids
参数指定的记录并返回记录结果集,如果为提供参数,或者参数为
[]
,则返回空结果集
self.browse([7, 18, 12])
res.partner(7, 18, 12)
Model.search(args[, offset=0][, limit=None][, order=None][, count=False])
[
源代码]
基于
args
搜索域
搜索记录
参数
args
–
搜索域
。使用
[]
代表匹配所有记录。
offset
(int) – 需要忽略的结果记录数 (默认: 0)
limit
(int) – 最大返回记录数 (默认返回所有)
order
(str) – 排序字符串
count
(bool) – 如果为
True
,仅计算并返回匹配的记录数 (默认: False)
返回
最多
limit
条符合搜索条件的记录
引发
AccessError
–如果用户尝试绕过访问规则读取请求的对象
Model.search_count(args)
→
int
[
源代码]
返回当前模型中匹配提供的搜索域
args
的记录数.
Model.name_search(name='', args=None, operator='ilike', limit=100)
→ records[
源代码]
搜索比较显示名称与给定
name
匹配(匹配方式为给定
operator
),且匹配搜索域
args
的记录
例如,这用于基于关系字段的部分值提供建议。有时被视为
name_get()
的反函数,但不能保证是。
此方法等效于使用基于
display_name
的搜索域调用
search()
,然后对搜索结果执行
“name_get()”
关于搜索结果
参数
name
(str) – 需要匹配的名称
args
(list) – 可选的搜索域, 进一步指定限制
operator
(str) – 用于匹配
name
的域操作,比如
'like'
或者
'='
limit
(int) – 可选参数,返回最大记录数
返回类型
list
返回
所有匹配记录的对值
(id, text_repr)
列表
Model.read([fields])
[
源代码]
读取
self
中记录的指定字段, 低阶/RPC方法。Python代码中,优选
browse()
.
参数
fields
– 需要返回的字段名称(默认返回所有字段)
返回
字典的列表,该字典为字段名称同其值映射,每条记录一个字典
引发
AccessError
– 如果用户没有给定记录的读取权限
Model.read_group(domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True)
[
源代码]
获取列表视图中按给定
groupby
字段分组的记录列表。
参数
domain
(list) –
搜索域
。使用
[]
表示匹配所有
fields
(list) – 对象上指定的列表视图中存在的字段列表。每个元素要么是“field”(字段名,使用默认聚合),要么是“field:agg”(使用聚合函数“agg”聚合字段),要么就是“name:agg(field)”(使用“agg'聚合字段并将其当做“name”返回)。可能的聚合函数为PostgreSQL提供的函数(
https://www.postgresql.org/docs/current/static/functions-aggregate.html
),且“count_distict”,具有预期含义。
groupby
(list) – 记录分组依据的分组依据描述列表。groupby描述要么是字段(然后将按该字段分组),要么是字符串“field:groupby_function”。目前,唯一支持的函数是
day
、
week
、
month
、
quarter
或
year
,它们只适用于
date/datetime
字段
offset
(int) – 需要跳过的记录数,可选参数。
limit
(int) – 需要返回的最大记录数,可选参数
orderby
(str) – 排序字符串(当前仅支持
Many2one
字段)。可选参数。
lazy
(bool) – 如果为
True
,则结果只按第一个groupby分组,其余groupby放入
__context
键中。如果为
False
,则在一个调用中完成所有groupby。
返回
字典列表(每条记录一个字典)。包含:按
groupby
参数中指定字段分组后的字段的值
__domain
: 指定搜索条件的元组的列表
__context
: 拥有类似
groupby
参数的字典
返回类型
[{‘field_name_1’: value, …]
引发
AccessError
–
如果用户对所请求的对象没有读取权限,
如果用户尝试绕过对访问规则读取所请求对象
Model.copy_data()
拷贝当前模型记录的数据,返回一个字典,字典key为模型字段名称,key值为对应的字段值。注意:返回字典key不包含Odoo系统自动生成的模型表字段:
create_uid
,
create_date
,
write_date
,
write_uid
,
id
字段/视图(Fields/Views)s
Model.fields_get([fields][, attributes])
[
源代码]
返回每个字段的定义
返回的值是包含字典的字典(按字段名索引)。包括继承字段。将转换string、help和selection(如果存在)属性
Model.fields_view_get([view_id | view_type='form'])
[
源代码]
获取所请求视图的详细组成,如字段、模型、视图架构
参数
view_id
(int) – 视图的ID或者None
view_type
(str) – 返回视图的类型,如果
view_id
为
None
的话(‘form’, ‘tree’, …)
toolbar
(bool) – 设置为
True
以包含上下文操作
submenu
– 已弃用
返回
请求视图的组成(包括继承的视图和扩展)
返回类型
dict
引发
AttributeError
–
如果继承的视图具有除“before”、“after”、“inside”、“replace”以外的未知位置
则如果在父视图中找到除“position”以外的标记
Invalid ArchitectureError
– 如果框架中有定义form, tree, calendar, search 等以外的视图
搜索域(Search domains)
域是一个标准列表,每个标准都是
(field_name,operator,value)
的三元组(一个“列表”或“元组”),其中:
field_name
(
str
)
当前模块的字段名称 或通过
Many2one
,使用点符号的关系遍历,例如
'street'
或者
'partner_id.country'
operator
(
str
)
用于比较
field_name
与
value
的运算符。有效运算符为:
=
等于
!=
不等于
>
大于
>=
大于等于
<
小于
<=
小于等于
=?
未设置或者等于(如果
value
为
None
或者
False
则返回
True
,否则与
=
一样)
=like
将
field_name
同
value
模式匹配。模式中的下划线
_
匹配任何单个字符;百分号
%
匹配任何零个或多个字符的字符串
like
将
field_name
同
%value%
模式匹配。类似
=like
,但是匹配前使用
%
包装
value
not like
不匹配
%value%
模式
ilike
大小写敏感的
like
not ilike
大小写敏感的
not like
=ilike
大小写敏感的
=like
in
等于
value
中的任意项,
value
应该为项列表
not in
不等于
value
中的任意项
child_of
是
value
记录的child(后代)(
value
可以是一个项或一个项列表)。考虑模型的语义(即遵循由
_parent_name
命名的关系字段)。
parent_of
是
value
记录的parent(祖先)(
value
可以是一个项或一个项列表)。考虑模型的语义(即遵循由
_parent_name
命名的关系字段)
value
变量类型,必须可同命名字段比较(通过
operator
)
可以使用前缀形式的逻辑运算符组合域条件:
例子:
搜索来自比利时或德国名为
ABC
,且语言不为英语的合作伙伴:
[('name','=','ABC'),
('language.code','!=','en_US'),
'|',('country_id.code','=','be'),
('country_id.code','=','de')]
该域被解释为:
(name is 'ABC')
AND (language is NOT english)
AND (country is Belgium OR Germany)
unlink
记录(集)信息
Model.ids
返回与
self
对应的真实记录ID
odoo.models.env
返回给定记录集的环境。类型
Environment
Model.exists()
→ records[
源代码]
返回self中存在的记录子集并将删除的记录标记为缓存中的记录. 可用作对记录的测试:
if record.exists():
...
按约定,将新记录作为现有记录返回
Model.ensure_one()
[
源代码]
验证当前记录集只拥有一条记录
引发
odoo.exceptions.ValueError
–
len(self) != 1
Model.name_get()
→ [id, name, ...][
源代码]
返回
self
中记录的文本表示形式。默认情况下,为
display_name
字段的值。
Model.get_metadata()
[
源代码]
返回关于给定记录的元数据
操作
记录集是不可变的,但可以使用各种集合操作组合同一模型的集合,从而返回新的记录集
record in set
返回
record
(必须为只包含一个元素的记录集) 是否在
set
中。
record not in set
则刚好相反
set1 <= set2
and
set1 < set2
返回
set1
是否是
set2
的子集
set1 >= set2
and
set1 > set2
返回
set1
是否是
set2
的超集
set1 | set2
返回两个记录集的并集。一个包含出现在两个源记录集中的所有记录的记录集
set1 & set2
返回两个记录集的交集。一个只包含同时存在两个源记录集中的记录的记录集。
set1 - set2
返回一个包含仅出现在
set1
中的记录的记录集
记录集是可迭代的,因此通常的Python工具可用于转换(
map()
,
sorted()
,
ifilter()
,…),然后这些函数返回
list
或
iterator
,删除对结果调用方法或使用集合操作的能力。
因此,记录集提供以下返回记录集本身的操作(如果可能):
Filter
Model.filtered(func)
[
源代码]
# only keep records whose company is the current user's
records.filtered(lambda r: r.company_id == user.company_id)
# only keep records whose partner is a company
records.filtered("partner_id.is_company")
Model.filtered_domain(domain)
[
源代码]
Map
Model.mapped(func)
[
源代码]
对
self
中的所有记录应用
func
,并将结果作为列表或记录集返回(如果
func
返回记录集)。后者返回的记录集的顺序是任意的。
# returns a list of summing two fields for each record in the set
records.mapped(lambda r: r.field1 + r.field2)
提供的函数可以是获取字段值的字符串:
# returns a list of names
records.mapped('name')
# returns a recordset of partners
records.mapped('partner_id')
# returns the union of all partner banks, with duplicates removed
records.mapped('partner_id.bank_ids')
注解
V13开始, 支持多多关系字段访问,像mapped调用那样工作:
records.partner_id # == records.mapped('partner_id')
records.partner_id.bank_ids # == records.mapped('partner_id.bank_ids')
records.partner_id.mapped('name') # == records.mapped('partner_id.name')
Sort
Model.sorted(key=None, reverse=False)
[
源代码]
返回按
key
排序的记录集
self
参数
key
(可调用对象或者str 或者
None
) – 一个参数的函数,为每个记录返回一个比较键,或字段名,或
None
,如果为
None
,记录按照默认模型的顺序排序
reverse
(bool) – 如果为
True
, 返回逆序排序的结果
# sort records by name
records.sorted(key=lambda r: r.name)
继承与扩展(Inheritance and extension)
Odoo提供三种不同的机制,以模块化方式扩展模型:
- 从现有模型创建新模型,向副本中添加新信息,但保留原始模块
- 扩展其他模块中定义的模型,替换以前的版本
- 将模型的一些字段委派给它包含的记录
经典继承
当同时使用
_inherit
和
_name
属性时,Odoo使用现有模型(通过
_inherit
提供)作为base创建新模型。新模型从其base中获取所有字段、方法和元信息(默认值等)。
class Inheritance0(models.Model):
_name = 'inheritance.0'
_description = 'Inheritance Zero'
name = fields.Char()
def call(self):
return self.check("model 0")
def check(self, s):
return "This is {} record {}".format(s, self.name)
class Inheritance1(models.Model):
_name = 'inheritance.1'
_inherit = 'inheritance.0'
_description = 'Inheritance One'
def call(self):
return self.check("model 1")
使用它们:
a = env['inheritance.0'].create({'name': 'A'})
b = env['inheritance.1'].create({'name': 'B'})
a.call()
b.call()
输出:
“This is model 0 record A” “This is model 1 record B”
第二个模型继承了第一个模型的
check
方法及其
name
字段,但重写了
call
方法,就像使用标准Python继承一样。
说明:
以上为官方文档给出的案例,笔者实践发现是无法直接运行的。
模型继承会继承父类中的所有属性,会拷贝字段、属性和方法。
可以同时继承多个模型,比如:
_inherit = ['res.partner', 'md.status.mixin']
扩展
当使用
_inherit
但省略
_name
时,新模型将替换现有模型,实质上就是在原有模型上扩展。这对于将新字段或方法添加到现有模型(在其他模块中创建)或自定义或重新配置它们(例如更改其默认排序顺序)非常有用:
class Extension0(models.Model):
_name = 'extension.0'
_description = 'Extension zero'
name = fields.Char(default="A")
def func():
print('test a')
class Extension1(models.Model):
_inherit = 'extension.0'
description = fields.Char(default="Extended")
def func(): # 重写函数
print('test b')
record = env['extension.0'].create({})
record.read()[0]
返回:
{'name': "A", 'description': "Extended"}
注解
它还会返回各种自动生成的字段,除非它们被禁用了。
env['extension.0'].func({})
返回:
test b
注意:
如果同时继承抽象模块和非抽象模块,并把
_name
配置为非抽象模块,抽象模块的字段也会添加到非抽象模块对应的表
委托(Delegation)
第三种继承机制提供了更大的灵活性(可以在运行时更改),但威力更小:使用
_inherits
模型,将当前模型中未找到的任何字段的查找委托给“children”模型。委托通过
Reference
执行在父模型上自动设置的字段。
主要区别在于意义。使用委托时,模型
has one
而不是
is one
,从而将关系转换为组合而不是继承:
class Screen(models.Model):
_name = 'delegation.screen'
_description = 'Screen'
size = fields.Float(string='Screen Size in inches')
class Keyboard(models.Model):
_name = 'delegation.keyboard'
_description = 'Keyboard'
layout = fields.Char(string='Layout')
class Laptop(models.Model):
_name = 'delegation.laptop'
_description = 'Laptop'
_inherits = {
'delegation.screen': 'screen_id',
'delegation.keyboard': 'keyboard_id',
}
name = fields.Char(string='Name')
maker = fields.Char(string='Maker')
# a Laptop has a screen
screen_id = fields.Many2one('delegation.screen', required=True, ondelete="cascade")
# a Laptop has a keyboard
keyboard_id = fields.Many2one('delegation.keyboard', required=True, ondelete="cascade")
record = env['delegation.laptop'].create({
'screen_id': env['delegation.screen'].create({'size': 13.0}).id,
'keyboard_id': env['delegation.keyboard'].create({'layout': 'QWERTY'}).id,
})
record.size
record.layout
将产生结果:
13.0
'QWERTY'
可以直接修改委托字段:
record.write({'size': 14.0})
警告
使用委托继承时,方法不是被继承的,只有字段
警告
_inherits
或多或少已实现,如果可以的话避免用它(
_inherits
is more or less implemented, avoid it if you can)
- 链式的
_inherits
基本上没有实现,我们不对最终行为做任何保证。(chained
_inherits
is essentially not implemented, we cannot guarantee anything on the final behavior)
字段增量定义
字段定义为模型类的类属性。如果扩展了模型,还可以通过在子类上重新定义具有相同名称和类型的字段来扩展字段定义。在这种情况下,字段的属性取自父类,并由子类中给定的属性覆盖。
例如,下面的第二个类仅在
state
字段上添加工具提示:
class First(models.Model):
_name = 'foo'
state = fields.Selection([...], required=True)
class Second(models.Model):
_inherit = 'foo'
state = fields.Selection(help="Blah blah blah")
入门实践
模型定义
odoo14\custom\estate\models\estate_property_tag.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from odoo import models, fields
class EstatePropertyTag(models.Model):
_name = 'estate.property.tag'
_description = 'estate property tag'
_order = 'name'
name = fields.Char(string='tag', required=True)
color = fields.Integer(string='Color')
odoo14\custom\estate\models\estate_property_offer.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from odoo import models, fields
class EstatePropertyOffer(models.Model):
_name = 'estate.property.offer'
_description = 'estate property offer'
property_id = fields.Many2one('estate.property', required=True)
price = fields.Integer()
odoo14\custom\estate\models\estate_property_type.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from odoo import models, fields
class EstatePropertyType(models.Model):
_name = 'estate.property.type'
_description = 'estate property type'
name = fields.Char(string='name', required=True)
odoo14\custom\estate\models\estate_property.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from odoo import models, fields
class EstateProperty(models.Model):
_name = 'estate.property'
_description = 'estate property table'
_order = 'id desc'
name = fields.Char(required=True)
property_type_id = fields.Many2one("estate.property.type", string="PropertyType")
tag_ids = fields.Many2many("estate.property.tag")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="PropertyOffer")
ORM操作实践
>>> self.env['estate.property.type']
estate.property.type()
# 创建单条记录
>>> self.env['estate.property.type'].create({'name':'house'})
estate.property.type(1,)
# 按id查询记录
>>> self.env['estate.property.type'].browse([1])
estate.property.type(1,)
# 未给定id列表,或者未提供参数的情况下,返回空记录集
>>> self.env['estate.property.type'].browse()
estate.property.type()
>>> self.env['estate.property.type'].browse([])
estate.property.type()
# 复制记录
>>> self.env['estate.property.type'].browse([1]).copy({'name':'garden'})
estate.property.type(2,)
# 针对仅获取单条记录的记录集,可通过 records.fieldName 的方式引用对应字段(读取字段值,或者给字段赋值)
>>> self.env['estate.property.type'].browse([2]).name
'garden'
# 更新记录
>>> self.env['estate.property.type'].browse([1]).name
'house'
>>> self.env['estate.property.type'].browse([1]).write({'name':'garden'})
True
>>> self.env['estate.property.type'].browse([1]).name
'garden'
# 针对仅获取单条记录的记录集,可通过 records.fieldName 的方式引用对应字段(读取字段值,或者给字段赋值)
>>> self.env['estate.property.type'].browse([1]).name = 'house'
>>> self.env['estate.property.type'].browse([1]).name
'house'
# 不能直接通过以下方式,试图在write函数指定id的方式来更新记录 # 不会修改任何记录,也未新增任何记录
>>> self.env['estate.property.type'].write({'id':1, 'name':'apartment'})
True
>>> self.env['estate.property.type'].browse([1]).name
'house'
# 通过search api查询记录集
>>> self.env['estate.property.type'].search([])
estate.property.type(1, 2)
# 批量创建记录
# 创建测试用数据
>>> self.env['estate.property.tag'].create([{'name': 'tag1', 'color': 1}, {'name': 'tag1', 'color': 2}, {'name': 'tag1', 'color': 3}])
estate.property.tag(1, 2, 3)
# 注意:Many2one类型字段的值,必须设置为对应记录的主键id
>>> self.env['estate.property'].create({'name': 'house in beijing', 'property_type_id': 1, 'tag_ids':[(0,0, {'name': 'tag1', 'color': 3})]})
estate.property(1,)
>>> self.env['estate.property'].search([])
estate.property(1,)
# 查询关系字段值
>>> self.env['estate.property'].browse([1]).property_type_id # Many2one
estate.property.type(1,)
>>> self.env['estate.property'].browse([1]).tag_ids # Many2many
estate.property.tag(4,)
# 更新Many2many关系字段值
>>> self.env['estate.property'].browse([1]).tag_ids.write({'name': 'tag4', 'color': 4})
True
>>> self.env['estate.property'].browse([1]).tag_ids.color
4
>>> self.env['estate.property.tag'].search([])
estate.property.tag(1, 2, 3, 4)
# 查询关系字段值
>>> self.env['estate.property'].browse([1]).offer_ids # One2many
estate.property.offer()
## 更新One2many关系字段值
# 为关系字段创建关联记录
# (0, 0, values)
# 从提供的`values`字典创建新记录。
>>> self.env['estate.property'].browse([1]).offer_ids = [(0, 0, {'property_id':1})]
>>> self.env['estate.property'].browse([1]).offer_ids
estate.property.offer(1,)
>>> self.env['estate.property'].browse([1]).offer_ids.property_id
estate.property(1,)
# 更新关系字段所代表记录对象的属性值
# (1, id, values)
# 使用 values 字典中的值更新id值为给定 id 值的现有记录。不能在create()中使用。
>>> self.env['estate.property'].browse([1]).offer_ids = [(1, 1, {'price': 30000})]
>>> self.env['estate.property'].browse([1]).offer_ids.price
30000
# 删除关系字段关联记录
# (3, id, 0)
# 从记录集中删除id为id的记录,但不从数据库中删除它,可以理解为仅解除关联。不能在create()中使用。
>>> self.env['estate.property'].browse([1]).offer_ids = [(3,1,0)]
>>> self.env['estate.property'].browse([1]).offer_ids
estate.property.offer()
# 将已存在记录同关系字段关联
# (4, id, 0)
# 添加一条id为id已存在记录到记录集
>>> self.env['estate.property.offer'].browse([1])
estate.property.offer(1,)
>>> self.env['estate.property'].browse([1]).offer_ids = [(4,1,0)]
>>> self.env['estate.property'].browse([1]).offer_ids
estate.property.offer(1,)
# 为关系字段一次创建多条关联记录
>>> self.env['estate.property'].browse([1]).offer_ids = [(0, 0, {'property_id':1, 'price': 100000}),(0, 0, {'property_id':1, 'price': 200000}), (0, 0, {'property_id':1, 'price': 200000}), (0, 0, {'property_id':1, 'price': 300000})]
>>> self.env['estate.property'].browse([1]).offer_ids
estate.property.offer(1, 2, 3, 4, 5)
# 替换关系字段关联的记录
# (6, 0, ids)
# 根据ids列表,替换所有已存在记录, 等价于使用命令(5, 0, 0),随后对ids中的每个id使用命令(4, id, 0)。
>>> self.env['estate.property'].browse([1]).offer_ids = [(3,1,0),(3,2,0)]
>>> self.env['estate.property'].browse([1]).offer_ids
estate.property.offer(3, 4, 5)
>>> self.env['estate.property'].browse([1]).offer_ids = [(6, 0, [1,2])] # 报错, 因为ID 1,2 对应的记录,其Many2one字段值为null
# 为Many2many关系字段创建多条关联记录
>>> self.env['estate.property'].create({'name': 'house in shanghai'})
estate.property(2,)
>>> self.env['estate.property'].browse([2])
estate.property(2,)
>>> self.env['estate.property'].browse([2]).tag_ids
estate.property.tag()
>>> self.env['estate.property'].browse([2]).tag_ids = [(0, 0, {'name': 'tag5', 'color': 5}), (0, 0, {'name': 'tag6', 'color': 6}), (0, 0, {'name': 'tag7', 'color': 7})]
>>> self.env['estate.property'].browse([2]).tag_ids
estate.property.tag(5, 6, 7)
# 删除关系字段关联的记录
# (2, id, 0)
# 从记录集中删除id为id的记录,然后(从数据库中)删除它,不能在create()中使用
>>> self.env['estate.property'].browse([2]).tag_ids = [(2, 5, 0)]
2023-01-29 08:48:25,491 15984 INFO odoo odoo.models.unlink: User #1 deleted estate.property.tag records with IDs: [5]
>>> print( self.env['estate.property.tag'].browse([5]).exists())
estate.property.tag()
>>> if self.env['estate.property.tag'].browse([5]).exists():
... print('exists record with id equal 5')
...
>>>
# 创建测试用数据
>>> self.env['estate.property.tag'].create({'name': 'tag8', 'color': 8})
estate.property.tag(8,)
>>> self.env['estate.property.tag'].create({'name': 'tag9', 'color': 9})
estate.property.tag(9,)
>>> self.env['estate.property'].browse([2])
estate.property(2,)
# 替换关系字段关联的记录
# (6, 0, ids)
# 根据ids列表,替换所有已存在记录, 等价于使用命令(5, 0, 0),随后对ids中的每个id使用命令(4, id, 0)。
>>> self.env['estate.property'].browse([2]).tag_ids
estate.property.tag(6, 7)
>>> self.env['estate.property'].browse([2]).tag_ids = [(6, 0 , [8, 9])]
>>> self.env['estate.property'].browse([2]).tag_ids
estate.property.tag(8, 9)
>>>
# 通过mapped获取记录字段值(关联记录的属性值)列表
>>> self.env['estate.property'].browse([2]).tag_ids.mapped('name')
['tag8', 'tag9']
>>> self.env['estate.property'].browse([2]).mapped('tag_ids')
estate.property.tag(8, 9)
>>> self.env['estate.property'].browse([2]).mapped('tag_ids').mapped('id'))
[8, 9]
# search api 应用
# 搜索域
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)])
estate.property.tag(6, 7, 8, 9)
# 偏移
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)], offset=1)
estate.property.tag(7, 8, 9)
# 限制返回记录集中的最大记录数
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)], offset=1, limit=2)
estate.property.tag(7, 8)
# 返回记录集中的记录排序
# 降序
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)], offset=1, limit=2, order = 'id desc')
estate.property.tag(8, 7)
# 升序
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)], offset=1, limit=2, order = 'id')
estate.property.tag(7, 8)
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)], offset=1, limit=2, order = 'id asc')
estate.property.tag(7, 8)
# 仅返回记录数
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)], count=True)
4
# 利用search_count api实现等价效果
>>> self.env['estate.property.tag'].search_count(args=[('id', '>', 5)])
4
# 搜索域条件组合
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5),('color', '<', 8)])
estate.property.tag(6, 7)
# 获取记录(集)信息
# ids
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)]).ids
[6, 7, 8, 9]
# env
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)]).env
<odoo.api.Environment object at 0x0000020E31C80080>
# name_get api 使用
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)]).name_get()
[(6, 'tag6'), (7, 'tag7'), (8, 'tag8'), (9, 'tag9')]
# get_metadata api 使用
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)]).get_metadata()
[{'id': 6, 'create_uid': (1, 'OdooBot'), 'create_date': datetime.datetime(2023, 1, 29, 8, 41, 10, 551001), 'write_uid': (1, 'OdooBot'), 'write_date': datetime.datetime(2023, 1, 29, 8,41, 10, 551001), 'xmlid': False, 'noupdate': False}, {'id': 7, 'create_uid': (1, 'OdooBot'), 'create_date': datetime.datetime(2023, 1, 29, 8, 41, 10, 551001), 'write_uid': (1, 'OdooBot'), 'write_date': datetime.datetime(2023, 1, 29, 8, 41, 10, 551001), 'xmlid': False, 'noupdate': False}, {'id': 8, 'create_uid': (1, 'OdooBot'), 'create_date': datetime.datetime(2023,1, 29, 8, 41, 10, 551001), 'write_uid': (1, 'OdooBot'), 'write_date': datetime.datetime(2023, 1, 29, 8, 41, 10, 551001), 'xmlid': False, 'noupdate': False}, {'id': 9, 'create_uid': (1, 'OdooBot'), 'create_date': datetime.datetime(2023, 1, 29, 8, 41, 10, 551001), 'write_uid': (1, 'OdooBot'), 'write_date': datetime.datetime(2023, 1, 29, 8, 41, 10, 551001), 'xmlid': False, 'noupdate': False}]
# 利用 read_group 实现按组读取
>>> self.env['estate.property.tag'].create({'name': 'tag10', 'color': 9})
estate.property.tag(10,)
>>> self.env['estate.property.tag'].read_group([], fields=['color'], groupby=['color'])
[{'color_count': 1, 'color': 6, '__domain': [('color', '=', 6)]}, {'color_count': 1, 'color': 7, '__domain': [('color', '=', 7)]}, {'color_count': 1, 'color': 8, '__domain': [('color', '=', 8)]}, {'color_count': 2, 'color': 9, '__domain': [('color', '=', 9)]}]
# 获取字段定义
>>> self.env['estate.property.tag'].fields_get(['name'])
{'name': {'type': 'char', 'change_default': False, 'company_dependent': False, 'depends': (), 'manual': False, 'readonly': False, 'required': True, 'searchable': True, 'sortable': True
, 'store': True, 'string': 'tag', 'translate': False, 'trim': True}}
# 回滚
>>> self.env.cr.rollback()
>>> self.env['estate.property.tag'].search(args=[('id', '>', 5)], offset=1, limit=2, order = 'id')
estate.property.tag()
# 执行 sql
self.env.cr.execute('TRUNCATE TABLE estate_property_tag_test CASCADE;')
self.env.cr.commit()
# 重置自增主键ID 为1(每个表的主键ID存在名为 tableName_id_seq 的序列中)
self.env.cr.execute('ALTER SEQUENCE estate_property_tag_test_id_seq RESTART WITH 1;')
self.env.cr.commit()
>>> self.env['estate.property.tag'].create([{'name': 'tag1', 'color': 1}, {'name': 'tag2', 'color': 2}, {'name': 'tag3', 'color': 3}])
estate.property.tag(1, 2, 3)
# 批量更新记录字段值 #记录集存在多条记录的情况下,不能通过 records.fieldName = 目标值 实现批量更新
>>> self.env['estate.property.tag'].browse([1,3]).write({'color':1})
True
>>> self.env['estate.property.tag'].browse([1,3]).mapped('color')
[1, 1]
# 修改查询记录集context
>>> self.env['estate.property.tag'].browse([]).env.context
{'lang': 'en_US', 'tz': 'Europe/Brussels'}
>>> self.env['estate.property.tag'].with_context(is_sync=False).browse([]).env.context
{'lang': 'en_US', 'tz': 'Europe/Brussels', 'is_sync': False}
# with_context和sudo共存时的使用方式
>>> self.env['estate.property.tag'].with_context(is_sync=False).sudo().browse([]).env.context
{'lang': 'en_US', 'tz': 'Europe/Brussels', 'is_sync': False}
>>> self.env['estate.property.tag'].sudo().with_context(is_sync=False).browse([]).env.context
{'lang': 'en_US', 'tz': 'Europe/Brussels', 'is_sync': False}
# 修改创建记录时返回记录的context(更新记录(write)也是一样的用法)
# 如此,可以通过重写对应模型的create或者write方法,并在方法中通过self.env.context获取目标key值,进而执行需求实现需要采取的动作,参见下文
>>> self.env['estate.property.tag'].with_context(is_sync=False).create({'name': 'tag4', 'color': 4}).env.context
{'lang': 'en_US', 'tz': 'Europe/Brussels', 'is_sync': False}
# 删除记录
>>> self.env['estate.property.tag'].search([])
estate.property.tag(1, 2, 3, 4)
>>> self.env['estate.property.tag'].search([('id', '>', 2)]).unlink()
2023-01-29 09:55:47,796 15984 INFO odoo odoo.models.unlink: User #1 deleted estate.property.tag records with IDs: [3, 4]
True
# 遍历记录集
>>> for record_set in self. self.env['estate.property.tag.test'].search([]):
... print(record_set)
...
estate.property.tag.test(1,)
estate.property.tag.test(2,)
获取context上下文目标key值示例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from odoo import models, fields,api
class EstatePropertyTag(models.Model):
_name = 'estate.property.tag'
_description = 'estate property tag'
_order = 'name'
name = fields.Char(string='tag', required=True)
color = fields.Integer(string='Color')
@api.model
def create(self, vals_list):
res = super(EstatePropertyTag, self).create(vals_list)
# 获取上下文目标key值
if not self.env.context.get('is_sync', True):
# do something you need
return res
参考连接
https://www.odoo.com/documentation/14.0/zh_CN/developer/reference/addons/orm.html#