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

简介: odoo ORM API学习总结兼orm学习教程
  • 当使用子字段时,依赖可使用分点路径:
@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.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

警告

  • 尝试读取多条记录上的字段将引发非关系字段的错误。
  • 访问一个关系字段(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使用的各种上下文数据:数据库游标(用于数据库查询)、当前用户(用于访问权限检查)和当前上下文(存储任意元数据)。环境还存储缓存。

所有记录集都有一个环境,它是不可变的,可以使用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)

  • 引发
    AccessError – 非法或者为授权 allowed_company_ids 上下文key内容
  • 返回
    当前公司(默认值=self.user.company_id)
  • 返回类型
    res.company

警告

在sudo模式下没有应用健康检查!在sudo模式下,用户可以访问任何公司,即使不是在他允许的公司。

这允许触发公司间修改,即使当前用户无权访问目标公司

Environment.companies

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

如果未在上下文 (allowed_company_ids)中指定, 返回当前用户的主公司(If not specified in the context(allowed_company_ids), fallback on current user companies)

  • 引发
    AccessError – 非法或者为授权 allowed_company_ids 上下文key内容
  • 返回
    当前公司(默认值=self.user.company_id)
  • 返回类型
    res.company

警告

在sudo模式下没有应用健康检查!在sudo模式下,用户可以访问任何公司,即使不是在他允许的公司。

这允许触发公司间修改,即使当前用户无权访问目标公司

目录
相关文章
|
21小时前
|
API 数据安全/隐私保护 UED
探索鸿蒙的蓝牙A2DP与访问API:从学习到实现的开发之旅
在掌握了鸿蒙系统的开发基础后,我挑战了蓝牙功能的开发。通过Bluetooth A2DP和Access API,实现了蓝牙音频流传输、设备连接和权限管理。具体步骤包括:理解API作用、配置环境与权限、扫描并连接设备、实现音频流控制及动态切换设备。最终,我构建了一个简单的蓝牙音频播放器,具备设备扫描、连接、音频播放与停止、切换输出设备等功能。这次开发让我对蓝牙技术有了更深的理解,也为未来的复杂项目打下了坚实的基础。
77 58
探索鸿蒙的蓝牙A2DP与访问API:从学习到实现的开发之旅
|
23天前
|
网络协议 API
检测指定TCP端口开放状态免费API接口教程
此API用于检测指定TCP端口是否开放,支持POST/GET请求。需提供用户ID、KEY、目标主机,可选指定端口(默认80)和地区(默认国内)。返回状态码、信息提示、检测主机、端口及状态(开放或关闭)。示例中ID和KEY为公共测试用,建议使用个人ID和KEY以享受更高调用频率。
42 14
|
24天前
|
API
获取网页状态码[可指定地域]免费API接口教程
该接口用于获取指定网址的访问状态码,支持从国内、香港、美国等地域节点访问。通过POST或GET请求,需提供用户ID、KEY及目标网址等参数。返回结果包括状态码和信息提示。 示例:https://cn.apihz.cn/api/wangzhan/getcode.php?id=88888888&key=88888888&type=1&url=www.apihz.cn。
|
24天前
|
缓存 算法 API
查询域名WHOIS信息免费API接口教程
该API用于查询顶级域名的WHOIS信息,不支持国别域名和中文域名。通过POST或GET请求,需提供用户ID、KEY及待查询域名。返回信息包括域名状态、注册商、时间等详细数据。示例与文档见官网。
|
24天前
|
API
icp备案查询免费API接口教程
该接口用于查询指定域名的ICP备案信息,支持POST或GET请求方式。请求时需提供用户ID、用户KEY及待查询的域名,可选参数为查询通道。响应中包含状态码、消息内容、备案号、备案主体、域名及审核时间等信息。示例中提供了GET和POST请求方式及返回数据样例。
|
24天前
|
API 区块链
获取指定网页基础信息【TDK】免费API接口教程
该接口用于从标准网页中提取标题、关键词、描述和图标等信息。支持POST/GET请求,需提供用户ID、KEY及目标网址等参数,可选指定访问节点。返回状态码、信息提示及提取的内容。示例与详细文档见官网。
|
26天前
|
API 数据格式
关帝灵签免费API接口教程
接口简介:提供随机获取一枝关帝灵签的服务,共100签。通过POST或GET请求,需提交用户ID和KEY。返回内容包括状态码、消息内容及灵签详情,如序号、吉凶、诗文等。示例请求与响应展示了使用方法和数据格式。
|
24天前
|
前端开发 JavaScript API
提取网页所有链接免费API接口教程
此API用于提取指定网页内的所有链接信息并进行分类,支持POST和GET请求方式。需提供用户ID、KEY及目标网址等参数,可选指定访问节点。返回结果包括状态码、信息提示及各类链接集合,如图片、视频、文档等。示例中展示了请求格式与返回数据结构。
|
12天前
|
人工智能 自然语言处理 API
Multimodal Live API:谷歌推出新的 AI 接口,支持多模态交互和低延迟实时互动
谷歌推出的Multimodal Live API是一个支持多模态交互、低延迟实时互动的AI接口,能够处理文本、音频和视频输入,提供自然流畅的对话体验,适用于多种应用场景。
61 3
Multimodal Live API:谷歌推出新的 AI 接口,支持多模态交互和低延迟实时互动
|
8天前
|
前端开发 API 数据库
Next 编写接口api
Next 编写接口api