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

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

目录
相关文章
|
9天前
|
API
车牌号归属地查询免费API接口教程
本接口用于根据车牌号查询社会车辆的归属地,不支持军车、使馆等特殊车牌。请求地址为 `https://cn.apihz.cn/api/other/chepai.php`,支持 POST 和 GET 请求。请求参数包括 `id`、`key` 和 `words`,返回数据包含车牌归属地信息。示例请求:`https://cn.apihz.cn/api/other/chepai.php?id=88888888&key=88888888&words=川B1234`。
45 21
|
8天前
|
API
获取网页重定向地址免费API接口教程
该API用于获取网页重定向跳转后的最终地址。请求地址为`https://cn.apihz.cn/api/wangzhan/tiaozhuan.php`,支持POST或GET方式。请求参数包括`id`、`key`和`url`,返回数据包含状态码`code`和最终URL`url`。示例返回:`{&quot;code&quot;:200,&quot;url&quot;:&quot;https://www.baidu.com/&quot;}`。
48 29
|
9天前
|
网络协议 API
检测指定TCP端口开放状态免费API接口教程
该API用于检测目标主机指定TCP端口是否开放,适用于检测连通状态等场景。支持指定大陆、美国、香港等检测节点。请求地址为 `https://cn.apihz.cn/api/wangzhan/port.php`,支持POST和GET请求方式。请求参数包括 `id`、`key`、`type`、`host` 和 `port`。返回参数包含检测结果和状态码。示例请求:`https://cn.apihz.cn/api/wangzhan/port.php?id=88888888&key=88888888&type=1&host=49.234.56.78&port=80`。
|
7天前
|
API 数据安全/隐私保护
抖音视频,图集无水印直链解析免费API接口教程
该接口用于解析抖音视频和图集的无水印直链地址。请求地址为 `https://cn.apihz.cn/api/fun/douyin.php`,支持POST或GET请求。请求参数包括用户ID、用户KEY和视频或图集地址。返回参数包括状态码、信息提示、作者昵称、标题、视频地址、封面、图集和类型。示例请求和返回数据详见文档。
|
12天前
|
API
图片压缩+格式转换免费API接口教程
这是一个免费的图片压缩和格式转换API接口,支持GET和POST请求。请求地址为 `https://cn.apihz.cn/api/img/yasuo.php`,需提供 `id`、`key`、`img` 等参数。返回数据包含处理后的图片URL和其他相关信息。更多详情请参考:https://www.apihz.cn/api/imgyasuo.html
|
12天前
|
API
天气预报-腾讯天气-7天-IP查询版免费API接口教程
根据IP地址自动查询该IP归属地7天天气预报的腾讯天气API。请求地址为`https://cn.apihz.cn/api/tianqi/tengxunip.php`,支持GET和POST请求。需提供ID、Key和IP地址作为参数。返回数据包含天气预报信息。
|
11天前
|
前端开发 JavaScript API
取网页纯文本内容免费API接口教程
该API用于获取指定网页的纯文本内容,去除HTML标签、CSS和JS等元素。支持POST和GET请求,需提供ID、Key、URL等参数。请求示例:https://cn.apihz.cn/api/wangzhan/getyuan.php?id=88888888&key=88888888&url=www.apihz.cn&dy=1。返回纯文本数据。
|
8天前
|
JSON API 数据格式
淘宝 / 天猫官方商品 / 订单订单 API 接口丨商品上传接口对接步骤
要对接淘宝/天猫官方商品或订单API,需先注册淘宝开放平台账号,创建应用获取App Key和App Secret。之后,详细阅读API文档,了解接口功能及权限要求,编写认证、构建请求、发送请求和处理响应的代码。最后,在沙箱环境中测试与调试,确保API调用的正确性和稳定性。
|
20天前
|
供应链 数据挖掘 API
电商API接口介绍——sku接口概述
商品SKU(Stock Keeping Unit)接口是电商API接口中的一种,专门用于获取商品的SKU信息。SKU是库存量单位,用于区分同一商品的不同规格、颜色、尺寸等属性。通过商品SKU接口,开发者可以获取商品的SKU列表、SKU属性、库存数量等详细信息。
|
21天前
|
JSON API 数据格式
店铺所有商品列表接口json数据格式示例(API接口)
当然,以下是一个示例的JSON数据格式,用于表示一个店铺所有商品列表的API接口响应