Skip to content

Commit 2019fa8

Browse files
committed
[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`.
1 parent 3787631 commit 2019fa8

File tree

1 file changed

+50
-1
lines changed

1 file changed

+50
-1
lines changed

src/util/records.py

+50-1
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,52 @@ def remove_asset(cr, name):
309309
remove_view(cr, name, silent=True)
310310
# fmt:on
311311

312+
def get_models_inheriting_actions_actions(cr):
313+
# NOTE: it would be nice to have this cached since its static info
314+
# + opted for sql querying over hard-coding for better maintainability and handling custom models
315+
cr.execute(
316+
"""
317+
SELECT child_model.model
318+
FROM ir_model AS child_model
319+
JOIN ir_model_inherit
320+
ON child_model.id = ir_model_inherit.model_id
321+
JOIN ir_model AS base_model
322+
ON ir_model_inherit.parent_id = base_model.id
323+
WHERE base_model.model = 'ir.actions.actions'
324+
"""
325+
)
326+
return cr.fetchall()
327+
328+
def remove_action(cr, xml_id=None, action_id=None):
329+
assert bool(xml_id) ^ bool(action_id)
330+
331+
if xml_id:
332+
action_id = ref(cr, xml_id)
333+
334+
# NOTE: when comparing to `remove_group` it makes sense to unindent, since the id given by the user can be from
335+
# a wrong model (if check is done for xml_id, why not do it too for normal id?)
336+
if action_id:
337+
# NOTE: we are repeating code from `remove_group` and `remove_view`, maybe we could create a method
338+
# check_if_xml_is_from_models(xml_id, model_list)
339+
module, _, name = xml_id.partition(".")
340+
cr.execute("SELECT model FROM ir_model_data WHERE module=%s AND name=%s", [module, name])
341+
[model] = cr.fetchone()
342+
if not model in get_models_inheriting_actions_actions(cr):
343+
raise ValueError(
344+
"%r should point to a model inheriting from 'ir.actions.actions', not a %r" % (xml_id, model)
345+
)
346+
347+
# NOTE: same note from get_models_inheriting_actions_actions(): sql query for maintainability + suggestion of
348+
# caching for performance
349+
cr.execute("SELECT name, model FROM ir_model_fields WHERE relation = 'ir.actions.actions' AND on_delete ='cascade'")
350+
for column_name, model in cr.fetchall():
351+
model_table = table_of_model(cr, model)
352+
# NOTE: consider disabling+setting field to NULL instead of deleting for better UX
353+
cr.execute(
354+
'DELETE FROM "{}" WHERE "{}" = %s'.format(model_table, column_name),
355+
(action_id,),
356+
)
357+
312358

313359
def remove_record(cr, name):
314360
"""
@@ -351,10 +397,13 @@ def remove_record(cr, name):
351397
if model == "res.groups":
352398
_logger.log(NEARLYWARN, "Removing group %r", name)
353399
return remove_group(cr, group_id=res_id)
400+
401+
if model in get_models_inheriting_actions_actions(cr):
402+
_logger.log(NEARLYWARN, "Removing action %r", name)
403+
return remove_action(cr, action_id=res_id)
354404

355405
return remove_records(cr, model, [res_id])
356406

357-
358407
def remove_records(cr, model, ids):
359408
if not ids:
360409
return

0 commit comments

Comments
 (0)