Erlo

odoo ORM API学习总结兼orm学习教程

2023-03-11 11:00:18 发布   206 浏览  
页面报错/反馈
收藏 点赞

环境

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:

  • Model 用于常规数据库持久化模型
  • TransientModel 用于临时数据,存储在数据库中,但每隔一段时间就会自动清空
  • AbstractModel 用于多继承模块共享的抽象父类,不会在数据库中创建模型表

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

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

要创建不需要实例化的类,可以将 _register 属性设置为False

  • _auto= False

    是否应该创建数据库表。如果设置为 False, 应该重写 init()来创建数据库表。默认设。针对ModelTransientModel自动设置为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

    执行writecreate, 对拥有 check_company=True属性的关联字段调用_check_company 以确保公司一致性

  • _parent_name= 'parent_id'

    用作父字段的many2one字段

  • _parent_store= False

    设置为True以计算parent_path字段。与parent_path 字段一起,设置记录树结构的索引存储,以便使用child_ofparent_of域运算符对当前模型的记录进行更快的分层查询

  • _date_name= 'date'

    用于默认日历视图的字段

  • _fold_name= 'fold'

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

AbstractModel

odoo.models.AbstractModel[源代码]

odoo.models.BaseModel的别名

Model

class odoo.models.Model[源代码]

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

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

class user(Model):
    ...

系统将为安装了该类模块的每个数据库实例化一次类

  • _auto= True

    是否应该创建数据库表。如果设置为 False, 应该重写 init()来创建数据库表。默认设。针对ModelTransientModel自动设置为False,针对AbstractModel自动设置为False。可通过继承AbstractModel来创建不需要任何数据表的模型

  • _abstract= False

    是否为抽象模型

  • _transient= False

    是否为transient模型

TransientModel

class odoo.models.TransientModel[源代码]

用于临时记录的父类模型,旨在暂时保持,并定期进行清理

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

  • _auto= True

    是否应该创建数据库表。如果设置为 False, 应该重写 init()来创建数据库表。默认设。针对ModelTransientModel自动设置为False,针对AbstractModel自动设置为False。可通过继承AbstractModel来创建不需要任何数据表的模型

  • _abstract= False

    是否为抽象模型

  • _transient= False

    是否为transient模型

