ORM API

对象关系映射模块:
  • 分层结构

  • 约束一致性和验证

  • 对象元数据取决于其状态

  • 通过复杂查询进行优化处理(一次执行多个操作)

  • 默认字段值

  • 权限优化

  • 持久化对象:DB postgresql

  • 数据转换

  • 多级缓存系统

  • 两种不同的继承机制

  • 丰富的字段类型集合:
    • 传统类型(varchar、integer、boolean等)

    • 关系型(一对多,多对一,多对多)

    • 功能性的

模型

模型字段定义为模型本身的属性:

from odoo import models, fields
class AModel(models.Model):
    _name = 'a.model.name'

    field1 = fields.Char()

警告

这意味着您不能定义一个与方法同名的字段,否则后者将会悄悄地覆盖前者。

默认情况下,字段的标签(用户可见名称)是字段名称的大写版本,可以使用 string 参数覆盖。

field2 = fields.Integer(string="Field Label")

对于字段类型和参数的列表,请参见 字段参考

默认值是作为字段参数定义的,可以是一个值:

name = fields.Char(default="a value")

或者作为一个被调用以计算默认值的函数,该函数应该返回该值:

def _default_name(self):
    return self.get_value()

name = fields.Char(default=lambda self: self._default_name())

API

class odoo.models.BaseModel[源代码]

Odoo模型的基类。

Odoo模型是通过继承以下模型之一创建的:

  • Model 用于常规数据库持久化模型

  • TransientModel 用于存储在数据库中的临时数据,但会定期自动清理

  • AbstractModel 用于被多个继承模型共享的抽象超类

系统会自动为每个数据库实例化每个模型一次。这些实例代表每个数据库上可用的模型,并且取决于该数据库上安装的模块。每个实例的实际类是由创建和继承相应模型的Python类构建的。

每个模型实例都是一个“recordset”,即模型记录的有序集合。Recordsets由诸如 browse()search() 或字段访问等方法返回。记录没有显式的表示:一个记录被表示为一个记录集合的一个记录。

要创建一个不应该被实例化的类,可以将 _register 属性设置为 False。

_auto = False

是否应该创建数据库表。如果设置为 False,请重写 init() 来创建数据库表。

自动默认为 True 用于 ModelTransientModel,为 False 用于 AbstractModel

小技巧

要创建一个没有任何表的模型,请继承自 AbstractModel

_log_access

ORM 是否应自动生成并更新 访问日志字段

默认为 _auto 属性设置的任何值。

_table = None

SQL 表名,如果 _auto 为真,则模型使用该表名

_sql_constraints = []

SQL 约束条件 [(名称,SQL定义,消息)]

_register = False

注册表可见性

_abstract = True

Whether the model is abstract.

另请参阅

AbstractModel

_transient = False

模型是否为 transient.

另请参阅

TransientModel

_name = None

模型名称(点符号表示法,模块命名空间)

_description = None

模型的非正式名称

_inherit = ()

Python继承的模型:

类型

字符串或字符串列表

注解

  • 如果 _name 被设置,继承的父模型的名称

  • 如果 _name 未设置,则为要就地扩展的单个模型的名称

_inherits = {}

字典{‘parent_model’: ‘m2o_field’}将父业务对象的_name映射到相应的外键字段名称以使用:

_inherits = {
    'a.model': 'a_field_id',
    'b.model': 'b_field_id'
}

实现基于组合的继承:新模型公开继承模型的所有字段,但不存储它们:值本身仍存储在链接记录上。

警告

如果在 _inherits -ed模型中定义了多个具有相同名称的字段,则继承的字段将对应于最后一个(按继承列表顺序)。

_rec_name = None

用于标记记录的字段,默认为: name

_order = 'id'

搜索结果的默认排序字段

_check_company_auto = False

在写入和创建时,调用 _check_company 来确保具有 check_company=True 属性的关系字段的公司一致性。

_parent_name = 'parent_id'

用作父级字段的many2one字段

_parent_store = False

设置为True以计算parent_path字段。

parent_path 字段旁边,设置了记录的树形结构的索引存储,以便使用 child_ofparent_of 域操作符在当前模型的记录上进行更快的层次查询。

_fold_name = 'fold'

用于确定看板视图中折叠组的字段

抽象模型

odoo.models.AbstractModel[源代码]

alias of odoo.models.BaseModel

模型

class odoo.models.Model(env, ids, prefetch_ids)[源代码]

常规数据库持久化Odoo模型的主超类。

Odoo模型是通过继承此类创建的:

class user(Model):
    ...

系统稍后将在每个安装了该类模块的数据库中实例化该类。

_auto = True

是否应该创建数据库表。如果设置为 False,请重写 init() 来创建数据库表。

自动默认为 True 用于 ModelTransientModel,为 False 用于 AbstractModel

小技巧

要创建一个没有任何表的模型,请继承自 AbstractModel

_abstract = False

Whether the model is abstract.

另请参阅

AbstractModel

瞬态模型

class odoo.models.TransientModel(env, ids, prefetch_ids)[源代码]

瞬态记录的模型超类,用于临时持久化,并定期进行清理。

瞬态模型具有简化的访问权限管理,所有用户都可以创建新记录,并且只能访问他们创建的记录。超级用户可以无限制地访问所有瞬态模型记录。

_transient_max_count = False

最大瞬态记录数,如果为 0 则无限制

_transient_max_hours = 1.0

最大空闲生命周期(以小时为单位),如果为 0 则无限制

_transient_vacuum()[源代码]

清理瞬态记录。

每当达到 _transient_max_count_transient_max_hours 条件(如果有的话),它会从临时模型表中取消链接旧记录。

实际清理只会在每5分钟内发生一次。这意味着可以频繁调用此方法(例如每次创建新记录时)。

同时启用max_hours和max_count的示例:

假设max_hours = 0.2(即12分钟),max_count = 20,表中有55行,其中有10行在最近5分钟内创建/更改,另外12行在5到10分钟之间创建/更改,其余的行创建/更改时间超过12分钟。

  • 基于时间的清理将保留最近12分钟内创建/更改的22行记录

  • 计数基础的清理将会清除另外12行。不仅仅是2行,否则每次添加都会立即导致最大值再次达到。

  • 过去5分钟内创建/更改的10行记录将不会被删除

字段

class odoo.fields.Field[源代码]

字段描述符包含字段定义,并管理记录上相应字段的访问和赋值。在实例化字段时,可以提供以下属性:

