跳转到主要内容

借助此插件,您可以开发Odoo的高级REST API。

项目描述

Beta License: LGPL-3 OCA/rest-framework Translate me on Weblate Try me on Runboat

此插件已被弃用,并且不再在Odoo 16上提供全面支持。请迁移到FastAPI迁移模块。请参阅 https://github.com/OCA/rest-framework/pull/291

此插件为开发Odoo的高级REST API提供基础。

随着Odoo成为企业IT系统的核心组成部分之一,经常需要设置专用服务接口,以便现有系统可以与Odoo交互。

虽然Odoo的XML-RPC接口在这种情况下很有用,但它需要对Odoo的内部数据模型有深入的了解。当广泛使用时,它会在Odoo内部和客户端系统之间创建强耦合,从而增加维护成本。

一旦开发完成,将从源代码生成一个OpenApi文档,并通过您的odoo服务器在 https://my_odoo_server/api-docs 提供的 Swagger UI 提供访问。

目录

配置

如果在调用服务的方法时发生错误(例如缺少参数等),则系统仅返回问题的通用描述而不会提供详细信息。这是故意为之,以确保最大程度地透明实施细节,从而降低安全问题。

当通过开发中的外部系统访问服务时,这种限制可能会带来问题。要知道错误的详细信息,确实需要访问服务器的日志。并不总是可以提供这种访问。这就是为什么您可以配置服务器以在开发模式下运行这些服务。

要在开发模式下运行REST API,您必须在服务器配置文件中添加一个新的部分“[base_rest]”,并包含选项“dev_mode=True”。

[base_rest]
dev_mode=True

当REST API在开发模式下运行时,在出现错误的情况下将返回原始描述和堆栈跟踪。**请务必不要在生产环境中使用此模式**。

用法

要添加自己的REST服务,您必须至少提供2个类。

  • 一个提供您服务业务逻辑的组件,

  • 一个用于注册您服务的控制器。

您的服务的业务逻辑必须实现到一个继承自(odoo.addons.component.core.Component)的组件中。

最初,base_rest默认公开所有服务中定义的公共方法。通过HTTP访问方法的约定如下

  • 如果定义了方法 def get(self, _id),则可通过HTTP GET路由 <string:_service_name>/<int:_id><string:_service_name>/<int:_id>/get 访问。

  • 如果定义了方法 def search(self, **params),则可通过HTTP GET路由 <string:_service_name>/<string:_service_name>/search 访问。

  • 如果定义了方法 def delete(self, _id),则可通过HTTP DELETE路由 <string:_service_name>/<int:_id> 访问。

  • 如果定义了 def update(self, _id, **params) 方法,则可通过HTTP PUT路由 <string:_service_name>/<int:_id> 访问。

  • 其他方法仅可通过HTTP POST路由 <string:_service_name><string:_service_name>/<string:method_name><string:_service_name>/<int:_id><string:_service_name>/<int:_id>/<string:method_name> 访问。

from odoo.addons.component.core import Component


class PingService(Component):
    _inherit = 'base.rest.service'
    _name = 'ping.service'
    _usage = 'ping'
    _collection = 'my_module.services'


    # The following method are 'public' and can be called from the controller.
    def get(self, _id, message):
        return {
            'response': 'Get called with message ' + message}

    def search(self, message):
        return {
            'response': 'Search called search with message ' + message}

    def update(self, _id, message):
        return {'response': 'PUT called with message ' + message}

    # pylint:disable=method-required-super
    def create(self, **params):
        return {'response': 'POST called with message ' + params['message']}

    def delete(self, _id):
        return {'response': 'DELETE called with id %s ' % _id}

    # Validator
    def _validator_search(self):
        return {'message': {'type': 'string'}}

    # Validator
    def _validator_get(self):
        # no parameters by default
        return {}

    def _validator_update(self):
        return {'message': {'type': 'string'}}

    def _validator_create(self):
        return {'message': {'type': 'string'}}

一旦实现了您的服务(ping,…),就必须告诉Odoo如何访问这些服务。这个过程通过实现一个继承自 odoo.addons.base_rest.controllers.main.RestController 的控制器来完成。

