Odoo模块关联现有PostgreSQL表时视图字段报错求助
Odoo关联现有PostgreSQL表时视图字段不存在问题解决
问题背景
开发Odoo模块测试CRUD功能,尝试关联已创建的两个PostgreSQL表。最初使用数据库视图仅能读取数据,无法执行增删改操作,因此改为直接使用真实表,但配置后启动服务器报错。
现有代码
models.py
from odoo import models, fields class PcClientes(models.Model): _name = 'clientes.pc_clientes' _description = 'Clientes PC' _table = 'pc_clientes' # Nombre de la tabla en la base de datos _auto = False # No queremos que Odoo cree la tabla automáticamente, ya existe # Campos de la tabla pc_clientes xempgen_id = fields.Char(string='EmpGen ID', required=True) xcliente_id = fields.Char(string='Cliente ID', required=True) xcod_postal = fields.Char(string='Código Postal') xdomicilio = fields.Char(string='Domicilio') xnif = fields.Char(string='NIF') xnif_ue = fields.Char(string='NIF UE') xnomabrev = fields.Char(string='Nombre Abreviado') xnombre = fields.Char(string='Nombre', required=True) xpais_id = fields.Char(string='ID País') xper_fiscal = fields.Char(string='Período Fiscal') xpoblacion = fields.Char(string='Población') xprovincia_id = fields.Char(string='ID Provincia') xlatitud = fields.Float(string='Latitud') xlongitud = fields.Float(string='Longitud') trial723 = fields.Char(string='Trial723') # Relación con la tabla pl_clientes pl_cliente_id = fields.Many2one( 'clientes.pl_clientes', string="PL Cliente", ondelete='cascade', required=True ) class PlClientes(models.Model): _name = 'clientes.pl_clientes' _description = 'Clientes PL' _table = 'pl_clientes' # Nombre de la tabla en la base de datos _auto = False # No queremos que Odoo cree la tabla automáticamente, ya existe # Campos de la tabla pl_clientes xcliente_id = fields.Char(string='Cliente ID', required=True) xcod_docto = fields.Char(string='Código Documento') xcopias_alb = fields.Integer(string='Copias Albarán') xcopias_fact = fields.Integer(string='Copias Factura') xmodalidad_id = fields.Char(string='ID Modalidad') xpor_comision = fields.Float(string='Porcentaje Comisión') xreferencia = fields.Char(string='Referencia') xrepresentante_id = fields.Char(string='ID Representante') xsecpref_id = fields.Char(string='ID SecPref') xtipo_fact = fields.Integer(string='Tipo de Factura', required=True) xempgen_id = fields.Char(string='EmpGen ID', required=True) xempresa_id = fields.Char(string='Empresa ID', required=True) xfax = fields.Char(string='Fax') xtelefono = fields.Char(string='Teléfono') xemail = fields.Char(string='Email') xfecha_alta = fields.Datetime(string='Fecha Alta') xfecha_baja = fields.Datetime(string='Fecha Baja') xinternet = fields.Char(string='Internet') xlangage = fields.Integer(string='Lenguaje') xlangage_remplace = fields.Integer(string='Lenguaje Reemplazado') xcopias_ped = fields.Integer(string='Copias Pedido') xcredito = fields.Float(string='Crédito') xctacon = fields.Char(string='Cta Con') xctamayor = fields.Char(string='Cta Mayor') xcyc_activo = fields.Integer(string='Cyc Activo') xcyc_poliza = fields.Char(string='Cyc Póliza') xdivisa_id = fields.Char(string='ID Divisa') xdto_com = fields.Float(string='Dto. Comercial') xcoment_albaran = fields.Char(string='Comentario Albarán') xcoment_factura = fields.Char(string='Comentario Factura') xcoment_pedido = fields.Char(string='Comentario Pedido') xcontacto = fields.Char(string='Contacto') xagrupa_alb = fields.Integer(string='Agrupa Albarán') xalb_valorado = fields.Integer(string='Albarán Valorador') xalta_modif = fields.Integer(string='Alta Modificada') xcalificacion_id = fields.Char(string='ID Calificación') xcargo = fields.Char(string='Cargo') xrecibe_facturae = fields.Integer(string='Recibe Facturae') xxjuego_id = fields.Char(string='ID Juego') p856_tipo_tarifa = fields.Char(string='Tipo Tarifa') p856_dto_metraje = fields.Float(string='Dto. Metraje') p856_dto_tarifa = fields.Float(string='Dto. Tarifa') p856_agen_com_id = fields.Char(string='Agente Com') p856_eti_metr_ext = fields.Char(string='Etiqueta Metr Ext') p856_observaciones = fields.Char(string='Observaciones') p856_cadena = fields.Char(string='Cadena') p856_credito_aseg = fields.Float(string='Crédito Asegurado') p856_credito_rioma = fields.Float(string='Crédito Rioma') p856_crm_id = fields.Char(string='ID CRM') p856_modificado = fields.Integer(string='Modificado') p856_modificado_r = fields.Integer(string='Modificado R') p856_dto_met_max = fields.Float(string='Dto. Metraje Max') p856_dto_pie_max = fields.Float(string='Dto. Pie Max') p856_entite = fields.Char(string='Entidad') p856_pass_crm = fields.Char(string='Pass CRM') p856_cerrado = fields.Integer(string='Cerrado') p856_supervisor = fields.Char(string='Supervisor') p856_tipo_cliente = fields.Integer(string='Tipo Cliente') p856_fact_ftp = fields.Integer(string='Fact. FTP') p856_backoffice_id = fields.Char(string='ID Backoffice') trial726 = fields.Char(string='Trial 726') # Relación con la tabla pc_clientes pc_cliente_id = fields.Many2one( 'clientes.pc_clientes', string="PC Cliente", ondelete='cascade', required=True )
视图代码(templates.xml)
<odoo> <data> <record id="clientes_list_view" model="ir.ui.view"> <field name="name">clientes.list.view</field> <field name="model">clientes.pc_clientes</field> <field name="arch" type="xml"> <list> <field name="xempgen_id"/> <field name="xcliente_id"/> <field name="xnomabrev"/> <field name="xnombre"/> <field name="pl_cliente_id.xfax"/> </list> </field> </record> <record id="clientes_form_view" model="ir.ui.view"> <field name="name">clientes.form.view</field> <field name="model">clientes.pc_clientes</field> <field name="arch" type="xml"> <form> <group name="Datos indicativos" string="Datos indicativos" class="central"> <group> <field name="xnombre" class="field"/> </group> <group> <field name="xnomabrev" class="field"/> </group> <group> <field name="xreferencia" class="field"/> </group> <field name="pl_cliente_id.xfax" class="field"/> <group/> </group> </form> </field> </record> <record id="clientes_action" model="ir.actions.act_window"> <field name="name">Clientes</field> <field name="res_model">clientes.pc_clientes</field> <field name="view_mode">list,form</field> </record> <menuitem id="menu_root" name="Rioma"/> <menuitem id="menu_clientes" name="Clientes" parent="menu_root"/> <menuitem id="menu_clientes_list" name="Clientes List" parent="menu_clientes" action="clientes_action"/> </data> </odoo>
报错信息
Error while validating view near: <field name="xnombre"/> <field name="pl_cliente_id.xfax"/> </list> Field "pl_cliente_id.xfax" does not exist in model "clientes.pc_clientes" View error context: {'file': 'c:\\odoo\\modules\\clientes\\views\\templates.xml', 'line': 7, 'name': 'clientes.list.view', 'view': ir.ui.view(878,), 'view.model': 'clientes.pc_clientes', 'view.parent': ir.ui.view(), 'xmlid': 'clientes_list_view'}
解决方案
1. 修正Many2one字段的关联配置
因为使用_auto=False,Odoo无法自动识别现有数据库表的外键关系,必须显式指定Many2one字段对应的数据库列:
- 假设
pc_clientes表中存储pl_clientes记录ID的字段名为pl_cliente_id,修改PcClientes类中的关联字段:pl_cliente_id = fields.Many2one( 'clientes.pl_clientes', string="PL Cliente", ondelete='cascade', required=True, column='pl_cliente_id' # 对应数据库表中的外键字段名 ) - 同理,
PlClientes类中的pc_cliente_id字段也要添加column参数,对应pl_clientes表中的外键字段名。
2. 优化视图中关联字段的显示方式
可以通过两种方式解决视图字段识别问题:
- 方式一:添加related字段
在PcClientes模型中添加一个关联字段,直接映射pl_clientes的xfax字段:
然后在视图中替换为:pl_cliente_fax = fields.Char(string="Fax PL Cliente", related='pl_cliente_id.xfax', store=False)<field name="pl_cliente_fax"/> - 方式二:确保关联字段配置正确后刷新模块
修正Many2one字段的column参数后,重启Odoo服务并升级模块,Odoo会重新识别字段关联关系,此时pl_cliente_id.xfax的写法即可生效。
3. 关键注意事项
- 确保PostgreSQL表
pc_clientes和pl_clientes中存在对应对方ID的字段,且字段名与模型中column参数指定的一致。 - 使用
_auto=False时,模型中定义的字段必须与数据库表的字段名完全匹配(注意PostgreSQL的大小写敏感设置)。 - 修改模型后必须重启Odoo并升级模块,否则元数据不会更新。
内容的提问来源于stack exchange,提问作者David Carmona Arrabal
相关产品推荐
相关产品推荐

