From e522a73d1f03729414a2b87989786ac5141ed147 Mon Sep 17 00:00:00 2001 From: Eric Antones Date: Wed, 15 Jul 2026 19:26:37 +0200 Subject: [PATCH] [FIX] base: cascade inactive child views on force unlink View.unlink() emulates an ondelete cascade on inherit_children_ids when _force_unlink is set, but reads that One2many with active_test enabled, so INACTIVE child views are skipped. They still hold the inherit_id RESTRICT foreign key, so the parent's DELETE is rejected, logged as a benign bad query, and the parent view is left orphaned. This is not migration-specific: _force_unlink is set on every module update/install (ir.model.data._process_end, called from odoo/modules/loading.py) and on every module uninstall (ir.model.data._module_data_uninstall). A later -u does not sweep the orphan either, as it re-runs the same cascade and re-skips the same child. Reproduced upgrading 16.0 -> 17.0: website_sale.payment_footer is dropped from core in 17.0 but survives, because its child payment_sale_note is active="False" and is not cascaded. Read inherit_children_ids with active_test=False so the cascade covers inactive children too. --- odoo/addons/base/models/ir_ui_view.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/odoo/addons/base/models/ir_ui_view.py b/odoo/addons/base/models/ir_ui_view.py index 01018f56ed3a91..1d6000ac075371 100644 --- a/odoo/addons/base/models/ir_ui_view.py +++ b/odoo/addons/base/models/ir_ui_view.py @@ -570,9 +570,13 @@ def write(self, vals): return res def unlink(self): - # if in uninstall mode and has children views, emulate an ondelete cascade - if self.env.context.get('_force_unlink', False) and self.inherit_children_ids: - self.inherit_children_ids.unlink() + # if in uninstall mode and has children views, emulate an ondelete cascade; + # inactive children are hidden by active_test but still hold the inherit_id + # RESTRICT foreign key, which would leave this view orphaned + if self.env.context.get('_force_unlink', False): + children = self.with_context(active_test=False).inherit_children_ids + if children: + children.unlink() self.env.registry.clear_cache('templates') return super(View, self).unlink()