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模式下,用户可以访问任何公司,即使不是在他允许的公司。

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

目录
相关文章
|
6天前
|
数据采集 监控 安全
各种业务场景调用API代理的API接口教程
API代理的API接口在各种业务场景中具有广泛的应用,本文将介绍哪些业务场景可以使用API代理的API接口,并提供详细的调用教程和代码演示,同时,我们还将讨论在不同场景下使用API代理的API接口所带来的好处。
|
6天前
|
机器学习/深度学习 JSON JavaScript
图文讲解 Stable Diffusion API 调用教程
Stable Diffusion 是一个先进的深度学习模型,用于创造和修改图像。这个模型能够基于文本描述来生成图像,让机器理解和实现用户的创意。使用这项技术的关键在于掌握其 API,通过编程来操控图像生成的过程。
|
6天前
|
存储 缓存 网络协议
dpdk课程学习之练习笔记二(arp, udp协议api测试)
dpdk课程学习之练习笔记二(arp, udp协议api测试)
75 0
|
6天前
|
存储 安全 机器人
【LLM】智能学生顾问构建技术学习(Lyrz SDK + OpenAI API )
【5月更文挑战第13天】智能学生顾问构建技术学习(Lyrz SDK + OpenAI API )
20 1
|
6天前
|
Kubernetes 安全 API
Kubernetes学习-集群搭建篇(三) Node配置完善和API概述
Kubernetes学习-集群搭建篇(三) Node配置完善和API概述
Kubernetes学习-集群搭建篇(三) Node配置完善和API概述
|
6天前
|
安全 API 数据安全/隐私保护
email api接口配置教程步骤详解
Email API是用于程序化访问邮件服务的工具,让开发者能集成邮件功能到应用中。配置Email API包括选择供应商(如SendGrid、Mailgun、AokSend),注册获取API密钥,配置API参数,及测试邮件发送。使用Email API能提升邮件发送的可靠性和效率,便于邮件管理及营销活动。AokSend支持大量验证码发送,适合高效邮件运营。
|
6天前
|
存储 负载均衡 API
部署大模型API的实战教程
部署大模型API的实战教程可以分为以下步骤:
|
6天前
|
JavaScript 前端开发 IDE
Vue3【为什么选择Vue框架、Vue简介 、Vue API 风格 、Vue开发前的准备 、Vue项目目录结构 、模板语法、属性绑定 、 】(一)-全面详解(学习总结---从入门到深化)
Vue3【为什么选择Vue框架、Vue简介 、Vue API 风格 、Vue开发前的准备 、Vue项目目录结构 、模板语法、属性绑定 、 】(一)-全面详解(学习总结---从入门到深化)
56 1
|
6天前
|
存储 API 数据安全/隐私保护
2021年最新最全Flink系列教程__Flink高级API(四)
2021年最新最全Flink系列教程__Flink高级API(四)
36 0
|
6天前
|
消息中间件 API 数据安全/隐私保护
2021年最新最全Flink系列教程__Flink高级API(三)
2021年最新最全Flink系列教程__Flink高级API(三)
20 0

热门文章

最新文章