You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Odoo控制器中如何获取当前res.partner对象的ID?

问题描述

我正在为Odoo开发自定义插件,在res.partner表单中新增页面以展示外部API数据。目前通过JavaScript调用控制器时会传递当前partner的ID,想了解是否可以直接在控制器中获取当前res.partner表单对应的对象ID?


现有代码如下:

views/my_view.xml

<data>
    <record id = "onglet_factu" model = "ir.ui.view">
        <field name = "name">La Factu</field>
        <field name = "model">res.partner</field>
        <field name = "inherit_id" ref = "base.view_partner_form"/>
        <field name = "arch" type = "xml">
            <xpath expr="//page[@name='internal_notes']" position='after'>
                <page string='Onglet API'>
                    <h2>Fake API</h2>
                    <field name="data" widget="my_widget"/>
                </page>
            </xpath>
        </field>
    </record>
</data>

models/api.py

from odoo import models, fields

class Api(models.Model):
    _inherit = 'res.partner'
    data = fields.Char()

static/src/js/api_component.js

export class MyApiField extends Component {
    setup() {
        super.setup();
        onWillStart(async () => {
            var id = this.env.model.root.data.id
            fetch('/napsis_factu/test?id='+id)
                .then(response => response.json())
                .then((data) => {
                    console.log(data);
                    this.state.data  = {
                        data1 : data['data1'],
                        data2: data['data2']
                    }
                })
                .catch((err) => {
                    console.log(err.message);
                });
        })

    }

}

MyApiField.template = "addon.template";
MyApiField.props = {
    ...standardFieldProps,
};
MyApiField.supportedTypes = ["char"];

export const apiField = { component: MyApiField };
registry.category("fields").add("my_text_field", apiField);

controllers/api_call.py

@http.route('/myaddon/api-call', auth='user', csrf=False )
def calling(self, **kwargs):
    theId = # How to get the ID of the res partner object I'm on ??
    payload = {   "Ctxt": "xxxxxxxxxxxxx",
                    "ID": theId,
                    }
    req = urllib.request.Request(url='https://myapi', headers={
        "Content-Type":"application/json"}, data = json.dumps(payload).encode())
    reply = json.loads(urllib.request.urlopen(req).read().decode('UTF-8'))
    if reply.get("error"):
     print('Alert!!')
     raise Exception(reply["error"])
    return json.dumps(reply) 

解答

核心结论

HTTP请求是无状态的,Odoo控制器无法直接感知用户当前操作的res.partner表单对象,必须通过显式传递ID的方式获取。不过可以优化现有实现,同时还有更贴合Odoo架构的方案可选:


1. 优化现有ID传递方案

当前通过URL参数传递ID的思路可行,需修正控制器代码正确获取ID,同时建议开启CSRF保护(关闭CSRF存在安全风险):

修正后的控制器代码

import json
import urllib.request
from odoo import http

class APIController(http.Controller):
    @http.route('/myaddon/api-call', auth='user', csrf=True )
    def calling(self, **kwargs):
        # 从URL参数中提取partner ID
        the_id = kwargs.get('id')
        if not the_id:
            return json.dumps({"error": "Partner ID缺失"})
        
        # 转换为Odoo要求的整数类型
        try:
            the_id = int(the_id)
        except ValueError:
            return json.dumps({"error": "无效的Partner ID"})
        
        payload = {
            "Ctxt": "xxxxxxxxxxxxx",
            "ID": the_id,
        }
        req = urllib.request.Request(
            url='https://myapi', 
            headers={"Content-Type":"application/json"}, 
            data=json.dumps(payload).encode()
        )
        try:
            reply = json.loads(urllib.request.urlopen(req).read().decode('UTF-8'))
            if reply.get("error"):
                return json.dumps({"error": reply["error"]})
            return json.dumps(reply)
        except Exception as e:
            return json.dumps({"error": str(e)})

修正JS中的CSRF传递

Odoo会在页面注入全局csrf_token,fetch时需带上该令牌:

fetch('/myaddon/api-call?id='+id, {
    headers: {
        'X-CSRF-Token': odoo.csrf_token
    }
})

2. 贴合Odoo架构的方案:使用计算字段

无需额外JS和控制器,直接在模型层实现API调用,通过计算字段展示数据,更符合Odoo的MVC模式:

修改models/api.py

from odoo import models, fields, api
import json
import urllib.request

class Api(models.Model):
    _inherit = 'res.partner'
    
    api_data = fields.Text(string="API数据", compute="_compute_api_data")
    
    @api.depends('id')
    def _compute_api_data(self):
        for record in self:
            if not record.id:
                record.api_data = ""
                continue
                
            payload = {
                "Ctxt": "xxxxxxxxxxxxx",
                "ID": record.id,
            }
            req = urllib.request.Request(
                url='https://myapi', 
                headers={"Content-Type":"application/json"}, 
                data=json.dumps(payload).encode()
            )
            try:
                reply = json.loads(urllib.request.urlopen(req).read().decode('UTF-8'))
                if reply.get("error"):
                    record.api_data = f"API错误: {reply['error']}"
                else:
                    # 格式化数据方便展示
                    record.api_data = json.dumps(reply, indent=2)
            except Exception as e:
                record.api_data = f"请求失败: {str(e)}"

修改视图(views/my_view.xml)

<page string='Onglet API'>
    <h2>Fake API</h2>
    <field name="api_data" widget="monospace"/>
</page>

这种方式的优势:

  • 无需额外JS代码,Odoo自动处理数据加载和渲染
  • 数据随表单自动刷新,无需手动调用API
  • 符合Odoo开发规范,维护成本更低

补充说明

Odoo控制器无法直接关联当前打开的表单,因为HTTP请求是独立的,没有上下文绑定到具体表单实例。即使使用session,也无法可靠区分用户同时打开的多个表单,因此显式传递ID是最可靠的方式。

内容的提问来源于stack exchange,提问作者Arthur Fortin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 07:45:56