字段(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_resolutionFalse,则不会验证字段内容,此时应该使用Binary字段。

class odoo.fields.Monetary[源代码]

封装以给定res_currency表示的浮点值。

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

参数:

  • currency_field (str) –拥有表示该货币字段的res_currencyMany2one字段名称(默认: '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-具有此选项的所有记录都将设置为字段定义的默认值
    • -一个可调用对象,其第一个也是唯一的参数将是包含指定的Selection选项的记录集,用于自定义处理

selection属性选择是强制性的,除非是related或扩展的字段

class odoo.fields.Text[源代码]

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

参数:

translate (bool 或者可调用对象) – 同 Char

Date(time) 字段

当将一个值赋值给 Date/Datetime 字段时,以下选择是合法的:

  • datedatetime 对象.
  • 正确格式的字符:
    • Date字段采用YYYY-MM-DD
    • Datetime字段采用 YYYY-MM-DD HH:MM:SS
  • False 或者 None.

DateDatetime 字段类拥有以下辅助函数,用于尝试转换为兼容类型:

示例

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

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)

    返回 valuerelativedelta之和

    • 参数

      value – 初始datedatetime

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

    返回 valuerelativedelta之差

    • 参数

      value – 初始date 或者datetime

      args – 传递给 relativedelta位置参数

      kwargs – 传递给 relativedelta的关键词参数

    • 返回

      date/datetime结果对象

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

    按客户端时区以适合date字段的格式返回当前日期

    注解

    该方法可能用于计算默认值

    • 参数

      record – 从中获取时区的记录集

      timestamp (datetime) – 替代当前日期时间(datetime)的可选的datetime对象

    • 返回类型

      date

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

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

    • 参数

      value – 初始datedatetime

      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 – 初始datedatetime

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

    尝试转换 valuedate 对象

    警告

    如果value为datetime对象,它将被转换为date对象,且所有日期时间特定信息(HMS, TZ, …)都会丢失。

    • 参数

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

    • 返回

      代表 value的对象

    • 返回类型

      date类型或者None

  • static to_string(value)[源代码]

    date 或者datetime 对象转为字符串

    • 参数

      value – 需要转换的日期或者日期时间对象

    • 返回

      以服务器日期格式返回代表 value 的字符串。如果 valuedatetime类型,自动舍弃小时,分,秒,时区信息。

    • 返回类型:str

    示例:

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

    • 参数

      record – 从中获取时区的记录集。

      timestamp (datetime) – 待转换为客户端时区的naive datetime值 (UTC表示的)

    • 返回

      按上下文时区转换为时区敏感的datetime

    • 返回类型

      datetime

  • 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

    • 参数

      value (str 或者 date 或者 datetime) – 需要转换的值

    • 返回

      代表 value的对象

    • 返回类型

      datetime 或者None

关系字段(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_nameinverse_name 参数是必选参数,除非是相关或者扩展字段

class odoo.fields.Many2many[源代码]

Many2many字段的值为一个结果集。

参数:

  • comodel_name – 目标模型的名称,必选参数,除非是关联或者扩展字段
  • relation (str) – 数据库中存储关系的表名,可选参数。
  • column1 (str) – relation表中引用"这些"记录的列名,可选参数
  • column2 (str) – relation表中引用"那些"记录的列名,可选参数

relation, column1column2 参数可选。 如果未给定,自动根据模型名称生成,提供的不同的model_namecomodel_name

注意,ORM不支持在给定模型,使用同样的comodel,创建多个省略了relation参数的字段,因为这些字段将使用相同的表。ORM阻止两个Many2many字段使用相同的relation参数,除非:

  • 两个字段都使用相同的模型, comodel并显示指定relation参数,否则
  • 至少有一个字段属于携带_auto = False的模型

参数:

  • domain – 用于设置客户端候选值的条件 (domain 或者字符串),可选
  • context (dict) – 处理该字段时供客户端使用的上下文
  • check_company (bool) – 标记需要在 _check_company()中校验的字段。取决于字段属性,添加一个默认的公司条件
  • limit (int) – 读取时用的可选限制

注意:odoo不会在当前模型对应表中为One2manyMany2many类型的属性建立对应的表字段,但会为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 fields)

计算字段的一种特殊情况是相关(代理)字段,它提供当前记录上子字段的值。它们是通过设置related参数来定义的,与常规计算字段一样,它们可以存储:

nickname = fields.Char(related='user_id.partner_id.name', store=True)

related字段的值是通过遍历一系列关系字段并读取所访问模型上的字段来给出的。要遍历的字段的完整序列由related属性指定

如果未重新定义某些字段属性,则会自动从源字段中复制这些属性:stringhelprequired(仅当序列中的所有字段都是必需的时)、groupsdigitssizetranslatecleaning”、“selectioncomodel_namedomaincontext。所有无语义属性都从源字段复制。

默认的, 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 可以用于引用另一个模型中的 One2manyMany2many 字段,前提是通过当前模型的一个Many2one关系来实现的。 One2manyMany2many 不被支持,无法正确的汇总结果:

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

自动生成的字段

  • odoo.fields.id

    ID字段

    如果当前记录集长度为1,返回记录集中唯一记录的ID。否则抛出一个错误

访问日志字段

如果启用_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.[Selectionstates 属性使用

  • odoo.fields.parent_id

    _parent_name的默认值,用于以树结构组织记录,并在domain中启用child_ofparent_of运算符。Many2one字段。

  • odoo.fields.parent_path

    _parent_store设置为True时,用于存储反映[_parent_name]树结构的值,并优化搜索domain中的child_ofparent_of运算符。必须使用index=True声明才能正确操作。odoo.fields.Char类型

  • odoo.fieldcnblogs.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

警告

  • 尝试读取多条记录上的字段将引发非关系字段的错误。
  • 访问一个关系字段(Many2oneOne2manyMany2many),总是返回记录集,如果未设置字段的话,则返回空记录集。

记录缓存和预取

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 仅当修饰方法中声明的字段包含在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 (上下文中的值或当前公司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使用的各种上下文数据:数据库游标(用于数据库查询)、当前用户(用

登录查看全部

参与评论

评论留言

还没有评论留言,赶紧来抢楼吧~~

手机查看

返回顶部

给这篇文章打个标签吧~

棒极了 糟糕透顶 好文章 PHP JAVA JS 小程序 Python SEO MySql 确认