外部 API

Odoo 通常通过模块在内部进行扩展,但它的许多功能和所有数据也可以从外部访问,用于外部分析或与各种工具集成。部分 模型 API 可以通过 XML-RPC 轻松访问,并支持多种编程语言。

重要

从 PHP8 开始,XML-RPC 扩展可能默认不可用。请查看 手册 了解安装步骤。

注解

通过外部 API 访问数据仅适用于 自定义 Odoo 定价计划。外部 API 访问权限在 单应用免费版标准版 计划中不可用。更多信息,请访问 Odoo 定价页面 或联系您的客户成功经理。

连接

配置

如果您已经安装了 Odoo 服务器,则可以直接使用其参数。

重要

对于 Odoo 在线实例(<domain>.odoo.com),用户创建时没有 本地 密码(作为用户,您是通过 Odoo 在线身份验证系统登录,而不是由实例本身处理)。要在 Odoo 在线实例上使用 XML-RPC,您需要为要使用的用户账户设置密码:

  • 使用管理员账户登录您的实例。

  • 前往 设置 ‣ 用户与公司 ‣ 用户

  • 点击您希望用于 XML-RPC 访问的用户。

  • 点击 操作 并选择 更改密码

  • 设置一个新的 新密码 值,然后点击 更改密码

服务器 URL 是实例的域名(例如 https://mycompany.odoo.com),数据库名称 是实例的名称(例如 mycompany)。用户名更改密码 页面显示的配置用户的登录名。

url = <insert server URL>
db = <insert database name>
username = 'admin'
password = <insert password for your admin user (default: admin)>

API 密钥

14.0 新版功能.

Odoo 支持 API 密钥,并且(根据模块或设置)可能 要求 使用这些密钥来执行 Web 服务操作。

在脚本中使用 API 密钥的方式是简单地将您的 密码 替换为密钥。登录名仍然保持不变。您应该像对待密码一样小心存储 API 密钥,因为它们本质上提供了对您用户账户的相同访问权限(尽管它们不能用于通过界面登录)。

为了向您的账户添加密钥,只需前往您的 偏好设置 (或 我的个人资料 ):

../../_images/preferences.png

然后打开 账户安全 标签页,并点击 新建 API 密钥

../../_images/account-security1.png

输入密钥的描述,该描述应尽可能清晰完整:这是您日后识别密钥并判断是否应删除或保留它们的唯一方法。

点击 生成密钥 ,然后复制提供的密钥。请妥善保存此密钥:它等同于您的密码,就像您的密码一样,系统以后无法检索或再次显示该密钥。如果您丢失了此密钥,则需要创建一个新的密钥(并可能删除丢失的那个)。

一旦您在账户中配置了密钥,它们将出现在 新建 API 密钥 按钮上方,并且您可以删除它们:

../../_images/delete-key.png

已删除的 API 密钥无法恢复或重新设置。您需要生成一个新的密钥,并更新所有使用旧密钥的地方。

测试数据库

为了简化探索,您还可以向 https://demo.odoo.com 请求一个测试数据库:

import xmlrpc.client
info = xmlrpc.client.ServerProxy('https://demo.odoo.com/start').start()
url, db, username, password = info['host'], info['database'], info['user'], info['password']

登录

Odoo 要求 API 用户在查询大多数数据之前进行身份验证。

xmlrpc/2/common 端点提供了一些不需要身份验证的元调用,例如身份验证本身或获取版本信息。在尝试进行身份验证之前,为了验证连接信息是否正确,最简单的调用是请求服务器版本。身份验证本身通过 authenticate 函数完成,并返回一个用户标识符( uid ),该标识符用于代替登录名进行身份验证调用。

common = xmlrpc.client.ServerProxy('{}/xmlrpc/2/common'.format(url))
common.version()

结果:

{
    "server_version": "13.0",
    "server_version_info": [13, 0, 0, "final", 0],
    "server_serie": "13.0",
    "protocol_version": 1,
}
uid = common.authenticate(db, username, password, {})

调用方法

第二个端点是 xmlrpc/2/object 。它通过 execute_kw RPC 函数调用 Odoo 模型的方法。

每次调用 execute_kw 时需要以下参数:

  • 使用的数据库,字符串类型

  • 用户 ID(通过 authenticate 获取),整数类型

  • 用户的密码,字符串类型

  • 模型名称,字符串类型

  • 方法名称,字符串类型

  • 按位置传递的参数数组/列表

  • 按关键字传递的参数映射/字典(可选)

Example

例如,要在 res.partner 模型中搜索记录,我们可以调用 name_search ,按位置传递 name 并按关键字传递 limit (以获取最多 10 个结果):

models = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url))
models.execute_kw(db, uid, password, 'res.partner', 'name_search', ['foo'], {'limit': 10})

结果:

true

列出记录

可以通过 search() 列出和过滤记录。

search() 接受一个强制的 过滤器(可能为空),并返回所有匹配过滤器的记录的数据库标识符。

