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
相关产品推荐
相关产品推荐

