From 1cdb03af78d344898953a1b508bc093c583aabf6 Mon Sep 17 00:00:00 2001 From: Eric Antones Date: Tue, 7 Jul 2026 11:07:25 +0200 Subject: [PATCH] [OU-FIX] openupgrade_framework: tolerate field type changes in _process_ondelete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The _process_ondelete patch already tolerates selection values whose MODEL vanished from the registry, but still crashes when the FIELD vanished or changed type between versions: AttributeError: 'Boolean' object has no attribute 'ondelete' This happens in _process_end when deleting orphaned ir.model.fields.selection records of fields that are no longer Selection fields on the target version — e.g. a module redefining a selection field as boolean, or reference-field selection rows that became orphans (ir.ui.menu.action, sale.report.order_reference, theme.ir.ui.view.inherit_id all surface this on a 16->17 upgrade). Skip those records: the ondelete machinery no longer applies to them, the unlink itself is what cleans them up. --- .../odoo/addons/base/models/ir_model.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py index 1185d8c0b027..9cffc58e59cb 100644 --- a/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py +++ b/openupgrade_framework/odoo_patch/odoo/addons/base/models/ir_model.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from openupgradelib import openupgrade -from odoo import api, models +from odoo import api, fields, models from odoo.addons.base.models.ir_model import ( IrModel, @@ -74,14 +74,22 @@ def _module_data_uninstall(self): def _process_ondelete(self): - """Don't break on missing models when deleting their selection fields""" + """Don't break on missing models, missing fields or fields that are no + longer Selection fields when deleting their selection values""" to_process = self.browse([]) for selection in self: try: - self.env[selection.field_id.model] # pylint: disable=pointless-statement - to_process += selection + model = self.env[selection.field_id.model] except KeyError: continue + field = model._fields.get(selection.field_id.name) + if field is None or not isinstance(field, fields.Selection): + # The field vanished or changed type between versions (e.g. + # selection -> boolean, or reference-field selection rows that + # became orphans): the ondelete machinery no longer applies, + # just let the record be unlinked. + continue + to_process += selection return IrModelSelection._process_ondelete._original_method(to_process)