Example

例如,列出客户公司:

models.execute_kw(db, uid, password, 'res.partner', 'search', [[['is_company', '=', True]]])

结果:

[7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74]

分页

默认情况下,搜索将返回所有符合条件的记录 ID,这可能会是一个巨大的数字。可以使用 offsetlimit 参数来仅检索所有匹配记录的一个子集。

Example

models.execute_kw(db, uid, password, 'res.partner', 'search', [[['is_company', '=', True]]], {'offset': 10, 'limit': 5})

结果:

[13, 20, 30, 22, 29]

计数记录

与其检索可能庞大的记录列表并对其进行计数,不如使用 search_count() 来仅检索与查询匹配的记录数量。它接受与 search() 相同的 过滤器,并且没有其他参数。

Example

models.execute_kw(db, uid, password, 'res.partner', 'search_count', [[['is_company', '=', True]]])

结果:

19

注解

如果其他用户正在使用服务器,则先调用 search 再调用 search_count (或反之)可能会导致不一致的结果:存储的数据可能在两次调用之间发生了变化。

读取记录

记录数据可以通过 read() 方法访问,该方法接受一个 ID 列表(由 search() 返回),以及一个可选的字段列表以供提取。默认情况下,它会提取当前用户可以读取的所有字段,这通常会是一个庞大的数量。

Example

ids = models.execute_kw(db, uid, password, 'res.partner', 'search', [[['is_company', '=', True]]], {'limit': 1})
[record] = models.execute_kw(db, uid, password, 'res.partner', 'read', [ids])
# count the number of fields fetched by default
len(record)

结果:

121

相反,仅选择三个被认为有趣的字段。

models.execute_kw(db, uid, password, 'res.partner', 'read', [ids], {'fields': ['name', 'country_id', 'comment']})

结果:

[{"comment": false, "country_id": [21, "Belgium"], "id": 7, "name": "Agrolait"}]

注解

即使未请求 id 字段,它也始终会被返回。

列出记录字段

fields_get() 可用于检查模型的字段,并查看哪些字段可能是感兴趣的。

因为它返回大量元信息(也被客户端程序使用),所以在打印之前应进行过滤。对于人类用户来说,最有趣的条目是 string (字段标签)、 help (如果有帮助文本)和 type (用于了解预期值或更新记录时发送的值)。

Example

models.execute_kw(db, uid, password, 'res.partner', 'fields_get', [], {'attributes': ['string', 'help', 'type']})

结果:

