Skip to content

Commit 8322013

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 8322013

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

src/util/records.py

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

420+
if model in [ihn.model for ihn in for_each_inherit(cr, "ir.actions.actions")]:
421+
_logger.log(NEARLYWARN, "Removing action %r", name)
422+
return remove_action(cr, action_id=res_id)
423+
355424
return remove_records(cr, model, [res_id])
356425

357426

0 commit comments

Comments
 (0)