addons/web/controllers/json.py
使用方法:
- 系统参数配置:web.json.enabled:true
- 打开想要获取的数据页面,将链接odoo改为json即可
- 将更改后的链接作为api使用get方式应用到外部系统即可获取数据
- 认证方式:Bearer Token(也就是在odoo用户生成的密钥)
代码综合分析
该 Python 代码文档定义了一个 Odoo Web 控制器
`WebJsonController`,其主要功能是提供一个通过 JSON 格式访问 Odoo 视图数据的接口。这个接口旨在模拟 Odoo
Web
客户端的行为,允许外部系统以结构化数据的方式查询和获取模型记录。代码逻辑严谨,处理了参数规范化、权限检查、视图类型解析、数据查询(包括简单读取、搜索读取和分组读取)等多种情况。
函数描述
web_json(self, subpath, **kwargs)
目的: 处理 `/json/` 格式的 URL 请求。这是一个简化的入口点,它会构建一个包含版本号 `1` 的新 URL,并将所有参数附加到查询字符串中,然后通过 HTTP 307 临时重定向到 `web_json_1` 方法。
参数:
-
- `subpath` (str): URL 路径部分,用于定位 Odoo 动作 (action)。
- `**kwargs`: 任意关键字参数,会被传递到重定向后的 URL。
返回值: `werkzeug.wrappers.Response`,一个 HTTP 重定向响应对象。
web_json_1(self, subpath, **kwargs)
目的: 这是实现 JSON
数据接口的核心方法。它解析 `subpath` 以获取 Odoo 动作,并根据传入的参数(如 `view_type`, `domain`,
`limit`, `groupby`
等)来决定如何查询数据。它能够处理表单视图的单条记录读取、列表视图的多条记录搜索读取,以及透视表、图表等视图的分组读取。该方法还包含参数规范化逻辑,如果传入的参数不完整,会计算出"规范"的参数并重定向到包含这些参数的
URL。
参数:
-
- `subpath` (str): 用于定位 Odoo 动作的 URL 路径。
- `**kwargs`: 包含各种查询选项的关键字参数,例如:
- `view_type`: 视图类型(如 `form`, `list`, `kanban`, `pivot`)。
- `domain`: Odoo 搜索域字符串。
- `offset`: 搜索结果的偏移量。
- `limit`: 搜索结果的数量限制。
- `groupby`: 用于分组查询的字段名,以逗号分隔。
- `fields`: 用于分组聚合或指定查询的字段名,以逗号分隔。
- `start_date`, `end_date`: 用于日历等视图的日期范围过滤。
返回值: `odoo.http.Response`,一个包含查询结果的 JSON 响应,或是在参数不规范时返回一个 HTTP 重定向响应。
_check_json_route_active(self)
目的: 这是一个私有辅助方法,用于检查 `/json` 路由是否被启用。该路由被视为实验性功能,默认仅在演示(demo)模式下或通过系统参数 `web.json.enabled` 明确启用时才可用。
参数: 无。
返回值: 无。如果路由未激活,则会引发 `NotFound` (HTTP 404) 异常。
_get_action(self, subpath)
目的: 一个私有辅助方法,负责从
URL `subpath` 解析出对应的 Odoo 动作 (`ir.actions.act_window` 或
`ir_actions_server`)、上下文 (`context`) 和记录 ID。它特别处理了服务器动作
(`ir_actions_server`),会尝试在只读事务中安全地执行该动作以获取其返回的窗口动作数据。
参数:
-
- `subpath` (str): 来自 URL 的路径。
返回值: `tuple`,包含四个元素:动作记录 (`action`)、更新后的上下文 (`context`)、用于 `safe_eval` 的求值上下文 (`eval_context`) 和记录 ID (`record_id`)。
get_view_id_and_type(action, view_type)
目的: 一个辅助函数,根据给定的窗口动作和可选的视图类型,确定要使用的具体视图 ID 和视图类型。如果未提供 `view_type`,则使用动作中定义的第一个视图类型。
参数:
-
- `action`: 一个 `ir.actions.act_window` 记录。
- `view_type` (str | None): 请求的视图类型。
返回值: `tuple`,包含视图 ID(如果找到则为 `int`,否则为 `None`)和最终确定的视图类型(`str`)。
get_default_domain(model, action, context, eval_context)
目的:
一个辅助函数,用于获取模型的默认搜索域 (domain)。它会首先查找与模型和动作关联的、被设为默认的 `ir.filters`
记录。如果没有找到,它会解析上下文 (`context`) 中以 `search_default_`
开头的键,并从动作的搜索视图中找到对应的过滤器定义来构建域。
参数:
-
- `model`: Odoo 模型对象。
- `action`: 窗口动作记录。
- `context`: 当前上下文。
- `eval_context`: 用于 `safe_eval` 的求值上下文。
返回值: 一个 Odoo 域列表。
get_date_domain(start_date, end_date, view_tree)
目的: 一个辅助函数,为日历、甘特图等时间相关的视图生成日期过滤域。如果未提供起止日期,则默认生成当月的日期范围。日期字段名从视图的 XML 结构(`view_tree`)中提取。
参数:
-
- `start_date` (date | None): 开始日期。
- `end_date` (date | None): 结束日期。
- `view_tree`: 视图的 `lxml` 解析树。
返回值: 一个包含两个条件的 Odoo 域列表,用于筛选日期范围。
get_groupby(view_tree, groupby=None, fields=None)
目的: 一个辅助函数,用于解析
`groupby` 和 `fields` 参数。如果用户提供了这些参数,则直接使用。否则,对于 `pivot` 和 `graph`
视图,它会从视图的 XML 结构中提取默认的分组字段和度量字段。对于看板等视图,它会检查 `default_group_by` 属性。
参数:
-
- `view_tree`: 视图的 `lxml` 解析树。
- `groupby` (str | None): 用户提供的 `groupby` 字符串。
- `fields` (str | None): 用户提供的 `fields` 字符串。
返回值: `tuple`,包含一个 `groupby` 字段列表(如果需要分组)和一个 `fields` 列表。
Odoo 专有知识点
控制器与路由 (Controller & Routing)
-
- 代码使用 `odoo.http.Controller` 作为基类来创建 Web 控制器。
- `@http.route` 装饰器用于将方法绑定到特定的 URL 路由。参数
`auth='user'` 表示需要用户登录会话,而 `auth='bearer'` 表示支持通过 bearer token (如 API
密钥) 进行认证。`type='http'` 指定了请求类型。`readonly=True` 提示 Odoo 在只读事务中执行该方法。
ORM 方法
-
- `request.env['res.model']`: 通过 `request.env` 获取特定模型的 ORM 模型类。
- `model.get_view()`: 获取指定类型视图的结构(包括 `arch` XML)。
- `model.web_read(spec)`: 读取单条记录的字段,`spec` 定义了要读取的字段及其子字段。
- `model.web_search_read(domain, spec, ...)`: 根据 `domain` 搜索记录,并返回指定 `spec` 的字段,是 `search()` 和 `read()` 的高效组合。
- `model.web_read_group(domain, fields, groupby, ...)`: 核心的分组查询方法,用于透视表和图表数据,可以按 `groupby` 字段对记录进行分组并计算 `fields` 中指定的聚合值。
- `model.browse(id)`: 通过 ID 获取记录集。
- `model.with_context(context)`: 返回一个绑定了新上下文的、新的模型实例,后续操作将在该上下文中执行。
动作与视图 (Actions & Views)
-
- 代码深度依赖 `ir.actions.act_window` 来驱动数据查询,模拟了客户端点击菜单项或按钮的行为。
- 通过解析动作的 `view_mode` 和 `views` 字段来确定可用的视图和其优先级。
- 使用 `lxml.etree` 直接解析视图的 `arch` 字段 (XML),以提取元数据,如 `date_start`、`default_group_by`、分组字段和度量字段。
域与上下文 (Domain & Context)
-
- `domain`: Odoo 中用于过滤记录的核心机制,表示为一个由元组组成的列表。代码中通过 `expression.AND` 合并来自不同来源(动作、用户输入、默认过滤器)的多个域。
- `context`: 一个在不同操作间传递信息的 Python 字典。代码中利用它来查找 `search_default_` 过滤器。
- `odoo.tools.safe_eval`: Odoo 提供的安全求值函数,用于执行来自数据库或用户输入的字符串表达式(如 `domain` 和 `context`),以防止恶意代码注入。
权限与配置
-
- `user.has_group('base.group_allow_export')`: 检查当前用户是否属于"允许导出"权限组,这是访问此接口的必要条件。
- `request.env['ir.config_parameter'].sudo().get_param(...)`: 使用 `sudo()` 提升权限来读取系统配置参数,这里用于检查功能开关 `web.json.enabled`。
通用编程知识点
标准库与第三方库
-
- `ast`: 使用 `ast.literal_eval` 安全地解析来自用户输入的、表示 Python 字面量(如列表、字典)的字符串。这比 `eval` 安全得多。
- `collections.defaultdict`: 用于在解析视图中的字段时,根据字段类型(`row`, `col`, `measure`)方便地对字段名进行分类。
- `urllib.parse.urlencode`: 用于构建 URL 的查询字符串,并使用 `safe` 参数防止对特定字符进行编码。
- `dateutil.relativedelta`: 一个强大的库,用于进行相对日期计算,如获取一个月前或一个月后的日期。
- `lxml`: 高性能的 XML 处理库,用于解析 Odoo 视图的 `arch` XML 结构。
- `werkzeug`: WSGI 工具库,代码中使用了其异常类 `BadRequest` 和 `NotFound` 来生成标准的 HTTP 400 和 404 错误响应。
Python 语言特性
-
- 赋值表达式 (Walrus Operator `:=`): 在 `if redirect := check_redirect():` 中使用,使得代码更简洁,可以在判断条件的同时进行赋值。
- F-strings: 用于格式化字符串,例如 `f'/json/1/{subpath}?{encoded_kwargs}'`,可读性强且高效。
- 异常处理: 精细地捕获特定类型的异常(如 `ValueError`, `KeyError`, `psycopg2.errors.ReadOnlySqlTransaction`),并将其转换为对用户更友好的 `BadRequest` 或 `AccessError`。
- 生成器与 `yield from`: 在 `get_action` 内部的 `get_action_triples` 函数中,使用 `yield from` 将另一个生成器的所有值委托出去,简化了代码结构。
- 类型提示 (Type Hinting): 如 `view_type: str | None`,增强了代码的可读性和可维护性,便于静态分析工具检查。
- 解包操作符 (`*`): 在 `*field_by_type.get('row', ())` 中使用,用于将列表或元组的元素解包并合并到新列表中。
设计模式与概念
-
- 控制器模式 (Controller Pattern): 代码遵循了 MVC(模型-视图-控制器)架构中的控制器角色,负责接收 HTTP 请求、调用模型层进行业务处理,并返回响应。
- 辅助函数封装: 将独立的逻辑(如获取视图、获取域、解析 groupby)封装到独立的辅助函数中,提高了代码的模块化和复用性。
- 规范化 URL 重定向: 当检测到请求参数不完整或可以被系统自动推断时,会计算出一套完整的"规范"参数,并重定向到带有这些参数的 URL。这有助于 API 的使用者发现所有可用参数,并确保 URL 的唯一性和可缓存性。
- 只读事务: 在处理可能存在风险的服务器动作时,强制在只读数据库事务中运行,防止任何意外的数据修改,增强了系统的安全性。