from odoo.addons.base_rest.controllers import main

class MyRestController(main.RestController):
    _root_path = '/my_services_api/'
    _collection_name = my_module.services

在您的控制器中,_‘root_path’ 用于指定访问您服务的路径根,而 ‘_collection_name’ 是提供请求服务业务逻辑的集合的名称。

通过继承 RestController,以下路由将被注册以访问您的服务

@route([
    ROOT_PATH + '<string:_service_name>',
    ROOT_PATH + '<string:_service_name>/search',
    ROOT_PATH + '<string:_service_name>/<int:_id>',
    ROOT_PATH + '<string:_service_name>/<int:_id>/get'
], methods=['GET'], auth="user", csrf=False)
def get(self, _service_name, _id=None, **params):
    method_name = 'get' if _id else 'search'
    return self._process_method(_service_name, method_name, _id, params)

@route([
    ROOT_PATH + '<string:_service_name>',
    ROOT_PATH + '<string:_service_name>/<string:method_name>',
    ROOT_PATH + '<string:_service_name>/<int:_id>',
    ROOT_PATH + '<string:_service_name>/<int:_id>/<string:method_name>'
], methods=['POST'], auth="user", csrf=False)
def modify(self, _service_name, _id=None, method_name=None, **params):
    if not method_name:
        method_name = 'update' if _id else 'create'
    if method_name == 'get':
        _logger.error("HTTP POST with method name 'get' is not allowed. "
                      "(service name: %s)", _service_name)
        raise BadRequest()
    return self._process_method(_service_name, method_name, _id, params)

@route([
    ROOT_PATH + '<string:_service_name>/<int:_id>',
], methods=['PUT'], auth="user", csrf=False)
def update(self, _service_name, _id, **params):
    return self._process_method(_service_name, 'update', _id, params)

@route([
    ROOT_PATH + '<string:_service_name>/<int:_id>',
], methods=['DELETE'], auth="user", csrf=False)
def delete(self, _service_name, _id):
    return self._process_method(_service_name, 'delete', _id)

结果是一个HTTP GET调用到‘http://my_odoo/my_services_api/ping’将被分配到方法 PingService.search

除了轻松公开您的函数外,该模块还允许您定义数据模式,交换的数据必须符合这些模式。这些模式基于 Cerberus schemas 定义,并使用以下命名约定与方法关联。对于一个方法 my_method

  • def _validator_my_method(self): 将被调用以获取验证输入参数所需的模式。

  • def _validator_return_my_method(self): 如果定义,将被调用以获取验证响应使用的模式。

为了提供更大的灵活性,已经开发了一个新的API。

这个新的API用Python装饰器显式标记方法作为通过REST API可用的服务,取代了之前隐式的方法:使用 odoo.addons.base_rest.restapi.method

class PartnerNewApiService(Component):
    _inherit = "base.rest.service"
    _name = "partner.new_api.service"
    _usage = "partner"
    _collection = "base.rest.demo.new_api.services"
    _description = """
        Partner New API Services
        Services developed with the new api provided by base_rest
    """

    @restapi.method(
        [(["/<int:id>/get", "/<int:id>"], "GET")],
        output_param=restapi.CerberusValidator("_get_partner_schema"),
        auth="public",
    )
    def get(self, _id):
        return {"name": self.env["res.partner"].browse(_id).name}

    def _get_partner_schema(self):
        return {
            "name": {"type": "string", "required": True}
        }

    @restapi.method(
        [(["/list", "/"], "GET")],
        output_param=restapi.CerberusListValidator("_get_partner_schema"),
        auth="public",
    )
    def list(self):
        partners = self.env["res.partner"].search([])
        return [{"name": p.name} for p in partners]

得益于这个新API,现在您可以自由指定自己的路由,并可以将其他对象类型用作方法的参数或响应。例如,base_rest_datamodel 允许您在服务中使用Datamodel对象实例。

from marshmallow import fields

from odoo.addons.base_rest import restapi
from odoo.addons.component.core import Component
from odoo.addons.datamodel.core import Datamodel


class PartnerSearchParam(Datamodel):
    _name = "partner.search.param"

    id = fields.Integer(required=False, allow_none=False)
    name = fields.String(required=False, allow_none=False)


