|
32 | 32 | _get_unique_indexes_with,
|
33 | 33 | _validate_table,
|
34 | 34 | column_exists,
|
| 35 | + column_nullable, |
35 | 36 | column_type,
|
36 | 37 | column_updatable,
|
37 | 38 | explode_execute,
|
@@ -310,6 +311,70 @@ def remove_asset(cr, name):
|
310 | 311 | # fmt:on
|
311 | 312 |
|
312 | 313 |
|
| 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 | + |
313 | 378 | def remove_record(cr, name):
|
314 | 379 | """
|
315 | 380 | Remove a record and its references corresponding to the given :term:`xml_id <external identifier>`.
|
@@ -352,6 +417,10 @@ def remove_record(cr, name):
|
352 | 417 | _logger.log(NEARLYWARN, "Removing group %r", name)
|
353 | 418 | return remove_group(cr, group_id=res_id)
|
354 | 419 |
|
| 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 | + |
355 | 424 | return remove_records(cr, model, [res_id])
|
356 | 425 |
|
357 | 426 |
|
|
0 commit comments