|
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,69 @@ 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 | + 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 | + |
313 | 377 | def remove_record(cr, name):
|
314 | 378 | """
|
315 | 379 | Remove a record and its references corresponding to the given :term:`xml_id <external identifier>`.
|
@@ -351,10 +415,13 @@ def remove_record(cr, name):
|
351 | 415 | if model == "res.groups":
|
352 | 416 | _logger.log(NEARLYWARN, "Removing group %r", name)
|
353 | 417 | 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) |
354 | 422 |
|
355 | 423 | return remove_records(cr, model, [res_id])
|
356 | 424 |
|
357 |
| - |
358 | 425 | def remove_records(cr, model, ids):
|
359 | 426 | if not ids:
|
360 | 427 | return
|
|
0 commit comments