class PartnerShortInfo(Datamodel):
    _name = "partner.short.info"

    id = fields.Integer(required=True, allow_none=False)
    name = fields.String(required=True, allow_none=False)


class PartnerNewApiService(Component):
    _inherit = "base.rest.service"
    _name = "partner.new_api.service"
    _usage = "partner"
    _collection = "base.rest.demo.new_api.services"
    _description = """
        Partner New API Services
        Services developed with the new api provided by base_rest
    """

    @restapi.method(
        [(["/", "/search"], "GET")],
        input_param=restapi.Datamodel("partner.search.param"),
        output_param=restapi.Datamodel("partner.short.info", is_list=True),
        auth="public",
    )
    def search(self, partner_search_param):
        """
        Search for partners
        :param partner_search_param: An instance of partner.search.param
        :return: List of partner.short.info
        """
        domain = []
        if partner_search_param.name:
            domain.append(("name", "like", partner_search_param.name))
        if partner_search_param.id:
            domain.append(("id", "=", partner_search_param.id))
        res = []
        PartnerShortInfo = self.env.datamodels["partner.short.info"]
        for p in self.env["res.partner"].search(domain):
            res.append(PartnerShortInfo(id=p.id, name=p.name))
        return res

BaseRestServiceContextProvider 为您的服务提供上下文,包括认证的合作伙伴ID。您可以根据选择的认证机制重新定义方法 _get_authenticated_partner_id() 以传递认证的合作伙伴ID。请参阅 base_rest_auth_jwt 以获取示例。

此外,认证的合作伙伴ID在记录规则评估上下文中可用。

已知问题/路线图

在GitHub上可以找到路线图已知问题

变更日志

16.0.1.0.2 (2023-10-07)

功能

  • 在Swagger UI中添加了对oauth2安全方案的兼容性。如果您的openapi规范包含类型为oauth2的安全方案,Swagger UI将在右上角显示一个登录按钮。为了完成登录过程,在初始化Swagger UI时必须提供重定向URL。现在Swagger UI通过一个 oauth2RedirectUrl 选项初始化,该选项引用由swagger-ui库提供的oauth2-redirect.html文件,并由当前插件提供服务。(#379

12.0.2.0.1

  • _validator_…() 方法现在可以返回一个cerberus Validator 对象,而不是schema字典,以提供额外的灵活性(例如,允许验证器选项如 allow_unknown)。

12.0.2.0.0

  • 许可证从AGPL-3更改为LGPL-3

12.0.1.0.1

  • 修复了在未提供REST API方法部分的文档时渲染jsonapi文档的问题。

12.0.1.0.0

首个官方版本。该插件已孵化到 Akretion 的 Shopinvader 仓库 中。有关更多信息,您需要查看git日志。

错误跟踪器

在GitHub上的 GitHub Issues 跟踪错误。如果遇到问题,请检查是否已报告您的问题。如果您是第一个发现它的人,请通过提供详细且受欢迎的 反馈 来帮助我们解决问题。

请不要直接联系贡献者以获取支持或技术问题的帮助。

鸣谢

作者

  • ACSONE SA/NV

贡献者

维护者

此模块由OCA维护。

Odoo Community Association

OCA,即Odoo社区协会,是一个非营利组织,其使命是支持Odoo功能的协作开发并推广其广泛使用。

此模块是GitHub上的OCA/rest-framework项目的一部分。

欢迎您贡献。要了解如何贡献,请访问 https://odoo-community.org/page/Contribute

项目详细信息


下载文件

下载适用于您的平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。

源代码分布

此版本没有可用的源代码分布文件。请参阅生成分布存档的教程

构建分布

odoo_addon_base_rest-16.0.1.0.2.2-py3-none-any.whl (5.3 MB 查看哈希值)

上传时间: Python 3

由以下组织支持

AWSAWS云计算和安全赞助商DatadogDatadog监控FastlyFastlyCDNGoogleGoogle下载分析MicrosoftMicrosoftPSF赞助商PingdomPingdom监控SentrySentry错误日志StatusPageStatusPage状态页面