Odoo是一个开源ERP框架,遵循MVC设计模式,本文想通过模型、字段、记录集、装饰器方法等来全面介绍Odoo的ORM操作。本文基于Odoo V13。
实时计算的字段:字段也可以是通过实时计算得来的,指定compute参数,并且当用到其他字段时需要使用depends声明。
Odoo的模型有三种基类:
- Model:用于常规的数据库持久化模型
- TransientModel:用于存储在数据库中的临时数据,但会经常自动清理
- AbstractModel:用于要由多个继承模型共享的抽象超类
每个模型实例都是一个“记录集”,即模型记录的有序集合。记录集由browser(),search()或者字段访问之类的方法返回。记录没有显示表示:一个记录表现为记录集的一条记录。
如果要创建一个不能实例化的类,_register属性要设置为False。
以下为模型的内置属性:
- _auto = False
决定是否创建数据库表(默认值为True)。如果设置为False,则需要重写init()函数来创建表。- _table = None
当_auto设置成False时,该值为创建的表名;默认情况下会自动生成一个。- _sequence = None
用于字段"ID"的SQL序列- _sql_constrains = []
通过一个[(name, sql_def, message)]的三元组列表定义表的SQL级别约束。- _register = True
在ORM注册表中不可见。- _name = None
业务对象的名称。- _description = None
业务对象的描述。- _inherit = None
如果设置了_name属性,它的取值是单个或多个父级的模型名称;没有设置_name属性时,只能是单个模型名称。- _inherits = {}
定义上级模型关联使用的字段
- _rec_name = None
可选的name字段名称,供osv的name_get()方法使用,默认值:name。- _order = ‘id’
查找结果的默认排序字段- _check_company_auto = False
在写入和创建时,调用_check_company以确保公司在具有check_company = True作为属性的关系字段上的一致性。- _parent_name = ‘parent_id’
用作父字段的many2one字段- _parent_store = False
- _abstract = True
判断模型是否为抽象模型- _transient = False
判断模型是否为瞬态模型- _date_name = ‘date’
用于默认日历视图的字段- _fold_name = ‘fold’
确定看板视图中折叠组的字段
class odoo.fields.Field
字段描述符包含了字段的定义,并且管理记录上相应字段的访问和分配。实例化字段时,可以提供以下属性:
- string(string) – 用户可见的字段的标签
- help(string) – 用户可见的该字段的提示
- readonly(boolean) – 字段是否设置为只读,默认为False
- required(boolean) – 字段是否设置为必填,默认为False
- index(boolean) – 字段是否作为索引保存在数据库中,默认为False
- default – 字段的默认值,可以是一个特定的值或者一个有返回值的函数。
- states – 用字典封装视图里的 属性-值 对,如"readonly",“required”,“invisible”…
- groups – 用逗号分隔的xml id列表,可以限制用户对字段的访问
- copy(boolean) – 指定当数据行被复制时该字段是否被复制,默认为True,实时计算字段和one2many字段默认为False
- store(boolean) – 字段是否设置为能存储在数据库中,默认为True,实时计算字段默认为False
实时计算字段:
可定义一个字段,它的值通过指定函数实时计算得来,定义实时计算字段只需要指定compute属性即可,它有以下几种参数:
- odoo.fields.Boolean – 布尔类型
- odoo.fields.Char – 字符串字段,可以有长度限制
- size(int) – 字符串最大长度
- translate – 启用字段的翻译
- trim(bool) – 决定值是否可以小于size,默认为True,该操作只能作用于web客户端
- odoo.fields.Float – 浮点型,可接受digits参数(total,decimal)指定位数
- odoo.fields.Integer – 整形
- odoo.fields.Binary – 存储二进制文件
- odoo.fields.Html – 存储html内容
- odoo.fields.Image – 存储图像
- odoo.fields.Monetary – 存储货币值
- odoo.fields.Selection – 多个值之间的单个选择
selection – 指定字段的取值列表,为(value, string)列表或一个模型的方法或方法名
selection_add – 当该字段来自重定义时,它提供selection参数的扩展,为(value, string)列表
- odoo.fields.Text – 文本类型,用于存储较多内容
- odoo.fields.Date – date类型
static today() – 以ORM期望的格式返回当前日期
static context_today() – 返回一个客户端时区的当前日期,可以接收一个datetime格式的参数
static to_date() – 将一个值转换成date对象
static to_string() – 将一个date或datetime对象转换成string
- odoo.fields.Datetime – datetime类型(方法参考date类型)
static add – 添加一个值
static context_timestamp
static now – 以ORM期望的格式返回当前日期和时间
static to_datetime()
static to_string()
static today()
odoo.fields.Many2one
该字段的获取到的集合的记录数量只会是0(无记录)或1(单条记录)
参数列表:
comodel_name(string) – 目标模型名称,除非是关联字段否则该参数必选
domain – 可选,用于在客户端筛选数据的domain表达式
context – 可选,用于在客户端处理时使用
ondelete – 当所引用的数据被删除时采取的操作,取值:‘set null’, ‘restrict’, ‘cascade’
auto_join – 在搜索该字段时是否自动生成JOIN条件,默认False
delegate – 设置为True时可以通过当前model访问目标model的字段,与_inherits功能相同odoo.fields.One2many
该字段的值是目标model的所有记录
参数列表:
comodel_name – 目标模型名称,
inverse_name – 在comodel_name 中对应的Many2one字段
domain – 可选,用于在客户端筛选数据的domain表达式
context – 可选,用于在客户端处理时使用
auto_join – 在搜索该字段时是否自动生成JOIN条件,默认False
limit(integer) – 可选,在读取时限制数量odoo.fields.Many2many
该字段的值为一个数据集合
参数:
comodel_name – 目标模型名称,除非是关联字段否则该参数必选
relation – 可选,关联的model在数据库存储的表名,默认采用comodel_name获取数据
column1 – 可选,与relation表记录相关联的列名
column2 – 可选,与relation表记录相关联的列名
domain – 可选,用于在客户端筛选数据的domain表达式
context – 可选,用于在客户端处理时使用
limit(integer) – 可选,在读取时限制数量odoo.fields.Reference
基于odoo.fields.Selection
- odoo.fields.id – 记录id
- odoo.fields.create_date – 记录创建的时间
- odoo.fields.create_uid – 创建人id,关联到res.users
- odoo.fields.write_date – 记录最近修改时间
- odoo.fields.write_uid – 记录最近修改者的id,关联到res.users
除了内置字段外,还保留了一些字段名称用于预定义的行为,当需要相关行为时,应在模型上定义它们:
odoo.fields.name – _rec_name的默认值,在需要用来展示的时候使用
odoo.fields.active – -- 设置记录的全局可见性,当值为False时通过search和list是获取不到的
odoo.fields.state – 对象的生命周期阶段,通过fileds的states属性使用
odoo.fields.parent_id – 用来对树形结构的记录排序,并激活domain表达式的child_of运算符
odoo.fields.company_id – 用于Odoo多公司行为的主字段名称
通过记录集执行模型与记录的交互,记录集是同一模型的记录的有序集合。
在模型上定义的方法本身是操作模型的记录集,self就是一个记录集
记录集提供“活动记录”接口:可以直接从记录中读取和写入模型字段作为属性。
When accessing non-relational fields on a recordset of potentially multiple records, use mapped():
total_qty = sum(self.mapped(‘qty’))
还可以像dict项一样访问字段值,对于动态字段名称,这比getattr()更优雅,更安全。 设置字段的值会触发数据库更新:
访问关系字段(Many2one,One2many,Many2many)始终返回记录集,如果未设置该字段,则为空。
odoo会为记录保留一份缓存,它有一种内置的预读取机制,通过缓存来提升性能
Odoo的API模块定义了Odoo环境和装饰器方法
odoo.api.depends_context(*args)
odoo.api.returns(model, downgrade=None, upgrade=None)
返回一个获取model实例的方法的装饰器
参数列表
model: 模型名称,self代表当前模型
downgrade:一个将value值从记录形式转化为传统形式的方法:downgrade(self, value, *args, **kwargs)
upgrade: 一个将value从传统形式转化为记录形式的方法:upgrade(self, value, *args, **kwargs)
odoo.api.model(method)
在记录行方式下装饰一个内容不明确、但模型明确的方法;这种方法的self参数是一个对象的引用而非一个记录集。
odoo.api.model_create_multi(method)
装饰一个采用多字典列表并创建多个记录的方法。 可以使用单个字典或多字典列表调用该方法
运行环境保存了很多ORM相关的变量:数据库查询游标、当前用户、元数据,还存有缓存。所有的model数据集都有不可改变的环境变量,可使用env来访问,如records.env.user,records.env.cr,records.env.context,records.env.su,运行环境还可用于为其他模型初始化一个空的集合并对该模型进行查询
env.ref(xml_id, raise_if_not_found=True) – 返回给定xml_id对应的记录
env.lang – 返回当前语言
env.user – 返回当前用户
env.company – 返回当前公司
env.companys – 以记录集的格式返回当前用户的所有有效公司
model.with_context() – 一个参数时可用于替换当前运行环境的context,多个参数时通过keyword添加到当前运行环境context或单参数时设置的context
model.with_user() – 以普通用户身份返回在指定环境下的新版记录集合
model.with_env() – 返回在指定环境下的新版记录集合
model.sudo() – 使用现有数据集创建一个新运行环境,得到一个基于新运行环境的数据集的拷贝,并且在拷贝中,有所有访问权限
利用游标cr,在复杂查询或者性能不佳的数据库操作时,直接通过写SQL的方式对数据进行增删改查。
self.env.cr.execute(“some_sql”, param1, param2, param3)
由于环境中存在大量缓存,所以在进行增删改操作时有必要清理缓存,否则可能会有未知的错误。
- Model.invalidate_cache(fnames=None, ids=None)
修改某些记录后,使记录缓存无效。 如果fname和id均为None,则将清除整个缓存。参数:
fnames – 已修改字段的列表
ids – 已修改字段id的列表
- Model.create(vals_list) -> records
为模型创建新记录。
例: [{ ‘field_name’ : field_value , … }, … ]- Model.copy(default=None)
重复记录self将其更新为默认值- Model.default_get(fields_list) -> default_values
返回中的字段的默认值fields_list。默认值由上下文,用户默认值和模型本身确定。- Model.name_create(name) -> record
通过create()仅提供一个值进行调用来创建新记录:新记录的显示名称。- Model.write(vals)
使用提供的值更新当前集中的所有记录。
例子:{ ‘foo’ : 1 , ‘bar’ : “ Qux” }- Model.flush(fnames=None, records=None)
处理所有待处理的重新计算(或至少存在给定的字段名称,fnames如果存在),并将待处理的更新刷新到数据库。
- Model.browse([ids]) → records
返回当前环境中作为参数提供的id的记录集。
例子:self.browse([7, 18, 12])- Model.search(args[, offset=0][, limit=None][, order=None][, count=False])
根据args参数里的domain表达式来搜索所有记录,参数列表:
1.args domain表达式,为空时返回所有记录
2.offset (int) 从第几条记录开始取
3.limit (int) 返回记录行数的最大值
4.order (str) 排序的字段
5.count (bool) 当值为True的时候只返回匹配记录的条数- Model.search_count(args) → int
返回根据给定domain表达式参数查询所得到的记录条数- Model.name_search(name=’’, args=None, operator=‘ilike’, limit=100) → records
返回根据name条件来查询,并满足args指定的domain表达式的记录集合
name (str) – 用来匹配的name字符串
args (list) – domain表达式列表
operator (str) – 用来匹配的操作符,如: ‘like’ , ‘=’.
limit (int) – 可选参数,最多返回的记录行数- Model.read([fields])
从self里读取指定的字段,专供rpc使用- Model.read_group(domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True)
得到一个通过groupby参数分组后的记录的列表
domain 搜索条件的domain表达式列表 [[‘field_name’, ‘operator’, ‘value’], …]
fields (list) 需要展示出来的字段列表
groupby (list) 用来分组的表达式列表,分组表达式可以是单个字段或者一个函数如:‘field:groupby_function’,目前函数只支持 ‘day’, ‘week’, ‘month’, ‘quarter’ or 'year’并且只能作用在date和datetime字段上
offset (int) 可选参数,代表从哪个条记录开始取
limit (int) 可选参数,代表取出多少条记录
orderby (list) 可选参数,和search函数的orderby参数一样
lazy (bool) 值为true时,记录只会根据第一个groupby值进行分组,后面的groupby参数会被存在__context 中;值为false时会把所有分组条件一起执行
Fields/Views(字段/视图)
Model.fields_get([fields][, attributes])返回每个字段的定义。
Model.fields_view_get([view_id | view_type=‘form’])
获取请求的视图的详细组成,例如字段,模型,视图架构
Search domains(搜索条件)
domain是一个条件列表,每个条件是一个(field_name, operator, value)的三元素元组
- Model.unlink()
从当前记录集删除一条记录
- Model.ids – 返回记录集id列表
- odoo.models.env – 返回给定记录集的环境
- Model.exists() → records – 返回self存在的记录子集
- Model.ensure_one() – 验证当前记录集是否保存单个记录
- Model.name_get() → [(id, name), …] – 返回中的记录的文本表示形式self。默认情况下,这是display_name字段的值
- Model.get_metadata() – 返回有关给定记录的一些元数据(uid,create_date,write_date…)
集合运算
filtered() 返回满足条件的数据集
sorted() 返回根据提供的键排序之后的结果
mapped() 返回应用了指定函数之后的结果集
提供的函数可以是用于获取字段值的字符串:
从V13开始,支持多关系字段访问,并且就像映射调用一样工作:
Odoo有三种模块化的模型继承机制
- 根据原有模型创建一个全新的模型,并基于新创建的模型修改,新模型与已存在的试图兼容,并保存在同一张表中。
- 从其他模块中扩展模型,并进行替换,一般用于复制,已存在的视图会忽略新建的模型,数据保存在新的数据表中。
当_inherit和_name属性一起使用时,odoo基于原有模型创建一个新模型,新的模型会自动继承原模型的字段、方法等