Skip to content

Commit 3d68a7a

Browse files
pauloamedKangOl
andcommitted
[IMP] util/records: enforce cascade removal for actions
The implementation of the python inheritance mechanism between the base class `ir.actions.actions` and its child classes (eg. `ir.actions.act_window`) does not allow the creation of foreign keys when `ir.actions.actions` is a M2O field of another model; what leads to the not execution of some constraints, one of them being the `ondelete='cascade'` constraint, which is set in PSQL level. That said, when a `ir.actions.actions` record is deleted, if it is being referenced as a M2O field by another model (eg. `ir.filters`), records from this second model won't be affected, what leads to undesired behaviour: a MissingError in the UI, indicating that the action was deleted. Such behaviour of not creating foreign keys and thus constraints is specific to `ir.actions.actions`. This commit remedies this specific case, removing records with a M2O field to `ir.actions.actions` with `ondelete=cascade` when the action referenced in such field is removed using `upgrade-util`. Co-authored-by: Christophe Simonis <[email protected]>
1 parent 3787631 commit 3d68a7a

File tree

1 file changed

+68
-1
lines changed

1 file changed

+68
-1
lines changed

src/util/records.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
_get_unique_indexes_with,
3333
_validate_table,
3434
column_exists,
35+
column_nullable,
3536
column_type,
3637
column_updatable,
3738
explode_execute,
@@ -310,6 +311,69 @@ def remove_asset(cr, name):
310311
# fmt:on
311312

312313

314+
def remove_action(cr, xml_id=None, action_id=None):
315+
assert bool(xml_id) ^ bool(action_id)
316+
317+
if xml_id:
318+
action_id = ref(cr, xml_id)
319+
320+
action_model = None
321+
if action_id:
322+
module, _, name = xml_id.partition(".")
323+
cr.execute("SELECT model FROM ir_model_data WHERE module=%s AND name=%s", [module, name])
324+
[action_model] = cr.fetchone()
325+
if not action_model in [ihn.model for ihn in for_each_inherit(cr, 'ir.actions.actions')]:
326+
raise ValueError(
327+
"%r should point to a model inheriting from 'ir.actions.actions', not a %r" % (xml_id, action_model)
328+
)
329+
330+
if version_gte("11.0"):
331+
cr.execute(
332+
"""
333+
SELECT name,
334+
model,
335+
on_delete
336+
FROM ir_model_fields
337+
WHERE relation = 'ir.actions.actions'
338+
AND on_delete IN ( 'cascade', 'set null' )
339+
AND ttype = 'many2one'
340+
"""
341+
)
342+
343+
for column_name, model, on_delete in cr.fetchall():
344+
model_table = table_of_model(cr, model)
345+
if on_delete == 'cascade':
346+
query = format_query(cr, "DELETE FROM {} WHERE {} = %s", model_table, column_name)
347+
else:
348+
query = format_query(cr, "UPDATE {} SET {} = NULL WHERE {} = %s", model_table, column_name, column_name)
349+
cr.execute(query, (action_id,))
350+
else:
351+
# Before V11, we did not have `on_delete` on `ir_model_fields`.
352+
# However, we can infer the intended behaviour by checking if the column containing the deleted action's id is
353+
# nullable or not:
354+
# if not nullable, the record containing the deleted action should also be deleted (cascade);
355+
# else, we just set the column to NULL (set null)
356+
cr.execute(
357+
"""
358+
SELECT name,
359+
model
360+
FROM ir_model_fields
361+
WHERE relation = 'ir.actions.actions'
362+
AND ttype = 'many2one'
363+
"""
364+
)
365+
366+
for column_name, model in cr.fetchall():
367+
model_table = table_of_model(cr, model)
368+
if not column_nullable(cr, model_of_table, column_name):
369+
query = format_query(cr, "DELETE FROM {} WHERE {} = %s", model_table, column_name)
370+
else:
371+
query = format_query(cr, "UPDATE {} SET {} = NULL WHERE {} = %s", model_table, column_name, column_name)
372+
cr.execute(query, (action_id,))
373+
374+
remove_records(cr, action_model, [action_id])
375+
376+
313377
def remove_record(cr, name):
314378
"""
315379
Remove a record and its references corresponding to the given :term:`xml_id <external identifier>`.
@@ -351,10 +415,13 @@ def remove_record(cr, name):
351415
if model == "res.groups":
352416
_logger.log(NEARLYWARN, "Removing group %r", name)
353417
return remove_group(cr, group_id=res_id)
418+
419+
if model in [ihn.model for ihn in for_each_inherit(cr, 'ir.actions.actions')]:
420+
_logger.log(NEARLYWARN, "Removing action %r", name)
421+
return remove_action(cr, action_id=res_id)
354422

355423
return remove_records(cr, model, [res_id])
356424

357-
358425
def remove_records(cr, model, ids):
359426
if not ids:
360427
return

0 commit comments

Comments
 (0)