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

为何Odoo无法读取为模型添加的额外字段?

解决Odoo读取自定义字段(绕过x_前缀/state限制)的问题

核心原因

Odoo启动时优先加载Python代码定义的字段,同时仅会读取ir.model.fields中state为base(原生字段)或manual(模块安装字段)的记录,custom状态的字段会被默认忽略。你在Technical->Models里看到字段,只是数据库记录存在,但Odoo运行时的模型对象并未加载这些字段。

可行解决方案

1. 通过模块Data文件定义字段(推荐)

这是最合规的方式,完全无需“绕过”限制:直接在模块的XML数据文件里定义字段,可自定义字段名(无需x_前缀),并将state设为manual,Odoo会自动加载这些字段到模型中。

示例XML代码:

<odoo>
    <record id="field_res_partner_my_custom_field" model="ir.model.fields">
        <field name="name">my_custom_field</field> <!-- 自定义字段名,无x_前缀 -->
        <field name="model">res.partner</field> <!-- 目标模型 -->
        <field name="field_description">我的自定义字段</field>
        <field name="ttype">char</field> <!-- 字段类型,如char、integer等 -->
        <field name="state">manual</field> <!-- 关键:设为manual,Odoo会加载 -->
        <field name="required" eval="False"/>
        <field name="readonly" eval="False"/>
    </record>
</odoo>

将该XML文件添加到模块__manifest__.py的data列表中,安装/升级模块后,字段会被正常加载,视图中直接引用即可。

2. 动态注入字段到模型

如果必须通过数据库直接创建字段(而非Data文件),可以在模块加载时,从ir.model.fields读取目标字段,动态注入到模型的_fields字典中。

示例Python代码(可放在模块的models/__init__.py中):

from odoo import models, fields
from odoo.modules.registry import Registry

def inject_custom_fields():
    registry = Registry.get('base')
    with registry.cursor() as cr:
        # 查询目标模型的非custom状态字段(根据实际情况调整条件)
        cr.execute("""
            SELECT name, ttype, field_description, required, readonly, default
            FROM ir_model_fields
            WHERE model = 'res.partner' AND state != 'custom'
        """)
        field_records = cr.fetchall()
        Model = registry['res.partner']
        
        for name, ttype, desc, required, readonly, default_val in field_records:
            if name not in Model._fields:
                # 根据字段类型创建对应的字段对象
                field_class = getattr(fields, ttype.capitalize())
                field = field_class(
                    string=desc,
                    required=required,
                    readonly=readonly,
                    default=default_val
                )
                # 将字段添加到模型的_fields字典
                Model._fields[name] = field
                # 清除字段缓存,确保Odoo能识别新字段
                Model._fields_get_cache.clear()

# 在模块加载时执行注入逻辑
inject_custom_fields()

注意:需确保在Odoo模型加载完成后执行,且要适配不同模型的需求。

3. 仅解决视图显示问题(临时方案)

如果只是需要在视图中展示字段,不涉及Python业务逻辑调用,可以重写模型的fields_view_get方法,动态将字段添加到视图的字段定义中:

class ResPartner(models.Model):
    _inherit = 'res.partner'

    def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
        res = super().fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
        # 查询需要显示的自定义字段
        custom_fields = self.env['ir.model.fields'].search([
            ('model', '=', self._name),
            ('state', '=', 'custom')  # 根据你的字段状态调整
        ])
        for field in custom_fields:
            if field.name not in res['fields']:
                res['fields'][field.name] = {
                    'string': field.field_description,
                    'type': field.ttype,
                    'required': field.required,
                    'readonly': field.readonly,
                    'default': field.default
                }
        return res

此方案仅能让视图渲染字段,无法在Python代码中通过record.name访问该字段,仅适合临时展示需求。

4. 修改Odoo核心加载逻辑(不推荐)

如果一定要保留custom状态的字段并让Odoo加载,可以修改Odoo核心代码odoo/models.py中的_load_fields方法,移除对state != 'custom'的判断。但这种方式会影响全局,且Odoo升级后会被覆盖,不建议使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 23:25:23