全书完整目录请见:Odoo 14开发者指南(Cookbook)第四版
Odoo服务支持远程过程调用(RPC),即可以在外部应用中连接Odoo实例,一种应用场景是在Android应用中显示发货订单的状态,而安卓是使用Java编写的(译者注:当然原生也早已支持 Kotlin 了)。这时可以通过RPC从Odoo获取投递状态,也可以在任意模型中调用公有方法。当前,我们需要设置相应的权限来执行这些操作,因为RPC遵循数据库所定义的访问权限和记录规则,因此RPC非常的安全。Odoo RPC具有平台无关性,因此可以在任何平台中使用它,包括Odoo.sh、在线平台和自托管平台。Odoo RPC可在任何一种编程语言中使用,因此可以将Odoo集成到任意的外部应用中。
Odoo提供了两种类型的RPC API – XML-RPC 和 JSON-RPC。本章中,我们将学习如何在外部程序中使用这些RPC。最后,读者将学到如何通过OCA的odoorpc库使用Odoo RPC。
本章中,我们将讲解如下内容:
- 通过XML-RPC登录/连接Odoo
- 通过XML-RPC搜索/读取记录
- 通过XML-RPC创建/更新/删除记录
- 通过XML-RPC调用方法
- 通过JSON-RPC登录/连接Odoo
- 通过JSON-RPC获取/搜索记录
- 通过JSON-RPC创建/更新/删除记录
- 通过JSON-RPC调用方法
- OCA odoorpc库
- 生成API密钥
技术准备
本章中我们将使用第十九章 使用Odoo.sh管理、部署和测试中所创建的my_library模块。初始的my_library模块代码请见GitHub仓库:https://github.com/alanhou/odoo14-cookbook/tree/main/Chapter20/00_initial_module。
这里我们不会介绍读者可能不熟悉的新语言。我们会继续使用Python来访问RPC API。读者当然可以使用其它语言,这里访问RPC的流程同样适用于其它语言。
通过RPC连接Odoo,我们需要运行Odoo实例来供连接。整章中我们都将假设你的Odoo服务运行于http://localhost:8069且安装了my_library模块的数据库名称为odoo-test。注意你其实可以通过RPC连接任意有效IP或域名。
通过XML-RPC登录/连接Odoo
在本节中,我们将通过RPC进行用户验证以检测所提供的认证信息是否有效。
准备工作
要通过RPC连接Odoo实例,需要运行Odoo实例来供连接。假定你的Odoo服务运行于http://localhost:8069(可根据具体情况修改代码中的地址)并且已安装了my_library模块。
如何实现…
执行如下步骤来通过RPC来进行用户校验:
- 添加odoo_authenticate.py文件,可以将该文件放在任何位置,因为RPC程序是独立运行的。
- 在该文件中添加如下代码:
1234567891011121314from xmlrpc import clientserver_url = 'http://localhost:8069'db_name = 'odoo-test'username = 'admin'password = 'admin'common = client.ServerProxy('%s/xmlrpc/2/common' % server_url)user_id = common.authenticate(db_name, username, password, {})if user_id:print("Success: User id is", user_id)else:print("Failed: wrong credentials") - 在终端中使用如下命令运行这一Python脚本:
1python3 odoo_authenticate.py
如果所提供的为有效的登录名和密码,会打印成功的消息及用户ID。
运行原理…
本节中,我们使用了Python的xmlrpc 库来通过XML-RPC访问Odoo实例。这是一个Python标准库,无需进行任何安装即可使用。
Odoo在/xmlrpc/2/common端点上提供了XML-RPC来进行验证操作。该端点用于无需进行验证的元方法。authentication() 方法本身是一个公有方法,因此可在外部调用。authentication()方法可接收4个参数:数据库名、用户名、密码以及user agent环境。user agent环境是必传的参数,但如果不想传入user agent参数,请至少传递一个空字典。
在执行带有全部有效参数的authenticate() 方法时,会调用Odoo服务并执行认证。然后如果所给定的登录ID和密码正确的话会返回用户ID。如果用户不存在或是密码错误的话则返回False。
需要在通过RPC访问数据之前使用authenticate()方法。这是因为使用错误的认证信息访问数据会产生报错。
📝注:Odoo的在线实例 (*.odoo.com)使用OAuth认证,因此并没有在实例中设置本地密码。要对这些实例使用XML-RPC,需要在实例的Settings > Users > Users菜单中手动设置用户的密码。
此外,用于访问数据的方法要求传入用户ID而非用户名,因此也需要使用authenticate()方法来获取用户的ID。
扩展知识…
/xmlrpc/2/common端点还提供另一个方法:version()。调用该方法可以无需认证信息。它会返回Odoo实例的版本信息。以下是version()方法的使用示例:
1 2 3 4 5 6 7 |
from xmlrpc import client server_url = 'http://localhost:8069' common = client.ServerProxy('%s/xmlrpc/2/common' % server_url) version_info = common.version() print(version_info) |
前面的程序会给出如下的输出:
该程序会根据服务端打印出版本信息。
通过XML-RPC搜索/读取记录
在本节中,我们将学习如何通过RPC来从Odoo实例中获取数据。用户可以访问受安全权限控制和记录规则所限之外大部分的数据,RPC可以在多种场景中使用,如收集数据进行分析、一次性操作大量数据或是获取数据来在另一个软件/系统中展示。有无尽的可能性,可以在任何需要的时候使用RPC。
准备工作
我们将创建一个Python程序来从library.book模型中获取图书数据。请确保已经安装了my_library模块并且服务运行于http://localhost:8069。
如何实现…
执行如下步骤来通过RPC获取图书信息:
- 添加books_data.py文件。可将文件放在任意位置,因为RPC程序是独立运行的。
- 在该文件中添加如下代码:
12345678910111213141516171819202122232425from xmlrpc import clientserver_url = 'http://localhost:8069'db_name = 'odoo-test'username = 'admin'password = 'admin'common = client.ServerProxy('%s/xmlrpc/2/common' % server_url)user_id = common.authenticate(db_name, username, password, {})models = client.ServerProxy('%s/xmlrpc/2/object' % server_url)if user_id:search_domain = ['|', ['name', 'ilike', 'odoo'], ['name', 'ilike', 'sql']]books_ids = models.execute_kw(db_name, user_id, password,'library.book', 'search',[search_domain],{'limit': 5})print('Books ids found:', books_ids)books_data = models.execute_kw(db_name, user_id, password,'library.book', 'read',[books_ids, ['name', 'date_release']])print("Books data:", books_data)else:print('Wrong credentials') - 使用如下命令来在终端中运行这个Python脚本:
1python3 books_data.py
以上的程序将获取到图书数据并输出如下:
上图中显示的输出内容依赖于数据库中的数据。读者Odoo实例的数据会不同,因而输出内容也不同。
运行原理…
要访问图书数据,首先要执行认证。在程序的开头处我们以通过XML-RPC登录/连接Odoo一节中相同的方式进行了认证。如果提供了有效认证信息,authentication()方法会返回用户记录的id。我们将使用这个用户ID来获取图书数据。
/xmlrpc/2/object端点用于操作数据库。在本节中,我们使用了object端点来获取图书数据。对比 /xmlrpc/2/common端点,这个端点在没有认证信息时无法使用。借助这个端点,我们通过execute_kw()可以访问任意模型的对公方法,execute_kw()接收如下参数:
- 数据库名
- 用户 ID(从authenticate()方法中获取)
- 密码
- 模型名,如res.partner, library.book
- 方法名,如search, read或create
- 一个位置参数数组
- 一个关键字参数的字典(可选)
在本例中,我们希望获取图书的信息。这可通过search()和read()的组合来实现。图书信息存储于library.book模型中,因此在execute_kw()中我们使用library.book 作为模型名、search作为方法名。这会调用ORM的搜索方法并返回记录ID。唯一的不同是ORM的搜索方法返回一个记录集,而这个search方法返回一个ID列表。
在execute_kw()中,可以向所提供方法传递参数和关键字参数。search()方法接收一个域作为位置参数,通过传递域来过滤图书。search方法还有其它的可选关键字参数,如 limit, offset, count和order,我们使用了limit参数来仅获取5条记录。这会返回一个名称中包含odoo或SQL字符串的图书ID列表。
但我们还需要从数据库中获取图书数据。使用read方法来完成这一操作。read方法接收一个ID和字段列表来完成这一任务。在第3步中的最后,我们使用了通过search方法接收到的图书ID列表,然后使用这些图书ID来获取图书的name和release_date。这会返回一个带有图书信息的字典列表。
📝注:注意传递给execute_kw()的参数和关键字参数取决于所传递的方法。你可以通过execute_kw()使用任意ORM公有方法。仅需传递方法名、有效参数和关键字参数。这些参数将被传递到ORM的方法中。
扩展知识…
通过search()和read()方法的组合获取数据颇为耗时,因为进行了两次调用, search_read是获取同样数据的一个替代方法。可以在单次调用中同时搜索并获取数据。下面是使用search_read()方法获取图书数据的替代方式。
📝注:即便未请求id字段,read和search_read方法也会返回id字段。此外对于many2one字段,我们将获取一个由id和显示名称组成的数组。例如,create_uid many2one字段返回类似这样的结果: [12, ‘Parth Gajjar’]。
它会如同前例一样返回相同的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from xmlrpc import client server_url = 'http://localhost:8069' db_name = 'odoo-test' username = 'admin' password = 'admin' common = client.ServerProxy('%s/xmlrpc/2/common' % server_url) user_id = common.authenticate(db_name, username, password, {}) models = client.ServerProxy('%s/xmlrpc/2/object' % server_url) if user_id: search_domain = ['|', ['name', 'ilike', 'odoo'], ['name', 'ilike', 'sql']] books_data = models.execute_kw(db_name, user_id, password, 'library.book', 'search_read', [search_domain, ['name', 'date_release']], {'limit': 5}) print('Books data:', books_data) else: print('Wrong credentials') |
search_read大幅地提升了性能,因为在同一次RPC调用中获取到了结果,因此应使用search_read方法来替代search与read方法的组合。
通过XML-RPC创建/更新/删除记录
在前一节中,我们学习了如何通过RPC搜索和读取数据。本节中我们通过RPC将执行增删改查(CRUD)中余下的操作,即创建、更新(write)和删除(unlink)。
准备工作
我们将创建Python程序来对 library.book模型创建、写入和删除数据。确保已安装了my_library并且服务运行于http://localhost:8069。
如何实现…
执行如下步骤来通过RPC创建、写入及更新图书信息:
- 添加books_operation.py文件。可以将该文件放在所希望的任意位置,因为RPC程序将独立运行:
- 在该文件中添加如下代码:
12345678910111213141516171819202122232425262728293031323334353637383940414243from xmlrpc import clientserver_url = 'http://localhost:8069'db_name = 'odoo-test'username = 'admin'password = 'admin'common = client.ServerProxy('%s/xmlrpc/2/common' % server_url)user_id = common.authenticate(db_name, username, password, {})models = client.ServerProxy('%s/xmlrpc/2/object' % server_url)if user_id:# create new bookscreate_data = [{'name': 'Book 1', 'release_date': '2019-01-26'},{'name': 'Book 3', 'release_date': '2019-02-12'},{'name': 'Book 3', 'release_date': '2019-05-08'},{'name': 'Book 7', 'release_date': '2019-05-14'}]books_ids = models.execute_kw(db_name, user_id, password,'library.book', 'create',[create_data])print("Books created:", books_ids)# Write in existing book recordbook_to_write = books_ids[1] # We will use ids of recently created bookswrite_data = {'name': 'Books 2'}written = models.execute_kw(db_name, user_id, password,'library.book', 'write',[book_to_write, write_data])print("Books written:", written)# Delete the book recordbooks_to_delete = books_ids[2:] # We will use ids ofrecently created booksdeleted = models.execute_kw(db_name, user_id, password,'library.book', 'unlink',[books_to_delete])print('Books unlinked:', deleted)else:print('Wrong credentials') - 通过以下命令在终端中运行这个Python脚本:
1python3 books_operation.py
上面的程序会创建4条图书记录。在图书记录中更新数据并在此后删除两条记录,输出如下(根据你的数据库所创建的ID可能会不同):
write和unlink方法在操作成功执行时返回True。这表示如果响应的值为True,记录就进行了成功的更新或删除。
运行原理…
在本节中,我们通过XML-RPC执行了create, write和delete操作。这个操作还使用了/xmlrpc/2/object端点及execute_kw()方法。
从Odoo v12开始, create()方法支持在单次调用中创建多条记录。在第2步中,我们首先创建了一个带有图书信息的字典。然后我们使用了图书的字典来通过XML-RPC新建图书记录。XML-RPC调用需要两个参数来创建新记录 – create方法名及图书数据。它会在library.book模型中创建4条图书记录。我们在ORM创建记录时,它返回一个所创建记录的记录集,但如果通过RPC创建记录,它会返回一个ID列表。
write方法与create方法运行的方式相似。在write方法中,需要传递一个记录ID及字段值列表来进行写入。在本例中,我们更新了在第一部分所创建的图书的名称。这会将第二本图书的名称从Book 3更新为Book 2。这里我们仅传递了图书的一个id,但如果想要在单次调用中更新多条记录的话也可以传递ID列表。
在这个程序的第3部分中,我们删除了两条在第1部分中所创建的图书。可以通过unlink方式及记录ID列表来删除记录。
在程序成功执行之后,在数据库中可以看到两条图书记录,参照图20.3。在此程序中,我们创建了4条记录,但删除了其中的两条,因此仅能在数据库中看到两条。
扩展知识…
在通过RPC执行增删改查操作时,可能会在没有执行该操作权限时产生报错。通过check_access_rights方法,可以检测该用户是否有执行某一操作的相应权限。check_access_rights方法根据用户的访问权限返回True或False。以下是一个展示用户是否具有图书记录创建权限的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from xmlrpc import client server_url = 'http://localhost:8069' db_name = 'odoo-test' username = 'admin' password = 'admin' common = client.ServerProxy('%s/xmlrpc/2/common' % server_url) user_id = common.authenticate(db_name, username, password, {}) models = client.ServerProxy('%s/xmlrpc/2/object' % server_url) if user_id: has_access = models.execute_kw(db_name, user_id, password, 'library.book', 'check_access_rights', ['create'], {'raise_exception': False}) print('Has create access on book:', has_access ) else: print('Wrong credentials') # 输出: Has create access on book: True |
在通过RPC进行复杂操作时,可在执行操作前使用check_access_rights来确定是否有相应的权限。
通过XML-RPC调用方法
在Odoo中,RPC API并不只限于增删改查操作,还可以调用业务逻辑。在本节中,我们将调用make_available方法来修改图书的状态。
准备工作
我们将创建一个Python程序来调用library.book模型的make_available方法。确保已安装了my_library模块并且服务运行于http://localhost:8069。
如何实现…
执行如下步骤来通过RPC创建、写入及更新图书的信息:
- 添加books_method.py文件。可以将该文件放在所希望的任何位置,因为RPC程序是独立运行的。
- 在该文件中添加如下代码:
123456789101112131415161718192021222324252627282930from xmlrpc import clientserver_url = 'http://localhost:8069'db_name = 'odoo-test'username = 'admin'password = 'admin'common = client.ServerProxy('%s/xmlrpc/2/common' % server_url)user_id = common.authenticate(db_name, username, password, {})models = client.ServerProxy('%s/xmlrpc/2/object' % server_url)if user_id:# Create book with state draftbook_id = models.execute_kw(db_name, user_id, password,'library.book', 'create',[{'name': 'New Book', 'date_release': '2019-01-26', 'state': 'draft'}])# Call make_available method on new bookmodels.execute_kw(db_name, user_id, password,'library.book', 'make_available',[[book_id]])# check book status after method callbook_data = models.execute_kw(db_name, user_id, password,'library.book', 'read',[[book_id], ['name', 'state']])print('Book state after method call:', book_data[0]['state'])else:print('Wrong credentials') - 在终端中通过如下命令运行这个Python脚本:
1python3 books_method.py
以上程序会创建一个draft状态的图书,然后通过调用make_available方法来修改图书的状态。然后,我们将获取到图书的数据来查看图书状态,它将会给出如下的输出:
本节的程序会新建图书记录并通过调用模型方法修改图书状态。在程序结束时,我们读取了图书记录并打印了更新后的状态。
运行原理…
可以通过RPC调用任意模型方法。这有助于我们执行业务逻辑却又不出现负面效果。例如,我们通过RPC创建了销售订单,然后调用sale.order 的action_confirm方法。这等价于在销售订单表单中点击Confirm按钮。
可以调用模型的任意公有方法,但无法通过RPC调用私有方法。以_开头的方法名称之为私有方法,如_get_share_url()和_get_data()。
使用这些方法是安全的,因为它们通过ORM并遵循所有的安全规则。如果该方法访问了未授权的记录,则会产生错误。
在示例中,我们创建了一个状态为draft的图书。然后我们再次调用了RPC来调用make_available方法,这会将该书的状态修改为available。最后,我们又进行了一次RPC调用来查看图书的状态。它会显示图书的状态已修改为available,如图20.4所示。
内部没有返回内容的方法默认返回None。这种方法不能通过RPC使用。因此,如果你希望通过RPC使用自己的方法,至少请添加返回True的语句。
扩展知识…
如果通过方法产生了异常,所有在事务中执行的操作会自动回滚到初始状态。这仅适用于单个事务(单次RPC调用)。例如,设想一下你对服务端做了两次RPC调用并且在第二次调用时产生了异常。这会在第二次RPC调用中对执行的操作进行回滚。通过第一个RPC调用所执行的操作并不会进行回滚。因此,需要通过RPC执行一个复杂的操作。推荐通过模型中的方法在单个RPC调用中执行它
通过JSON-RPC登录/连接Odoo
Odoo还提供另一种类型的RPC API: JSON-RPC。由名称可知,JSON-RPC以JSON格式来进行操作并使用 jsonrpc 2.0规范。本节中,我们将学习如何使用JSON-RPC来进行登录。Odoo网页客户端本身使用JSON-RPC来从服务端获取数据。
准备工作
本节中,我们将通过JSON-RPC执行用户认证来检查给定的认证信息是否有效。确保已安装了my_library模块并且服务运行于http://localhost:8069。
如何实现…
执行如下步骤来通过RPC进行用户认证:
- 添加jsonrpc_authenticate.py文件。可以把该文件放在任意位置,因为RPC会独立运行。
- 在这个文件中添加如下代码:
12345678910111213141516171819202122232425262728293031323334import jsonimport randomimport requestsserver_url = 'http://localhost:8069'db_name = 'odoo-test'username = 'admin'password = 'admin'json_endpoint = "%s/jsonrpc" % server_urlheaders = {"Content-Type": "application/json"}def get_json_payload(service, method, *args):return json.dumps({"jsonrpc": "2.0","method": 'call',"params": {"service": service,"method": method,"args": args},"id": random.randint(0, 100000000),})payload = get_json_payload("common", "login", db_name,username, password)response = requests.post(json_endpoint, data=payload,headers=headers)user_id = response.json()['result']if user_id:print("Success: User id is", user_id)else:print("Failed: wrong credentials") - 使用如下命令在终端中运行这个Python脚本:
1python3 jsonrpc_authenticate.py
运行以上程序并传入有效登录名和密码时,该程序会打印一个带有用户 id 的成功消息,如下:
JSON的认证和XML-RPC类似,但返回的结果是JSON格式。
运行原理…
JSON-RPC使用JSON格式通过/jsonrpc端点来与服务端通讯。在本例中,我们使用了Python的requests包来进行POST请求,但你也可以使用其它想要用的包,如urllib。
JSON-RPC仅接收JSON-RPC 2.0规范格式的数据,参见https://www.jsonrpc.org/specification了解有关JSON-RPC格式的更多知识。在本例中,我们创建了 get_json_payload()方法。该方法将以有效的JSON-RPC 2.0格式准备加载数据。这个方法接收服务名和要调用的方法,并且剩余的参数将会放在*args中。我们将在后续的小节中使用这一方法。JSON-RPC接收JSON格式的请求并且这些请求仅接收包含{“Content-Type”: “application/json”}头的请求。请求的结果为JSON格式。
类似XML-RPC,所有的公有方法,包含login,都在common服务下。出于这一原因,我们传递了common作为服务以及login作为方法来准备JSON请求。login方法要求一些额外的参数,因此我们传递了数据库名、用户名和密码。然后我们使用请求数据和头信息对这个JSON端点进行了POST请求。如果传递了正确的用户名和密码,该方法返回用户 id。响应将为JSON格式并会在result键中获取到结果。
📝注:注意本节中创建的get_json_payload()方法用于在示例中去除重复代码。并非必须使用它,可以自己做调整。
扩展知识…
类似XML-RPC,JSON-RPC中也可使用version方法。version方法在common服务中并且可对外公开访问。无需登录信息即可获取到版本信息。下例展示了如何获取Odoo服务端版本信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import json import random import requests server_url = 'http://localhost:8069' json_endpoint = "%s/jsonrpc" % server_url headers = {"Content-Type": "application/json"} def get_json_payload(service, method, *args): ... # see full function definition in last section payload = get_json_payload("common", "version") response = requests.post(json_endpoint, data=payload, headers=headers) print(response.json()) |
这个程序会显示如下输出:
此程序会根据服务端打印出版本信息。
通过JSON-RPC获取/搜索记录
在前一节中,我们学习了如何通过JSON-RPC来进行认证。本节中,我们将学习如何通过JSON-RPC来从Odoo实例中获取数据。
准备工作
本节中我将通过JSON-RPC来获取图书信息。确保已安装my_library模块并且服务运行于http://localhost:8069。
如何实现…
执行如下步骤来从library.book模型中获取图书数据:
- 添加jsonrpc_fetch_data.py文件。可以将这个文件放在任意所希望的位置,因为RPC程序将独立运行。
- 在该文件中添加如下代码:
1234567891011121314151617181920# 放置authentication和get_json_payload方法(参见第一个jsonrpc小节)if user_id:# 搜索图书idsearch_domain = ['|', ['name', 'ilike', 'odoo'], ['name', 'ilike', 'sql']]payload = get_json_payload("object", "execute_kw",db_name, user_id, password,'library.book', 'search', [search_domain], {'limit': 5})res = requests.post(json_endpoint, data=payload, headers=headers).json()print('Search Result:', res) # ids will be in result keys# 为图书id 读取数据payload = get_json_payload("object", "execute_kw",db_name, user_id, password,'library.book', 'read', [res['result'], ['name','date_release']])res = requests.post(json_endpoint, data=payload, headers=headers).json()print('Books data:', res)else:print("Failed: wrong credentials") - 通过如下命令在终端中运行这个Python脚本:
1python3 jsonrpc_fetch_data.py
以上程序将返回如下输出。第一个RPC调用会打印图书的id并且第二个会打印针对图书id的信息:
上图中输出结果是基于我的数据库中的数据。你的Odoo实例中数据可能不同,因而输出也会不一样。
运行原理…
在通过JSON-RPC登录/连接Odoo一节中,我们学习到可以进行用户名密码的验证。如果登录的详情都正确的话,RPC调用会返回user_id。然后可以使用这个user_id来获取模型数据。类似XMLRPC,我们需要使用search和read的组合来从模型中获取数据。要获取数据,我们使用object来作为服务、execute_kw来作为方法,execute_kw与我们用XML-RPC获取数据时使用的是同一个方法,因此它也接收相同的参数,如下:
- 数据库名
- 用户 ID(从authenticate()方法中获取)
- 密码
- 模型名,如res.partner, library.book
- 方法名,如search, read, create
- 一个位置参数数组(args)
- 一个关键字参数的字典(可选)(kwargs)
在本例中,我们首先调用了search方法。execute_kw()方法通常接收必选参数来作为位置参数、可选参数作为关键字参数。在search方法中,domain是必选参数,因此我们在列表中进行传递并传递了可选参数limit作为关键字参数(字典)。你将获取到一个JSON格式的响应,并且在本节中search()方法RPC的响应会在result键中包含图书的ID。
在第2步中,我们通过read方法作了一次RPC调用。为读取图书的信息,我们传递了两个位置参数:图书ID列表和要获取字段的列表。这个RPC调用将以JSON格式返回图书信息并且可以在result键中访问它。
📝注:除execute_kw()外,还可以使用execute来作为方法。它不支持关键字参数,因此如果想要传递一些可选参数的话需要传递所有的中间参数。
扩展知识…
类似于XML-RPC,你可以使用search_read()方法来代替 search() 和 read()方法的组合,因为那样耗时会略多。以下是通过search_read()来获取图书数据的一种替代方式。它会与前例返回相同的输出:
Similar to XML-RPC, you can use the search_read() method instead of the search() and read() method combination as it is slightly time-consuming. Take a look at the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 放置authentication和get_json_payload方法(参见第一个jsonrpc小节) if user_id: # 对图书 id 进行搜索和读取 search_domain = ['|', ['name', 'ilike', 'odoo'], ['name', 'ilike', 'sql']] payload = get_json_payload("object", "execute_kw", db_name, user_id, password, 'library.book', 'search_read', [search_domain, ['name', 'date_release']], {'limit': 5}) res = requests.post(json_endpoint, data=payload, headers=headers).json() print('Books data:', res) else: print("Failed: wrong credentials") |
以上代码通过另一种方式使用了search_read()获取图书数据。这会返回和前例相同的数据。
通过JSON-RPC创建/更新/删除记录
在前一小节中我们了解了如何通过JSON-RPC搜索和读取数据。在本节中,我们将通过RPC执行增删改查中剩余的操作 – 创建、更新(写)和删除(unlink)。
准备工作
我们将创建一个Python程序来创建、写入和删除 library.book 模型中的数据。确保已安装了my_library模块并且服务运行于http://localhost:8069。
如何实现…
执行如下步骤来通过RPC创建、写入和删除图书的信息:
- 添加jsonrpc_operation.py文件。可以将该文件放在任意位置,因为RPC程序会独立运行。
- 在文件中添加如下代码:
1234567891011121314151617181920212223242526272829303132333435363738# 放置authentication和get_json_payload方法(参见最后一节获取更多内容)if user_id:# 创建图书记录create_data = [{'name': 'Book 1', 'date_release': '2019-01-26'},{'name': 'Book 3', 'date_release': '2019-02-12'},{'name': 'Book 5', 'date_release': '2019-05-08'},{'name': 'Book 7', 'date_release': '2019-05-14'}]payload = get_json_payload("object", "execute_kw",db_name, user_id, password, 'library.book', 'create',[create_data])res = requests.post(json_endpoint, data=payload,headers=headers).json()print("Books created:", res)books_ids = res['result']# 写入已有图书记录book_to_write = books_ids[1] # 我们将使用新近创建的idwrite_data = {'name': 'Book 2'}payload = get_json_payload("object", "execute_kw",db_name, user_id, password, 'library.book', 'write',[book_to_write, write_data])res = requests.post(json_endpoint, data=payload,headers=headers).json()print("Books written:", res)# 在已有图书记录中进行删除book_to_unlink = books_ids[2:] # 我们将使用新近创建的idpayload = get_json_payload("object", "execute_kw",db_name, user_id, password, 'library.book', 'unlink',[book_to_unlink])res = requests.post(json_endpoint, data=payload, headers=headers).json()print("Books deleted:", res)else:print("Failed: wrong credentials") - 使用如下命令在终端中运行Python脚本:
1python3 jsonrpc_operation.py
以上程序将创建4本书。写入一本书并删除两本书得到如下输出(根据你的数据库所创建的 ID 可能会不同):
如果操作成功write和unlink方法返回True。这表示如果在响应内容中得到True,表明记录已成功更新或删除。
运行原理…
execute_kw用于创建、更新和删除的操作。从Odoo 12开始,create方法支持创建多条记录。因此我们准备了包含4本书信息的字典。然后我们通过library.book作为模型名、create作为方法名进行了JSON-RPC调用。它会在数据库中创建4条图书记录并返回带有这些新建图书 ID 的JSON响应。在下一个RPC调用中,我们希望使用这些 ID 做RPC调用来进行更新和删除操作,因此我们将其赋给了books_ids变量。
📝注:JSON-RPC和XML-RPC 在未对必填字段提供值而尝试创建记录时产生报错,因此确保在create的值中添加了所有必填字段。
在下一个RPC调用中,我们使用了write方法来更新已有记录。write方法接收两个位置参数:待更新的记录和要写入的值。在本例中,我们通过使用所创建图书 ID中的第二本书的 ID更新了书名。这会将第二本书的书名由Book 3修改为Book 2。
然后我们进行了删除两本图书记录的RPC调用。执行这一操作我们使用了unlink方法。unlink方法仅接收一个参数,我们希望删除的记录ID。这个RPC调用将删除最后两本图书。
扩展知识…
类似XML-RPC,可以在 JSON-RPC中使用check_access_rights来检测是否有执行这一操作的权限。这个方法要求有两个参数 – 模型名和操作名。在下例中,我们查看对library.book模型创建操作的权限:
1 2 3 4 5 6 7 8 9 10 |
# 放置authentication和get_json_payload方法(参见上一节获取更多信息) if user_id: payload = get_json_payload("object", "execute_kw", db_name, user_id, password, 'library.book', 'check_access_rights', ['create']) res = requests.post(json_endpoint, data=payload, headers=headers).json() print("Has create access:", res['result']) else: print("Failed: wrong credentials") |
这个程序的输出如下:
在通过RPC执行复杂的操作时,可在执行操作前使用check_access_rights方法来确保拥有相应的权限。
通过JSON-RPC调用方法
本节中,我们将学习如何通过JSON-RPC调用模型中的自定义方法。我们将调用make_available()方法修改图书的状态。
准备工作
我们将创建Python程序来调用library.book中的make_available方法。确保已安装my_library模块并且服务运行于http://localhost:8069。
如何实现…
执行如下步骤来通过RPC创建、写入及更新图书的信息:
- 添加jsonrpc_method.py文件。可以把这个文件放在任意位置,因为RPC程序独立运行。
- 在文件中添加如下代码:
1234567891011121314151617181920212223242526# 放置authentication和get_json_payload方法(参见最后一节获取更多信息)if user_id:# 创建draft状态的图书payload = get_json_payload("object", "execute_kw",db_name, user_id, password,'library.book', 'create', [{'name': 'Book 1', 'state': 'draft'}])res = requests.post(json_endpoint, data=payload,headers=headers).json()print("Book created with id:", res['result'])book_id = res['result']# 通过调用make_available方法来修改图书状态payload = get_json_payload("object", "execute_kw",db_name, user_id, password,'library.book', 'make_available', [book_id])res = requests.post(json_endpoint, data=payload, headers=headers).json()# 在调用方法后查看图书状态payload = get_json_payload("object", "execute_kw",db_name, user_id, password,'library.book', 'read', [book_id, ['name', 'state']])res = requests.post(json_endpoint, data=payload,headers=headers).json()print("Book state after the method call:", res['result'])else:print("Failed: wrong credentials") - 使用如下命令在终端中运行Python脚本:
1python3 jsonrpc_method.py
以上命令会创建一本状态为draft的图书,然后我们通过调用make_available方法修改图书的状态。此后,我们获取图书数据来检查图书的状态,输出如下:
本节中的程序会新建一条图书记录,然后通过调用模型方法修改其状态。在程序的最后,我们读取了该图书记录并打印了更新后的状态。
运行原理…
execute_kw()能够调用模型中的任意公有方法。如我们在通过XML-RPC调用方法一节中所看到的,公有方法是那些方法名不以 _ (下划线)开头的方法。以 _ 开头的为私有方法,不可通过JSON-RPC进行调用。
本例中,我们创建了状态为draft的图书。然后进行了RPC调用来调用make_available方法,它修改图书的状态为available。最后,我们再次使用RPC调用来查看图书的状态。这会显示图书的状态已修改为available,参见图20.10。
内部没有返回任何内容的方法默认返回None。这种方法无法通过RPC使用。如果希望在RPC中使用自己的方法,至少应添加return True语句。
OCA odoorpc库
Odoo社区联盟(OCA)提供名为odoorpc的Python库。可通过https://github.com/OCA/odoorpc进行获取。odoorpc库提供一种用户友好的语法,使用它可以通过RPC访问Odoo数据。它提供一种类似于后端服务的语法。在本节中,我们将学习如何使用odoorpc库来通过RPC执行操作。
准备工作
odoorpc库在Python包(PyPI)索引中有登记。使用这个库我们需要通过如下命令来安装它。如果需要可在单独的虚拟环境中使用它:
1 |
pip install OdooRPC |
本节中,我们将使用odoorpc库来进行一些基本操作。我们使用library.book模型来执行这些操作。确保已安装my_library模型并且服务运行于http://localhost:8069。
如何实现…
执行如下步骤来通过RPC创建、写入并更新图书的信息:
- 添加odoorpc_library.py文件。可以将文件放在任意位置,因为RPC程序独立运行。
- 在该文件中添加如下代码:
1234567891011121314151617181920212223242526272829import odoorpcdb_name = 'odoo-test'user_name = 'admin'password = 'admin'# 准备对服务端的连接odoo = odoorpc.ODOO('localhost', port=8069)odoo.login(db_name, user_name, password) # login# 用户信息user = odoo.env.userprint(user.name) # 连接的用户print(user.company_id.name) # 用户的公司名print(user.email) # 用户的emailBookModel = odoo.env['library.book']search_domain = ['|', ['name', 'ilike', 'odoo'], ['name', 'ilike', 'sql']]books_ids = BookModel.search(search_domain, limit=5)for book in BookModel.browse(books_ids):print(book.name, book.date_release)# 创建图书并更新状态book_id = BookModel.create({'name': 'Test book', 'state': 'draft'})book = BookModel.browse(book_id)print("Book state before make_available:", book.state)book.make_available()book = BookModel.browse(book_id)print("Book state after make_available:", book.state) - 使用如下命令来终端中运行这个Python脚本:
1python3 odoorpc_library.py
该程序会进行验证、打印用户信息并在library.book 模型中执行操作。它会生成如下输出:
以上输出中多次RPC调用后的结果。我们获取了用户信息、部分图书信息并且修改了图书的状态。
运行原理…
在安装了odoorpc库之后,我们就可以直接使用它了。使用时需要导入odoorpc包,然后我们通过传递服务端URL和端口来创建ODOO类的对象。它会对服务端进行/version_info调用来检查连接。登录需要使用该对象的 login()方法。此处,我们需要传递数据库名、用户名和密码。
成功登录后,可以使用odoo.env.user访问用户信息。odoorpc提供一种用户友好的RPC版本,我们可以完全像服务端的记录集一样使用这个用户对象。在示例中,我们从用户对象中访问了姓名、邮箱和公司名。
如果想要访问模型数据,可以使用odoo.env对象。我们可以调用模型中的任意模型方法。底层odoorpc库使用jsonrpc,因此我们无法调用以 _ 开头的私有模型方法。在本例中,我们从仓库中访问了library.book模型。然后,我们调用了带有domain和limit参数的search方法。它会返回图书的ID。通过对browse()方法传递图书ID,我们可以生成一个library.book模型记录集。
在程序的结束处,我们新建了一本图书并通过调用make_available()方法修改了图书的状态。如果仔细查看程序的语法,会看到它使用了和服务端相同的语法。
扩展知识…
虽然它像服务端一样提供了用户友好的语法,但你可以用普通RPC同样的语法使用这个库。我们需要使用带有模型名、方法名和参数的odoo.execute方法来进行实现。以下是在原生RPC语法中读取图书信息的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import odoorpc db_name = 'odoo-test' user_name = 'admin' password = 'admin' # 准备对服务端的连接 odoo = odoorpc.ODOO('localhost', port=8069) odoo.login(db_name, user_name, password) # login books_info = odoo.execute('library.book', 'search_read', [['name', 'ilike', 'odoo']], ['name', 'date_release']) print(books_info) |
其它内容
有一些针对Odoo的RPC库的其它实现,如下:
- https://github.com/akretion/ooor
- https://github.com/OCA/odoorpc
- https://github.com/odoo/openerp-client-lib
- http://pythonhosted.org/OdooRPC
- https://github.com/abhishek-jaiswal/php-openerp-lib
生成API密钥
Odoo v14内置了双因素认证(2FA) 的功能。2FA是对用户账户附加的安全层,用户需要输入密码及和时间相关的验证码。如果启用了2FA,就不能通过用户ID和密码来使用RPC了。解决这一问题,需要为用户生成API密钥。本节中,我们学习如何生成API密钥。
如何实现…
执行如下步骤生成用于RPC的API密钥:
- 打开用户首选项,然后进入Account Security 标签栏。
- 点击New API Key按钮:
图20.12 – 重新新的API密钥 - 这时会打开下图这样的弹窗(正常会要求先输入密码进行确认)。输入API密钥名并点击Generate key按钮:
图20.13 –为密钥命名 - 这时便会生成API密钥并在一个新弹窗中显示。记录下这个API密钥,因为会在之后使用:
图20.14 – 记录所生成的API密钥
密钥生成后,可以像普通密码那样在RPC中使用这个API密钥。
运行原理…
API的使用非常直接。但有些需要注意的事项。API密钥按用户生成,如果想要对多个用户使用RPC,就需要为每个用户生成一个API密钥。此外,用户的API密钥和该用户具有相同的访问权限,如果有人获取到了这个密钥,就可以执行该用户所能执行的所有操作。因此应该保存好API密钥。
📝注:在生成API密钥时,仅会显示一次。需要记录下这个密钥。如果遗失密钥,就无法再次获取到。这时需要删除掉原API密钥,重新生成一个。
API密钥的使用非常简单。在进行RPC调用时,只需要使用API密钥替换掉用户的密码。此时即使激活了2FA也可以调用RPC了。