{
    "ean13": {
        "type": "char",
        "help": "BarCode",
        "string": "EAN13"
    },
    "property_account_position_id": {
        "type": "many2one",
        "help": "The fiscal position will determine taxes and accounts used for the partner.",
        "string": "Fiscal Position"
    },
    "signup_valid": {
        "type": "boolean",
        "help": "",
        "string": "Signup Token is Valid"
    },
    "date_localization": {
        "type": "date",
        "help": "",
        "string": "Geo Localization Date"
    },
    "ref_company_ids": {
        "type": "one2many",
        "help": "",
        "string": "Companies that refers to partner"
    },
    "sale_order_count": {
        "type": "integer",
        "help": "",
        "string": "# of Sales Order"
    },
    "purchase_order_count": {
        "type": "integer",
        "help": "",
        "string": "# of Purchase Order"
    },

搜索并读取

由于这是一个非常常见的任务,Odoo 提供了一个 search_read() 快捷方式,顾名思义,它等同于先执行 search() 再执行 read() ,但避免了发出两个请求并保留 ID 的麻烦。

其参数类似于 search() 的参数,但它还可以接受一个 fields 列表(类似于 read() ,如果未提供该列表,则会获取所有匹配记录的字段)。

Example

models.execute_kw(db, uid, password, 'res.partner', 'search_read', [[['is_company', '=', True]]], {'fields': ['name', 'country_id', 'comment'], 'limit': 5})

结果:

[
    {
        "comment": false,
        "country_id": [ 21, "Belgium" ],
        "id": 7,
        "name": "Agrolait"
    },
    {
        "comment": false,
        "country_id": [ 76, "France" ],
        "id": 18,
        "name": "Axelor"
    },
    {
        "comment": false,
        "country_id": [ 233, "United Kingdom" ],
        "id": 12,
        "name": "Bank Wealthy and sons"
    },
    {
        "comment": false,
        "country_id": [ 105, "India" ],
        "id": 14,
        "name": "Best Designers"
    },
    {
        "comment": false,
        "country_id": [ 76, "France" ],
        "id": 17,
        "name": "Camptocamp"
    }
]

创建记录

使用 create() 创建模型的记录。该方法创建单个记录并返回其数据库标识符。

create() 接受字段到值的映射,用于初始化记录。对于具有默认值且未通过映射参数设置的任何字段,将使用默认值。

Example

id = models.execute_kw(db, uid, password, 'res.partner', 'create', [{'name': "New Partner"}])

结果:

78

警告

虽然大多数值类型是预期的( Integer 为整数, CharText 为字符串),

更新记录

可以使用 write() 更新记录。它接受要更新的记录列表以及类似于 create() 的字段到值的映射。

可以同时更新多条记录,但它们将获得相同的字段值。无法执行“计算”更新(即设置的值依赖于记录的现有值)。

Example

models.execute_kw(db, uid, password, 'res.partner', 'write', [[id], {'name': "Newer partner"}])
# get record name after having changed it
models.execute_kw(db, uid, password, 'res.partner', 'read', [[id], ['display_name']])

结果:

[[78, "Newer partner"]]

删除记录

可以通过向 unlink() 提供记录 ID 来批量删除记录。

Example

models.execute_kw(db, uid, password, 'res.partner', 'unlink', [[id]])
# check if the deleted record is still in the database
models.execute_kw(db, uid, password, 'res.partner', 'search', [[['id', '=', id]]])

结果:

[]

检查与自省

虽然我们之前使用 fields_get() 查询模型,并从一开始就在使用任意模型,但 Odoo 将大部分模型元数据存储在几个元模型中,这些元模型允许通过 XML-RPC 查询系统并在运行时动态修改模型和字段(有一些限制)。

ir.model

通过其各种字段提供有关 Odoo 模型的信息。

name

模型的人类可读描述

model

系统中每个模型的名称

state

模型是通过 Python 代码生成的( base )还是通过创建 ir.model 记录生成的( manual

field_id

通过 One2manyir.model.fields 的模型字段列表

view_ids

One2many 到为模型定义的 视图架构

access_ids

One2many 到模型上设置的 访问权限 的关系

ir.model 可用于

  • 查询系统中已安装的模型(作为对模型进行操作或探索系统内容的前提条件)。

  • 获取有关特定模型的信息(通常是通过列出与其关联的字段)。

  • 通过 RPC 动态创建新模型。

重要

  • 自定义模型名称必须以 x_ 开头。

  • 必须提供 state 并将其设置为 manual,否则模型将不会被加载。

  • 无法向自定义模型添加新的 方法,只能添加字段。

Example

自定义模型最初将仅包含所有模型上可用的“内置”字段:

models.execute_kw(db, uid, password, 'ir.model', 'create', [{
    'name': "Custom Model",
    'model': "x_custom_model",
    'state': 'manual',
}])
models.execute_kw(db, uid, password, 'x_custom_model', 'fields_get', [], {'attributes': ['string', 'help', 'type']})

结果:

{
    "create_uid": {
        "type": "many2one",
        "string": "Created by"
    },
    "create_date": {
        "type": "datetime",
        "string": "Created on"
    },
    "__last_update": {
        "type": "datetime",
        "string": "Last Modified on"
    },
    "write_uid": {
        "type": "many2one",
        "string": "Last Updated by"
    },
    "write_date": {
        "type": "datetime",
        "string": "Last Updated on"
    },
    "display_name": {
        "type": "char",
        "string": "Display Name"
    },
    "id": {
        "type": "integer",
        "string": "Id"
    }
}

ir.model.fields

提供有关 Odoo 模型字段的信息,并允许在不使用 Python 代码的情况下添加自定义字段。

model_id

Many2one 到字段所属的 ir.model

name

字段的技术名称(用于 readwrite

field_description

字段的用户可读标签(例如 fields_get 中的 string

ttype

要创建的字段的 类型

state

字段是通过 Python 代码创建的( base )还是通过 ir.model.fields 创建的( manual

requiredreadonlytranslate

启用字段上的相应标志

groups

字段级访问控制 ,一个到 res.groupsMany2many

selectionsizeon_deleterelationrelation_fielddomain

特定类型的属性和自定义,详情请参阅 字段文档

重要

  • 与自定义模型类似,只有使用 state="manual" 创建的新字段才会作为实际字段激活到模型中。

  • 无法通过 ir.model.fields 添加计算字段,某些字段元信息(默认值、onchange)也无法设置。

Example

id = models.execute_kw(db, uid, password, 'ir.model', 'create', [{
    'name': "Custom Model",
    'model': "x_custom",
    'state': 'manual',
}])
models.execute_kw(db, uid, password, 'ir.model.fields', 'create', [{
    'model_id': id,
    'name': 'x_name',
    'ttype': 'char',
    'state': 'manual',
    'required': True,
}])
record_id = models.execute_kw(db, uid, password, 'x_custom', 'create', [{'x_name': "test record"}])
models.execute_kw(db, uid, password, 'x_custom', 'read', [[record_id]])

结果:

[
    {
        "create_uid": [1, "Administrator"],
        "x_name": "test record",
        "__last_update": "2014-11-12 16:32:13",
        "write_uid": [1, "Administrator"],
        "write_date": "2014-11-12 16:32:13",
        "create_date": "2014-11-12 16:32:13",
        "id": 1,
        "display_name": "test record"
    }
]