参数
  • string (str) – 用户可见的字段标签;如果未设置,则ORM将使用类中的字段名称(大写)。

  • help (str) – 用户可见的字段工具提示

  • invisible – 字段是否可见(布尔型,默认为 False

  • readonly (bool) – 字段是否只读(默认: False ) 这只对用户界面有影响。代码中的任何字段赋值都将起作用(如果字段是存储字段或可逆字段)。

  • required (bool) – 字段的值是否必需(默认值: False

  • index (str) – 字段是否在数据库中建立索引,以及索引的类型。注意:这对于非存储和虚拟字段没有影响。可能的值有: * "btree"True:标准索引,适用于many2one * "btree_not_null":没有NULL值的BTREE索引(当大多数值为NULL或者从不搜索NULL时有用) * "trigram":带有trigrams的广义倒排索引(适用于全文搜索) * NoneFalse:无索引(默认)

  • default (value or callable) – 字段的默认值;可以是静态值,也可以是一个接收记录集并返回值的函数;使用 default=None 来丢弃字段的默认值。

  • states (dict) – 将状态值映射到UI属性值对列表的字典;可能的属性有: readonlyrequiredinvisible 。 .. warning:: 任何基于状态的条件都需要在客户端UI上可用的 state 字段值。通常通过将其包含在相关视图中来实现,如果对最终用户不相关,则可能将其设置为不可见。

  • groups (str) – 逗号分隔的组xml id列表(字符串);这将限制字段访问仅限于给定组的用户

  • company_dependent (bool) – 是否依赖于当前公司的字段值;该值不存储在模型表中。它被注册为 ir.property 。当需要company_dependent字段的值时,会搜索与当前公司(和当前记录,如果存在)关联的 ir.property 。如果在记录上更改了值,则会修改当前记录的现有属性(如果存在),或为当前公司和res_id创建一个新的属性。如果在公司端更改了值,则会影响所有未更改该值的记录。

  • copy (bool) – 是否在复制记录时复制字段值(默认情况下,对于普通字段为 True ,对于 one2many 和计算字段(包括属性字段和相关字段)为 False

  • store (bool) – 字段是否存储在数据库中(默认值:True,计算字段为``False``)

  • group_operator (str) – 用于 read_group() 方法在对该字段进行分组时使用的聚合函数。支持的聚合函数有: * array_agg :将值(包括空值)连接成一个数组 * count :行数 * count_distinct :不同行的数量 * bool_and :如果所有值都为真,则为真;否则为假 * bool_or :如果至少有一个值为真,则为真;否则为假 * max :所有值的最大值 * min :所有值的最小值 * avg :所有值的平均值 * sum :所有值的总和

  • group_expand (str) – 用于在当前字段分组时扩展read_group结果的函数。.. code-block:: python @api.model def _read_group_selection_field(self, values, domain, order): return [‘choice1’, ‘choice2’, …] # 可用的选择项。 @api.model def _read_group_many2one_field(self, records, domain, order): return records + self.search([custom_domain])

计算字段

参数
  • compute (str) – 计算字段的方法的名称 .. seealso:: 高级 字段/计算字段

  • precompute (bool) – 是否在记录插入数据库之前计算字段。当字段可以在记录插入之前计算时,应该手动将某些字段指定为precompute=True。(例如,避免基于搜索/读取组的统计字段,many2one链接到上一个记录等)(默认值: False ).. warning:: 仅当create()未提供显式值和默认值时,预计算才会发生。这意味着默认值会禁用预计算,即使字段被指定为precompute=True。如果给定模型的记录不是批量创建的,则预计算字段可能会适得其反。考虑一次创建多条记录的情况。如果字段未预计算,它通常会在flush()时批量计算,并且预取机制将有助于使计算效率高。另一方面,如果字段被预计算,计算将逐个进行,因此无法利用预取机制。根据上述备注,预计算字段可能在one2many的行上很有趣,这些行通常由ORM自身批量创建,前提是它们是通过写入包含它们的记录来创建的。

  • compute_sudo (bool) – 字段是否应该作为超级用户重新计算以绕过访问权限(对于存储字段默认为 True ,对于非存储字段默认为 False

  • recursive (bool) – 字段是否具有递归依赖关系(字段 X 具有类似 parent_id.X 的依赖关系);声明字段为递归必须是明确的,以确保重新计算的正确性

  • inverse (str) – 反转字段的方法名称(可选)

  • search (str) – 实现字段搜索的方法名称(可选)

  • related (str) – 字段名称的顺序

  • default_export_compatible (bool) – 是否默认在兼容导入的导出中导出字段 .. seealso:: 高级字段/相关字段

基本字段

class odoo.fields.Boolean[源代码]

封装了一个 bool

class odoo.fields.Char[源代码]

基本字符串字段,可以限制长度,在客户端通常显示为单行字符串。

参数
  • size (int) – 该字段存储的值的最大大小

  • trim (bool) – 指示值是否被修剪(默认为 True)。请注意,修剪操作仅在Web客户端中应用。

  • translate (bool or callable) – 启用字段值的翻译;使用 translate=True 以整体方式翻译字段值; translate 也可以是可调用对象,使得 translate(callback, value) 通过使用 callback(term) 来检索术语的翻译来翻译 value

class odoo.fields.Float[源代码]

封装了一个 float

精度数字由(可选的) digits 属性给出。

参数

digits (tuple(int,int) or str) – 一对(total,decimal)或引用一个 DecimalPrecision 记录的字符串 名称。

当浮点数与计量单位相关联时,使用正确的工具以正确的精度比较或舍入值非常重要。

Float类提供了一些静态方法来实现此目的:

round() to round a float with the given precision. is_zero() to check if a float equals zero at the given precision. compare() to compare two floats at the given precision.

示例

按照计量单位的精度四舍五入数量:

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 或模型表的列(默认值: True )。

class odoo.fields.Html[源代码]

封装 HTML 代码内容。

参数
  • sanitize (bool) – 值是否需要进行清理(默认: True

  • sanitize_overridable (bool) – 是否允许属于 base.group_sanitize_override 组的用户绕过清洁处理(默认值: False

  • sanitize_tags (bool) – 是否对标签进行清理(只接受属性的白名单,默认值: True

  • sanitize_attributes (bool) – 是否对属性进行清理(只接受属性的白名单,默认值: True

  • sanitize_style (bool) – 是否清理样式属性(默认: False

  • strip_style (bool) – 是否剥离样式属性(被移除,因此未经过清理,默认值: False

  • strip_classes (bool) – 是否剥离类属性(默认: False

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 中表示的 float

小数精度和货币符号取自 currency_field 属性。

参数

currency_field (str) – name of the Many2one field holding the res_currency this monetary field is expressed in (default: 'currency_id')

class odoo.fields.Selection[源代码]

封装了不同值之间的互斥选择。

参数
  • selection (list(tuple(str,str)) or callable or str) – 指定此字段的可能值。它可以是一个由 (value, label) 对组成的列表,也可以是一个模型方法或方法名。

  • selection_add (list(tuple(str,str))) – 提供了对覆盖字段的选择扩展。它是一个由 (value, label) 或单个值 (value,) 组成的列表,其中单个值必须出现在覆盖的选择中。新值按照与覆盖选择一致的顺序插入到此列表中:: selection = [(‘a’, ‘A’), (‘b’, ‘B’)] selection_add = [(‘c’, ‘C’), (‘b’,)] > result = [(‘a’, ‘A’), (‘c’, ‘C’), (‘b’, ‘B’)]

  • ondelete – 提供了一个回退机制,用于任何带有selection_add的覆盖字段。它是一个字典,将selection_add中的每个选项映射到一个回退操作。这个回退操作将应用于所有selection_add选项映射到它的记录。这些操作可以是以下任何一种: - ‘set null’ – 默认值,所有具有此选项的记录将其选择值设置为False。 - ‘cascade’ – 所有具有此选项的记录将与该选项本身一起被删除。 - ‘set default’ – 所有具有此选项的记录将被设置为字段定义的默认值 - ‘set VALUE’ – 所有具有此选项的记录将被设置为给定值 - <callable> – 一个可调用的函数,其第一个和唯一的参数将是包含指定Selection选项的记录集,用于自定义处理

属性 selection 是必需的,除非是 related 或扩展字段的情况。

class odoo.fields.Text[源代码]

非常类似于 Char,但用于更长的内容,没有大小,并且通常显示为多行文本框。

参数

translate (bool or callable) – 启用字段值的翻译;使用 translate=True 以整体方式翻译字段值; translate 也可以是可调用对象,使得 translate(callback, value) 通过使用 callback(term) 来检索术语的翻译来翻译 value

日期(时间)字段

DatesDatetimes 是任何类型的商业应用程序中非常重要的字段。它们的错误使用可能会导致看不见但痛苦的错误,本节旨在为Odoo开发人员提供避免错误使用这些字段所需的知识。

当给日期/时间字段赋值时,以下选项是有效的:

  • 一个 datedatetime 对象。

  • 一个符合服务器格式的字符串:

    • YYYY-MM-DD for Date fields,

    • YYYY-MM-DD HH:MM:SS for Datetime fields.

  • FalseNone.

Date和Datetime字段类有助手方法,尝试转换为兼容类型:

Example

解析来自外部来源的日期/日期时间::

fields.Date.to_date(self._context.get('date_from'))

日期/时间比较最佳实践:

  • 日期字段 只能 与日期对象进行比较。

  • Datetime字段 只能 与datetime对象进行比较。

警告

表示日期和日期时间的字符串可以相互比较,但结果可能不是预期的结果,因为日期时间字符串始终大于日期字符串,因此 强烈 不建议这种做法。

日期和日期时间的常见操作,如加法、减法或获取一个周期的开始/结束,都可以通过 DateDatetime 进行操作。这些辅助函数也可以通过导入 odoo.tools.date_utils 来使用。

注解

时区

日期时间字段在数据库中以 timestamp without timezone 列的形式存储,并以UTC时区存储。这是有意设计的,因为它使Odoo数据库独立于托管服务器系统的时区。时区转换完全由客户端管理。

class odoo.fields.Date[源代码]

封装了一个python :class:`date <datetime.date>`对象。

static add(value, *args, **kwargs)[源代码]

返回 valuerelativedelta 的和。

参数
  • value – 初始日期或日期时间。

  • args – 传递给 relativedelta 的位置参数。

  • kwargs – 传递给 relativedelta 的关键字参数。

返回

结果日期/日期时间。

static context_today(record, timestamp=None)[源代码]

返回当前日期,以客户端时区为准,格式适合日期字段。

注解

此方法可用于计算默认值。

参数
  • record – 获取时区的记录集。

  • timestamp (datetime) – 可选的日期时间值,用于代替当前日期和时间(必须是日期时间,普通日期无法在时区之间转换)。

返回类型

date

static end_of(value, granularity)[源代码]

从日期或日期时间获取时间段的结束时间。

参数
  • value – 初始日期或日期时间。

  • granularity – 时间段类型字符串,可以是年、季度、月、周、日或小时。

返回

指定时间段开始的日期/日期时间对象。

static start_of(value, granularity)[源代码]

从日期或日期时间获取时间段的开始。

参数
  • value – 初始日期或日期时间。

  • granularity – 时间段的类型,可以是年、季度、月、周、日或小时。

返回

一个对应于指定时间段开始的日期/日期时间对象。

static subtract(value, *args, **kwargs)[源代码]

返回 valuerelativedelta 之间的差异。

参数
  • value – 初始日期或日期时间。

  • args – 传递给 relativedelta 的位置参数。

  • kwargs – 传递给 relativedelta 的关键字参数。

返回

结果日期/日期时间。

static to_date(value)[源代码]

尝试将 value 转换为 date 对象。

警告

如果传入的值是一个日期时间对象,它将被转换为日期对象,所有日期时间特定的信息将会丢失(如时分秒,时区等)。

参数

value (str or date or datetime) – 需要转换的值。

返回

表示 value 的对象。

返回类型

date or None

static to_string(value)[源代码]

datedatetime 对象转换为字符串。

参数

value – 需要转换的值。

返回

一个字符串表示服务器日期格式中的 value,如果 valuedatetime 类型,则小时、分钟、秒和时区信息将被截断。

返回类型

str

static today(*args)[源代码]

返回ORM所期望的当前日期格式。

注解

此函数可用于计算默认值。

class odoo.fields.Datetime[源代码]

封装了一个python :class:`datetime <datetime.datetime>`对象。

static add(value, *args, **kwargs)[源代码]

返回 valuerelativedelta 的和。

参数
  • value – 初始日期或日期时间。

  • args – 传递给 relativedelta 的位置参数。

  • kwargs – 传递给 relativedelta 的关键字参数。

返回

结果日期/日期时间。

static context_timestamp(record, timestamp)[源代码]

返回给定的时间戳转换为客户端时区。

注解

此方法 适用于默认初始化程序,因为日期时间字段在客户端显示时会自动转换。对于默认值,应该使用 now() 代替。

参数
  • record – 获取时区的记录集。

  • timestamp (datetime) – 将表示为UTC的本地日期时间值转换为客户端时区。

返回

将时间戳转换为上下文时区的带时区感知的日期时间。

返回类型

datetime

static end_of(value, granularity)[源代码]

从日期或日期时间获取时间段的结束时间。

参数
  • value – 初始日期或日期时间。

  • granularity – 时间段类型字符串,可以是年、季度、月、周、日或小时。

返回

指定时间段开始的日期/日期时间对象。

static now(*args)[源代码]

以ORM所期望的格式返回当前日期和时间。

注解

此函数可用于计算默认值。

static start_of(value, granularity)[源代码]

从日期或日期时间获取时间段的开始。

参数
  • value – 初始日期或日期时间。

  • granularity – 时间段的类型,可以是年、季度、月、周、日或小时。

返回

一个对应于指定时间段开始的日期/日期时间对象。

static subtract(value, *args, **kwargs)[源代码]

返回 valuerelativedelta 之间的差异。

参数
  • value – 初始日期或日期时间。

  • args – 传递给 relativedelta 的位置参数。

  • kwargs – 传递给 relativedelta 的关键字参数。

返回

结果日期/日期时间。

static to_datetime(value)[源代码]

将 ORM value 转换为 datetime 值。

参数

value (str or date or datetime) – 需要转换的值。

返回

表示 value 的对象。

返回类型

datetime or None

static to_string(value)[源代码]

datetimedate 对象转换为字符串。

参数

value (datetime or date) – 需要转换的值。

返回

一个字符串,表示服务器的日期时间格式中的 value ,如果 valuedate 类型,则时间部分将为午夜(00:00:00)。

返回类型

str

static today(*args)[源代码]

返回当前日期,午夜零点(00:00:00)。

关系型字段

class odoo.fields.Many2one[源代码]

这样的字段的值是一个大小为0(没有记录)或1(单个记录)的记录集。

参数
  • comodel_name (str) – 目标模型的名称 必填 ,除了相关或扩展字段之外。

  • domain – 在客户端上设置在候选值上的可选域(域或字符串)

  • context (dict) – 在处理该字段时,在客户端使用的可选上下文

  • ondelete (str) – 当引用的记录被删除时的操作;可能的值有: 'set null''restrict''cascade'

  • auto_join (bool) – 是否在通过该字段进行搜索时生成JOIN(默认值: False

  • delegate (bool) – 将其设置为 True 以使目标模型的字段可从当前模型访问(对应于 _inherits

  • check_company (bool) – 在 _check_company() 中标记要进行验证的字段。根据字段属性添加默认的公司域。

class odoo.fields.One2many[源代码]

One2many字段;此类字段的值是 comodel_name 中所有记录的记录集,其中字段 inverse_name 等于当前记录。

参数
  • comodel_name (str) – name of the target model

  • inverse_name (str) – name of the inverse Many2one field in comodel_name

  • domain – 在客户端上设置在候选值上的可选域(域或字符串)

  • context (dict) – 在处理该字段时,在客户端使用的可选上下文

  • auto_join (bool) – 是否在通过该字段进行搜索时生成JOIN(默认值: False

除了相关字段或字段扩展的情况外, comodel_nameinverse_name 属性是必需的。

class odoo.fields.Many2many[源代码]

Many2many字段;此字段的值为记录集。

参数
  • comodel_name – 目标模型的名称(字符串),除了相关或扩展字段的情况外,必填

  • relation (str) – optional name of the table that stores the relation in the database

  • column1 (str) – 可选,指向表中“这些”记录的列的名称 relation

  • column2 (str) – 可选,用于引用表中“那些”记录的列的名称 relation

属性 relationcolumn1column2 是可选的。如果未提供,名称将从模型名称自动生成,前提是 model_namecomodel_name 不同!

请注意,在给定模型上具有多个具有隐式关系参数的字段,且具有相同的关联模型是不被ORM接受的,因为这些字段将使用相同的表。ORM防止两个many2many字段使用相同的关系参数,除非

  • 两个字段使用相同的模型、关联模型和关系参数是明确的;或者

  • 至少有一个字段属于一个具有 _auto = False 的模型。

参数
  • domain – 在客户端上设置在候选值上的可选域(域或字符串)

  • context (dict) – 在处理该字段时,在客户端使用的可选上下文

  • check_company (bool) – 在 _check_company() 中标记要进行验证的字段。根据字段属性添加默认的公司域。

class odoo.fields.Command[源代码]

One2manyMany2many 字段 期望一个特殊的命令来操作它们所实现的关系。

内部上,每个命令都是一个由3个元素组成的元组,其中第一个元素是一个必需的整数,用于标识命令,第二个元素要么是相关记录的id(用于update、delete、unlink和link命令),要么是0(用于create、clear和set命令),第三个元素要么是要写入记录的 values (用于create和update命令),要么是新的相关记录的 ids 列表(用于set命令),要么是0(用于delete、unlink、link和clear命令)。

通过Python,我们鼓励开发人员通过此命名空间的各种函数来创建新命令。我们还鼓励开发人员在比较现有命令的第一个元素时使用命令标识符常量名称。

通过RPC,无法使用函数或命令常量名称。必须改为编写字面量3元组,其中第一个元素是命令的整数标识符。

CREATE = 0
UPDATE = 1
DELETE = 2
CLEAR = 5
SET = 6
classmethod create(values: dict)[源代码]

使用 values 在 comodel 中创建新记录,将创建的记录链接到 self

Many2many 关系中,会在 comodel 中创建一个唯一的新记录,使得 self 中的所有记录都与新记录关联。

One2many 关系中,为 self 中的每个记录在 comodel 中创建一个新记录,以便 self 中的每个记录都与新记录中的一个记录关联。

返回命令三元组 (CREATE, 0, values)

classmethod update(id: int, values: dict)[源代码]

在相关记录上写入 values

返回命令三元组 (UPDATE, id, values)

classmethod delete(id: int)[源代码]

从数据库中删除相关记录并删除与 self 的关联。

Many2many 关系中,如果记录仍然与其他记录关联,从数据库中删除记录可能会被阻止。

返回命令三元组 (DELETE, id, 0)

移除 self 和相关记录之间的关联关系。

One2many 关系中,如果逆向字段设置为 ondelete='cascade',则给定的记录将从数据库中删除。否则,逆向字段的值将设置为 False,并保留该记录。

返回命令三元组 (UNLINK, id, 0)

self 和相关记录之间建立关联。

返回命令三元组 (LINK, id, 0)

classmethod clear()[源代码]

从与 self 的关系中删除所有记录。它的行为类似于对每条记录执行 unlink 命令。

返回命令三元组 (CLEAR, 0, 0)

classmethod set(ids: list)[源代码]

self 的当前关系替换为给定的关系。它的行为类似于在每个已删除的关系上执行 unlink 命令,然后在每个新关系上执行 link 命令。

返回命令三元组 (SET, 0, ids)

伪关系字段

class odoo.fields.Reference[源代码]

伪关联字段(数据库中没有外键)。

字段值以 string 的形式存储在数据库中,遵循模式 "res_model,res_id"

class odoo.fields.Many2oneReference[源代码]

伪关联字段(数据库中没有外键)。

字段值在数据库中以 integer id 的形式存储。

Reference 字段相反,模型必须在 Char 字段中指定,其名称必须在当前 Many2oneReference 字段的 model_field 属性中指定。

参数

model_field (str) – 模型名称存储在 Char 类中的名称。

计算字段

字段可以使用``compute``参数进行计算(而不是直接从数据库中读取)。它必须将计算后的值赋给字段。如果它使用其他*字段*的值,应该使用:func:`~odoo.api.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)]
    

    搜索方法在执行实际搜索之前,在处理域时被调用。它必须返回一个等价于条件 field operator value 的域。

  • 默认情况下,计算字段是只读的。要允许在计算字段上 设置 值,请使用 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
    

警告

虽然可以在多个字段中使用相同的计算方法,但不建议在反向方法中这样做。

在计算反向关系时, 所有 使用该反向关系的字段都受到保护,这意味着即使它们的值不在缓存中,也无法计算它们的值。

如果访问这些字段并且它们的值不在缓存中,ORM 将简单地为这些字段返回默认值 False 。这意味着除了触发反向方法的字段之外的反向字段的值可能不会给出它们的正确值,这可能会破坏反向方法的预期行为。

自动字段

Model.id

标识符 field

如果当前记录集的长度为1,则返回其中唯一记录的ID。

否则抛出错误。

访问日志字段

这些字段在启用 _log_access 时会自动设置和更新。可以禁用它以避免在不需要它们的表上创建或更新这些字段。

默认情况下, _log_access 的值与 _auto 相同

Model.create_date

记录创建时间, Datetime

Model.create_uid

存储创建记录的 whoMany2oneres.users

Model.write_date

记录上次更新的时间, Datetime

Model.write_uid

记录最后更新的人, Many2oneres.users

警告

_log_access 必须TransientModel 上启用。

保留字段名称

一些字段名称被保留用于预定义的行为,超出了自动化字段的范畴。当需要相关的行为时,应在模型上定义它们:

Model.name

默认值为 _rec_name,用于在需要代表性“命名”的上下文中显示记录。

Char

Model.active

切换记录的全局可见性,如果 active 设置为 False ,则该记录在大多数搜索和列表中都是不可见的。

Boolean

特殊方法:

toggle_active()[源代码]

self 中反转记录的 active 的值。

Model.action_archive()[源代码]

将记录集的 active 设置为 False,通过调用 其当前活动记录的 toggle_active() 方法。

Model.action_unarchive()[源代码]

将记录集的 active 设置为 True,通过调用 其当前非活动记录的 toggle_active() 方法。

Model.state

对象的生命周期阶段,由 states 属性在 fields 上使用。

Selection

Model.parent_id

default_value of _parent_name, 用于组织树形结构中的记录,并在域中启用 child_ofparent_of 运算符。

Many2one

Model.parent_path

_parent_store 设置为 True 时,用于存储反映 _parent_name 的树结构的值,并优化搜索域中的 child_ofparent_of 运算符。必须使用 index=True 声明以确保正常运行。

Char

Model.company_id

Odoo 多公司行为所使用的主字段名称。

用于 :meth:~odoo.models._check_company 检查多公司一致性。定义记录是否在公司之间共享(没有值)或仅可由给定公司的用户访问。

Many2one :type: res_company

记录集

与模型和记录的交互是通过记录集进行的,记录集是相同模型的记录的有序集合。

警告

与其名称所暗示的相反,目前记录集中可能包含重复项。这在未来可能会改变。

在模型上定义的方法在记录集上执行,它们的 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'))

字段值也可以像字典项一样访问,这比使用动态字段名的 getattr() 更加优雅和安全。设置字段的值会触发对数据库的更新:

>>> record.name
Example Name
>>> record.company_id.name
Company Name
>>> record.name = "Bob"
>>> field = "name"
>>> record[field]
Bob

警告

尝试读取多个记录上的字段将会对非关系型字段引发错误。

访问关系字段(Many2oneOne2manyMany2many)*始终*返回一个记录集,如果字段未设置,则为空。

记录缓存和预取

Odoo 维护记录字段的缓存,以便不是每个字段访问都会发出数据库请求,这对性能来说是可怕的。以下示例仅查询第一个语句的数据库:

record.name             # first access reads value from database
record.name             # second access gets value from cache

为了避免逐个读取每个记录的每个字段,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)[源代码]

装饰一个方法,使其被每日的清理定时任务(模型 ir.autovacuum)调用。这通常用于类似垃圾回收的任务,不需要特定的定时任务。

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 只有在装饰方法中声明的字段包含在 createwrite 调用中时才会触发。这意味着在视图中不存在的字段在记录创建期间不会触发调用。必须重写 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 (value in context or current company id),

  • uid (current user id and superuser flag),

  • active_test (value in env.context or value in field.context).

odoo.api.model(method)[源代码]

装饰一个记录式方法,其中 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'},
}

如果类型设置为通知,则警告将显示在通知中。否则,默认情况下将在对话框中显示。

警告

@onchange 仅支持简单字段名称,不支持点分字段名称(例如关联字段的字段 partner_id.tz),将被忽略

危险

由于 @onchange 返回伪记录的记录集,对上述记录集调用任何一个CRUD方法 (create(), read(), write(), unlink()) 都是未定义的行为,因为它们在数据库中可能尚不存在。

相反,只需像上面的示例中所示设置记录的字段或调用 update() 方法。

警告

不可能通过 onchange 来修改 one2manymany2many 字段本身。这是一个 web 客户端的限制 - 请参见 #2693

odoo.api.ondelete(*, at_uninstall)[源代码]

标记一个方法,在 unlink() 执行期间被调用。

这个装饰器的目的是允许在取消关联记录时出现客户端错误,如果从业务角度来看,删除这些记录是没有意义的。例如,用户不应该能够删除已验证的销售订单。

虽然可以通过简单地覆盖模型上的 unlink 方法来实现,但它的缺点是与模块卸载不兼容。在卸载模块时,覆盖可能会引发用户错误,但我们不应该关心,因为模块正在被卸载,因此与模块相关的 所有 记录都应该被删除。

这意味着通过覆盖 unlink ,有很大的机会导致一些表/记录作为未安装模块的剩余数据保留下来。这将使数据库处于不一致的状态。此外,如果该模块在该数据库上重新安装,存在冲突的风险。

使用 @ondelete 修饰的方法应该在某些条件下引发错误,并且按照约定,该方法的名称应该是 _unlink_if_<condition>_unlink_except_<not_condition>

@api.ondelete(at_uninstall=False)
def _unlink_if_user_inactive(self):
    if any(user.active for user in self):
        raise UserError("Can't delete an active user!")

# same as above but with _unlink_except_* as method name
@api.ondelete(at_uninstall=False)
def _unlink_except_active_user(self):
    if any(user.active for user in self):
        raise UserError("Can't delete an active user!")
参数

at_uninstall (bool) – 装饰的方法是否在实现该方法的模块被卸载时调用。通常应为 False ,以避免模块卸载时触发这些错误。

危险

如果您实现的检查也适用于卸载模块,请将参数 at_uninstall 仅设置为 True

例如,卸载 sale 时,删除已验证的销售订单并不重要,因为与 sale 相关的所有数据都应该被删除,在这种情况下, at_uninstall 应该设置为 False

然而,如果没有安装其他语言,防止删除默认语言是有意义的,因为删除默认语言将破坏很多基本行为。在这种情况下, at_uninstall 应该设置为 True

odoo.api.returns(model, downgrade=None, upgrade=None)[源代码]

返回一个装饰器,用于返回 model 实例的方法。

参数
  • model – 一个模型名称,或者 'self' 表示当前模型

  • downgrade – 一个函数 downgrade(self, value, *args, **kwargs) 用于将 记录样式的 value 转换为传统样式的输出

  • upgrade – 一个函数 upgrade(self, value, *args, **kwargs) 用于将传统风格的 value 转换为记录风格的输出

参数 self*args**kwargs 是以记录样式传递给方法的参数。

该装饰器将方法的输出适应于API风格: idids 或传统风格的 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)

请注意,被装饰的方法必须符合这个约定。

这些装饰器会自动 继承 :覆盖已装饰的现有方法的方法将使用相同的 @returns(model) 装饰。

环境

class odoo.api.Environment(cr, uid, context, su=False)[源代码]

环境存储ORM使用的各种上下文数据:

  • cr: 当前数据库游标(用于数据库查询);

  • uid: 当前用户ID(用于访问权限检查);

  • context: 当前上下文字典(任意元数据);

  • su: 是否处于超级用户模式。

它通过实现从模型名称到模型的映射来提供对注册表的访问。它还持有记录的缓存和管理重新计算的数据结构。

>>> records.env
<Environment object ...>
>>> records.env.uid
3
>>> records.env.user
res.user(3)
>>> records.env.cr
<Cursor object ...>

在从另一个记录集创建记录集时,环境会被继承。环境可用于在另一个模型中获取空记录集,并查询该模型:

>>> 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.lang

返回当前语言代码。

返回类型

str

Environment.user

返回当前用户(作为实例)。

返回

当前用户 - 已使用sudo命令

返回类型

res.users 记录

Environment.company

返回当前公司(作为实例)。

如果在上下文中未指定(allowed_company_ids),则回退到当前用户的主要公司。

引发

AccessError – 无效或未授权的 allowed_company_ids 上下文键内容。

返回

当前公司(默认=`self.user.company_id`),与当前环境一起

返回类型

res.company record

警告

在sudo模式下不适用任何健全性检查!在sudo模式下,用户可以访问任何公司,即使不在他允许的公司列表中。

这允许触发跨公司修改,即使当前用户没有访问目标公司的权限。

Environment.companies

返回用户启用的公司的记录集。

如果在上下文中未指定(allowed_company_ids),则回退到当前用户的公司。

引发

AccessError – 无效或未授权的 allowed_company_ids 上下文键内容。

返回

当前公司(默认=`self.user.company_ids`),与当前环境

返回类型

res.company recordset

警告

在sudo模式下不适用任何健全性检查!在sudo模式下,用户可以访问任何公司,即使不在他允许的公司列表中。

这允许触发跨公司修改,即使当前用户没有访问目标公司的权限。

有用的环境方法

Environment.ref(xml_id, raise_if_not_found=True)[源代码]

Return the record corresponding to the given xml_id.

参数
  • xml_id (str) – 记录的 xml_id,格式为 <module.id>

  • raise_if_not_found (bool) – whether the method should raise if record is not found

返回

Found record or None

引发

ValueError – 如果记录未找到且 raise_if_not_found 为 True

Environment.is_superuser()[源代码]

返回环境是否处于超级用户模式。

Environment.is_admin()[源代码]

返回当前用户是否拥有“访问权限”组或处于超级用户模式。

Environment.is_system()[源代码]

返回当前用户是否拥有“设置”组或处于超级用户模式。

修改环境

Model.with_context([context][, **overrides]) Model[源代码]

返回一个新版本的记录集,附加到扩展上下文。

扩展上下文是指提供的 context 中合并 overrides 或者 当前 上下文中合并 overrides ,例如:

# 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
参数

company (res_company or int) – 新环境的主公司。

警告

当当前用户使用未授权的公司时,在非sudo环境下访问环境中的公司可能会触发AccessError。

Model.with_env(env)[源代码]

返回一个附加到提供环境的新版本记录集。

参数

env (Environment) –

注解

返回的记录集与 self 具有相同的预取对象。

Model.sudo([flag=True])[源代码]

返回一个启用或禁用超级用户模式的新版本记录集,取决于 flag。超级用户模式不会更改当前用户,只是绕过访问权限检查。

警告

使用 sudo 可能会导致数据访问跨越记录规则的边界,可能会混合本应该被隔离的记录(例如在多公司环境中来自不同公司的记录)。

这可能会导致在选择一个记录时出现非直观的结果,例如获取默认公司或选择一个物料清单。

注解

返回的记录集与 self 具有相同的预取对象。

SQL 执行

在环境中, cr 属性是当前数据库事务的游标,允许直接执行 SQL,无论是为了难以使用 ORM 表达的查询(例如复杂的连接)还是出于性能原因:

self.env.cr.execute("some_sql", params)

警告

执行原始 SQL 会绕过 ORM,从而绕过 Odoo 的安全规则。请确保在使用用户输入时对查询进行过滤,并且如果您不真正需要使用 SQL 查询,则优先使用 ORM 工具。

关于模型,需要知道的一件重要的事情是它们不一定立即执行数据库更新。为了性能原因,框架会延迟在修改记录后重新计算字段的操作。而且一些数据库更新也会被延迟。因此,在查询数据库之前,必须确保它包含了查询所需的相关数据。这个操作被称为 flushing ,它执行了预期的数据库更新。

Example

# make sure that 'partner_id' is up-to-date in database
self.env['model'].flush_model(['partner_id'])

self.env.cr.execute("SELECT id FROM model WHERE partner_id IN %s", [ids])
ids = [row[0] for row in self.env.cr.fetchall()]

在执行每个 SQL 查询之前,必须刷新该查询所需的数据。刷新有三个级别,每个级别都有自己的 API。可以刷新所有内容、模型的所有记录或某些特定记录。由于延迟更新通常可以提高性能,因此建议在刷新时要 具体

Environment.flush_all()[源代码]

刷新所有待处理的计算和更新到数据库。

Model.flush_model(fnames=None)[源代码]

处理 self 模型上的待处理计算和数据库更新。 当给定参数时,该方法保证至少将给定字段刷新到数据库。 虽然可以刷新更多字段。

参数

fnames – optional iterable of field names to flush

Model.flush_recordset(fnames=None)[源代码]

处理记录 self 上的待处理计算和数据库更新。当给定参数时,该方法保证至少将记录 self 上的给定字段刷新到数据库。但是,可以刷新更多的字段和记录。

参数

fnames – optional iterable of field names to flush

因为模型使用相同的游标和 Environment 保存了各种缓存,所以在使用原始 SQL 修改数据库时,必须使这些缓存失效,否则模型的进一步使用可能会变得不一致。在使用 SQL 的 CREATEUPDATEDELETE 时,需要清除缓存,但不需要在使用 SELECT 时(它只是读取数据库)。

Example

# make sure 'state' is up-to-date in database
self.env['model'].flush_model(['state'])

self.env.cr.execute("UPDATE model SET state=%s WHERE state=%s", ['new', 'old'])

# invalidate 'state' from the cache
self.env['model'].invalidate_model(['state'])

与刷新类似,可以使整个缓存失效,使模型的所有记录的缓存失效,或使特定记录的缓存失效。甚至可以使某些记录的特定字段或模型的所有记录的特定字段失效。由于缓存通常可以提高性能,我们建议在使缓存失效时要 具体

Environment.invalidate_all(flush=True)[源代码]

使所有记录的缓存失效。

参数

flush – 是否在失效之前刷新挂起的更新。默认为 True ,可确保缓存一致性。除非您知道自己在做什么,否则不要使用此参数。

Model.invalidate_model(fnames=None, flush=True)[源代码]

使 self 的模型的所有记录的缓存失效,当缓存的值不再与数据库的值相对应时。如果给定了参数,则只有给定的字段会从缓存中失效。

参数
  • fnames – 要使无效的字段名称的可选可迭代对象

  • flush – 是否在失效之前刷新挂起的更新。默认为 True ,可确保缓存一致性。除非您知道自己在做什么,否则不要使用此参数。

Model.invalidate_recordset(fnames=None, flush=True)[源代码]

使 self 中的记录缓存失效,当缓存的值不再与数据库的值相对应时。如果给定了参数,则只有 self 上的给定字段会从缓存中失效。

参数
  • fnames – 要使无效的字段名称的可选可迭代对象

  • flush – 是否在失效之前刷新挂起的更新。默认为 True ,可确保缓存一致性。除非您知道自己在做什么,否则不要使用此参数。

上述方法可以使缓存和数据库保持一致。但是,如果计算字段的依赖关系在数据库中被修改,就必须通知模型重新计算计算字段。框架需要知道的唯一信息是 哪些 记录上的 哪些 字段已经发生了变化。

Example

# make sure 'state' is up-to-date in database
self.env['model'].flush_model(['state'])

# use the RETURNING clause to retrieve which rows have changed
self.env.cr.execute("UPDATE model SET state=%s WHERE state=%s RETURNING id", ['new', 'old'])
ids = [row[0] for row in self.env.cr.fetchall()]

# invalidate the cache, and notify the update to the framework
records = self.env['model'].browse(ids)
records.invalidate_recordset(['state'])
records.modified(['state'])

需要找出哪些记录已被修改。有许多方法可以做到这一点,可能涉及额外的 SQL 查询。在上面的示例中,我们利用了 PostgreSQL 的 RETURNING 子句,在不进行额外查询的情况下检索信息。在通过失效使缓存一致之后,使用已更新的字段调用已修改记录上的 modified 方法。

Model.modified(fnames, create=False, before=False)[源代码]

通知在 self 上的字段将被修改或已经被修改。这将在必要时使缓存无效,并准备重新计算依赖的存储字段。

参数
  • fnames – 在记录 self 上修改的字段名称的可迭代对象

  • create – 是否在记录创建的上下文中调用

  • before – 是否在修改记录 self 之前调用

常见 ORM 方法

创建/更新

Model.create(vals_list) records[源代码]

为模型创建新记录。

新记录使用字典列表 vals_list 中的值进行初始化,如果需要还可以使用 default_get() 中的值。

参数

vals_list (Union[list[dict], dict]) – 模型字段的值,作为字典列表:: [{‘field_name’: field_value, …}, …] 为了向后兼容, vals_list 可以是一个字典。它被视为一个单例列表 [vals] ,并返回一个单个记录。 详见 write()

返回

已创建的记录

引发
  • AccessError – 如果当前用户没有权限创建指定模型的记录

  • ValidationError – 如果用户尝试为选择字段输入无效值

  • ValueError – 如果在创建数值中指定的字段名称不存在。

  • UserError – 如果在对象层次结构中创建了循环,操作的结果(例如将对象设置为其自身的父级)

Model.copy(default=None)[源代码]

复制记录 self 并使用默认值更新它

参数

default (dict) – 在复制记录的原始值中覆盖字段值的字典,例如: {'field_name': overridden_value, ...}

返回

新记录

Model.default_get(fields_list) default_values[源代码]

返回 fields_list 中字段的默认值。默认值由上下文、用户默认值和模型本身决定。

参数

fields_list (list) – names of field whose default is requested

返回

如果字段有默认值,则将字段名称映射到相应的默认值的字典。

返回类型

dict

注解

未请求的默认值不会被考虑,在 fields_list 中没有字段名的字段不需要返回值。

Model.name_create(name) record[源代码]

通过调用 create() 方法并提供一个值(即新记录的显示名称)来创建新记录。

新记录将使用适用于此模型的任何默认值进行初始化,或通过上下文提供。适用于 create() 的通常行为。

参数

name – 创建记录的显示名称

返回类型

tuple

返回

创建记录的 name_get() 对值

Model.write(vals)[源代码]

使用提供的值更新 self 中的所有记录。

参数

vals (dict) – 需要更新的字段及其对应的值

引发
  • AccessError – 如果用户没有权限修改指定的记录/字段

  • ValidationError – 如果选择字段指定了无效值

  • UserError – 如果在对象层次结构中创建了循环,操作的结果(例如将对象设置为其自身的父级)

  • 对于数字字段(IntegerFloat),值应该是相应类型的

  • 对于 Boolean,值应该是一个 bool

  • 对于 Selection,值应该与选择的值匹配 (通常是 str,有时是 int)

  • 对于 Many2one,值应该是要设置的记录的数据库标识符

  • 预期的值是一个 One2manyMany2many 关系字段的列表,它操作实现的关系。总共有 7 个命令:create()update()delete()unlink()link()clear()set()

  • 对于 Date~odoo.fields.Datetime,值应该是一个日期(时间)或字符串。

    警告

    如果为日期(时间)字段提供了字符串,则必须仅使用UTC,并按照 odoo.tools.misc.DEFAULT_SERVER_DATE_FORMATodoo.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT 进行格式化

  • 其他非关系型字段使用字符串作为值

搜索/读取

Model.browse([ids]) records[源代码]

在当前环境中,返回提供的参数所对应的记录集。

self.browse([7, 18, 12])
res.partner(7, 18, 12)
参数

ids (int or iterable(int) or None) – id(s)

返回

记录集

Model.search(domain[, offset=0][, limit=None][, order=None][, count=False])[源代码]

基于 domain search domain 进行记录搜索。

参数
  • domainA search domain. Use an empty list to match all records.

  • offset (int) – 忽略的结果数量(默认值:无)

  • limit (int) – 返回的最大记录数(默认:全部)

  • order (str) – 排序字符串

  • count (bool) – 如果为True,则仅计算并返回匹配记录的数量(默认值:False)

返回

最多返回符合搜索条件的 limit 条记录

引发

AccessError – 如果用户无权访问请求的信息

Model.search_count(domain) int[源代码]

返回当前模型中与 提供的领域 匹配的记录数。

参数
  • domainA search domain. Use an empty list to match all records.

  • limit – 最大记录数(上限)(默认:全部)

搜索与给定的 name 模式匹配的显示名称的记录,与给定的 operator 进行比较,同时也匹配可选的搜索域(args)。

这用于例如基于关系字段的部分值提供建议。通常应该与 name_get() 的反向行为相同,但这不是保证。

此方法等同于使用基于 display_name 的搜索域调用 search() 方法,然后在搜索结果上调用 name_get() 方法。

参数
  • name (str) – 要匹配的名称模式

  • args (list) – 可选的搜索域(参见 search() 语法),指定进一步的限制

  • operator (str) – 用于匹配 name 的域操作符,例如 'like''='

  • limit (int) – 可选的最大返回记录数

返回类型

list

返回

返回所有匹配记录的 (id, text_repr) 对列表。

Model.read([fields])[源代码]

读取 self 中记录的请求字段,低级/RPC 方法。

参数
  • fields (list) – 返回的字段名称(默认为所有字段)

  • load (str) – 加载模式,目前唯一的选项是将其设置为 None 以避免加载 m2o 字段的 name_get

返回

一个字典列表,将字段名映射到它们的值,每个记录一个字典

返回类型

list

引发
Model.read_group(domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True)[源代码]

获取按给定的 groupby 字段分组的列表视图中的记录列表。

参数
  • domain (list) – A search domain. Use an empty list to match all records.

  • fields (list) – 在对象上指定的列表视图中存在的字段列表。每个元素可以是 ‘field’(字段名称,使用默认聚合),或者 ‘field:agg’(使用聚合函数 ‘agg’ 的聚合字段),或者 ‘name:agg(field)’(使用 ‘agg’ 聚合字段并将其作为 ‘name’ 返回)。可能的聚合函数是由 PostgreSQL 提供的函数和 ‘count_distinct’,具有预期的含义。

  • groupby (list) – 记录将按照分组描述进行分组。分组描述可以是一个字段(那么它将按照该字段进行分组),也可以是一个字符串“field:granularity”。目前,唯一支持的粒度是“day”、“week”、“month”、“quarter”或“year”,并且它们只对日期/时间字段有意义。

  • offset (int) – 可选的跳过记录数

  • limit (int) – 可选的最大返回记录数

  • orderby (str) – 可选的 order by 规范,用于覆盖组的自然排序顺序,另请参阅 search() (目前仅支持 many2one 字段)

  • lazy (bool) – 如果为真,则结果仅按第一个groupby分组,其余的groupby放在__context键中。如果为假,则所有groupby在一次调用中完成。

返回

包含字典列表(每个记录一个字典),其中包含: * 按 groupby 参数分组的字段值 * __domain:指定搜索条件的元组列表 * __context:带有 groupby 参数的字典 * __range:(仅限日期/时间)字典,以field_name:granularity为键,映射到具有键的字典:”from”(包含)和”to”(不包含),映射到组的时间边界的字符串表示

返回类型

[{‘field_name_1’: value, …}, …]

引发

AccessError – 如果用户无权访问请求的信息

字段

Model.fields_get([allfields][, attributes])[源代码]

返回每个字段的定义。

返回的值是一个字典(以字段名为索引)的字典。包括_inherits的字段。字符串、帮助和选择(如果存在)属性已翻译。

参数
  • allfields (list) – 如果为空或未提供,则为所有字段记录文档

  • attributes (list) – 每个字段返回的属性,如果为空或未提供,则返回所有属性

返回

将字段名称映射到将属性映射到值的字典的字典。

返回类型

dict

搜索域

域是一个条件列表,每个条件都是一个三元组(可以是 listtuple ),其中: (field_name, operator, value)

  • field_name (str)

    当前模型的字段名称,或通过点符号导航到 Many2one 的关系遍历,例如 'street''partner_id.country'

  • operator (str)

    用于将 field_namevalue 进行比较的运算符。有效的运算符有:

    =

    等于

    !=

    不等于

    >

    大于

    >=

    大于或等于

    <

    小于

    <=

    小于或等于

    =?

    未设置或等于(如果 valueNoneFalse ,则返回true,否则的话就像 = 一样)

    =like

    使用 value 模式匹配 field_name 。模式中的下划线 _ 代表(匹配)任意单个字符;百分号 % 匹配零个或多个字符的任意字符串。

    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 记录的子级(后代)(value 可以是一个项目或项目列表)。

    考虑模型的语义(即遵循由 _parent_name 指定的关系字段)。

    parent_of

    value 记录的父级(祖先)(value 可以是一个项目或项目列表)。

    考虑模型的语义(即遵循由 _parent_name 指定的关系字段)。

  • value

    变量类型,必须与命名字段通过 operator 进行比较。

域准则可以使用 前缀 形式的逻辑运算符进行组合:

'&'

逻辑 AND,默认操作是将相继的条件组合在一起。Arity 2(使用下2个条件或组合)。

'|'

逻辑 OR,arity 2.

'!'

逻辑 NOT,arity 1.

注解

主要用于否定条件的组合。个别条件通常有一个否定形式(例如 = -> !=< -> >= ),这比否定肯定形式更简单。

Example

要搜索名为 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)

记录(集合)信息

Model.ids

返回与 self 对应的实际记录 id 列表。

odoo.models.env

返回给定记录集的环境。

类型

Environment

Model.exists() records[源代码]

返回 self 中存在的记录子集。它可以用作记录的测试::

if record.exists():
    ...

按照惯例,新记录被视为已存在。

Model.ensure_one()[源代码]

验证当前记录集是否只包含一条记录。

引发

odoo.exceptions.ValueErrorlen(self) != 1

Model.name_get()[源代码]

返回 self 中记录的文本表示,每个输入记录输出一个项目,顺序相同。

警告

虽然 name_get() 可以使用上下文数据进行更丰富的上下文格式化,但由于它是 display_name 的默认实现,如果上下文键为空/缺失,重置为“默认”行为非常重要。

返回

每个记录的 (id, text_repr) 列表对

返回类型

list[(int, str)]

Model.get_metadata()[源代码]

返回有关给定记录的一些元数据。

返回

每个请求记录的所有权字典列表

返回类型

包含以下键的字典列表: * id:对象ID * create_uid:创建记录的用户 * create_date:记录创建日期 * write_uid:最后更改记录的用户 * write_date:记录的最后更改日期 * xmlid:用于引用此记录的XML ID(如果有),格式为 module.name * xmlids:包含xmlid的字典列表,格式为 module.name ,并且noupdate为布尔值 * noupdate:一个布尔值,指示记录是否将被更新

操作

记录集是不可变的,但是可以使用各种集合操作来组合同一模型的集合,返回新的记录集。

  • record in set 返回 record``(必须是一个包含一个元素的记录集)是否存在于 ``set 中。record not in set 是其相反操作

  • set1 <= set2set1 < set2 返回 set1 是否是 set2 的子集 (分别是子集和严格子集)

  • set1 >= set2set1 > set2 返回 set1 是否是 set2 的超集 (分别是严格超集)

  • set1 | set2 returns the union of the two recordsets, a new recordset containing all records present in either source

  • set1 & set2 returns the intersection of two recordsets, a new recordset containing only records present in both sources

  • set1 - set2 返回一个新的记录集,其中只包含 set1 中的记录,这些记录在 set2 中*不存在*。

Recordsets 是可迭代的,因此通常的 Python 工具可用于转换 (map(), sorted(), ifilter(), …) 但是这些返回的是 list 或者 iterator,这样就无法对其结果调用方法或使用集合操作。

因此,记录集提供以下操作,返回记录集本身(如果可能):

筛选

Model.filtered(func)[源代码]

返回满足 funcself 中的记录。

参数

func (callable or str) – 一个函数或由字段名用点号分隔的序列

返回

符合条件的记录集,可能为空。

# 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)[源代码]

返回满足筛选条件并保持原有顺序的 self 记录。

参数

domainA search domain.

地图

Model.mapped(func)[源代码]

func 应用于 self 中的所有记录,并将结果作为列表或记录集返回(如果 func 返回记录集)。在后一种情况下,返回的记录集的顺序是任意的。

参数

func (callable or str) – 一个函数或由字段名用点号分隔的序列

返回

如果 func 为假,则返回 self;否则返回应用于所有 self 记录的 func 的结果。

返回类型

list or recordset

# 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版本开始,支持多关系字段访问,并且与映射调用类似:

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')

排序

Model.sorted(key=None, reverse=False)[源代码]

返回按 key 排序的记录集 self

参数
  • key (callable or str or None) – 要么是一个接受一个参数并为每个记录返回比较键的函数,要么是一个字段名,要么是 None ,在这种情况下,记录按照默认模型的顺序排序

  • reverse (bool) – 如果 True,以相反的顺序返回结果

# sort records by name
records.sorted(key=lambda r: r.name)

继承和扩展

Odoo 提供了三种不同的机制以模块化的方式扩展模型:

  • 从现有模型创建一个新模型,向副本添加新信息,但保留原始模块不变

  • 在原地扩展在其他模块中定义的模型,替换先前的版本

  • 将模型的一些字段委托给其包含的记录

../../../_images/inheritance_methods.png

经典继承

当同时使用 _inherit_name 属性时,Odoo 会基于已有的模型 (通过 _inherit 提供)创建一个新的模型。新模型将继承 基础模型的所有字段、方法和元信息(默认值和其他)。

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()

将产生:

“这是模型0记录A” “这是模型1记录B”

第二个模型继承了第一个模型的 check 方法和 name 字段,但是重写了 call 方法,就像使用标准的 Python 继承 一样。

扩展

当使用 _inherit 但是省略了 _name 时,新模型将替换现有模型,本质上是在原地扩展它。这对于向现有模型(在其他模块中创建的)添加新字段或方法非常有用,或者用于自定义或重新配置它们(例如更改它们的默认排序顺序):

class Extension0(models.Model):
_name = 'extension.0'
_description = 'Extension zero'

name = fields.Char(default="A")

class Extension1(models.Model):
_inherit = 'extension.0'

description = fields.Char(default="Extended")
record = env['extension.0'].create({})
record.read()[0]

将产生:

{'name': "A", 'description': "Extended"}

注解

它还将产生各种 自动字段,除非它们已被禁用

委托

第三种继承机制提供了更大的灵活性(可以在运行时进行修改),但功能较弱:使用 _inherits 属性,一个模型将 委托 查找当前模型上未找到的任何字段 给”子”模型。委托是通过在父模型上自动设置的 Reference 字段来执行的。

主要区别在于含义。使用委托时,模型 有一个 而不是 是一个 ,将关系转变为组合而不是继承:

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 is more or less implemented, avoid it if you can;

  • 链式 _inherits 实际上未被实现,我们无法保证最终行为。

字段增量定义

字段是在模型类上定义的类属性。如果模型被扩展,也可以通过在子类上重新定义具有相同名称和相同类型的字段来扩展字段定义。在这种情况下,字段的属性来自父类,并被子类中给定的属性覆盖。

例如,下面的第二个类只在字段 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")

错误管理

Odoo 异常模块定义了几个核心异常类型。

这些类型被RPC层理解。任何其他异常类型在传递到RPC层之前都将被视为“服务器错误”。

注解

如果您考虑引入新的异常,请查看 odoo.addons.test_exceptions 模块。

exception odoo.exceptions.AccessDenied(message='Access Denied')[源代码]

登录/密码错误。

注解

没有追踪信息。

示例

当您尝试使用错误的密码登录时。

exception odoo.exceptions.AccessError(message)[源代码]

访问权限错误。

示例

当您尝试读取您无权访问的记录时。

exception odoo.exceptions.CacheMiss(record, field)[源代码]

缓存中缺少值。

示例

当您尝试在已刷新的缓存中读取值时。

exception odoo.exceptions.MissingError(message)[源代码]

缺少记录。

示例

当您尝试在已删除的记录上进行写操作时。

exception odoo.exceptions.RedirectWarning(message, action, button_text, additional_context=None)[源代码]

警告,可以重定向用户而不仅仅是显示警告消息。

参数
  • message (str) – 异常消息和前端模态框内容

  • action_id (int) – 重定向操作所在的动作的ID

  • button_text (str) – 在触发重定向的按钮上放置的文本。

  • additional_context (dict) – 传递给 action_id 的参数。例如,可以用于将视图限制为 active_ids。

exception odoo.exceptions.UserError(message)[源代码]

客户端处理的通用错误。

通常情况下,当用户尝试在记录的当前状态下执行没有意义的操作时会出现此错误。在语义上类似于通用的400 HTTP状态码。

exception odoo.exceptions.ValidationError(message)[源代码]

违反了Python约束条件。

示例

当您尝试使用已经存在于数据库中的登录名创建新用户时。