diff options
Diffstat (limited to 'odoo/addons/base/models/ir_model.py')
| -rw-r--r-- | odoo/addons/base/models/ir_model.py | 38 |
1 files changed, 30 insertions, 8 deletions
diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py index a2dbffe57b1..acb8476e4c7 100644 --- a/odoo/addons/base/models/ir_model.py +++ b/odoo/addons/base/models/ir_model.py @@ -1368,6 +1368,29 @@ class IrModelSelection(models.Model): def _process_ondelete(self): """ Process the 'ondelete' of the given selection values. """ + def safe_write(records, fname, value): + if not records: + return + try: + with self.env.cr.savepoint(): + records.write({fname: value}) + except Exception as e: + # going through the ORM failed, probably because of an exception + # in an override or possibly a constraint. + _logger.warning( + "Could not fulfill ondelete action for field %s.%s, " + "attempting ORM bypass...", records._name, fname, + ) + query = sql.SQL("UPDATE {} SET {}=%s WHERE id IN %s").format( + sql.Identifier(records._table), + sql.Identifier(fname), + ) + # if this fails then we're shit out of luck and there's nothing + # we can do except fix on a case-by-case basis + value = field.convert_to_column(value, records) + self.env.cr.execute(query, [value, records._ids]) + records.invalidate_cache([fname]) + for selection in self: Model = self.env[selection.field_id.model] # The field may exist in database but not in registry. In this case @@ -1390,10 +1413,10 @@ class IrModelSelection(models.Model): elif callable(ondelete): ondelete(selection._get_records()) elif ondelete == 'set null': - selection._get_records().write({field.name: False}) + safe_write(selection._get_records(), field.name, False) elif ondelete == 'set default': value = field.convert_to_write(field.default(Model), Model) - selection._get_records().write({field.name: value}) + safe_write(selection._get_records(), field.name, value) elif ondelete == 'cascade': selection._get_records().unlink() else: @@ -2150,13 +2173,12 @@ class IrModelData(models.Model): constraints = self.env['ir.model.constraint'].search([('module', 'in', modules.ids)]) constraints._module_data_uninstall() - # Remove fields, selections and relations. Note that the selections of - # removed fields do not require any "data fix", as their corresponding - # column no longer exists. We can therefore completely ignore them. That - # is why selections are removed after fields: most selections are - # deleted on cascade by their corresponding field. - delete(self.env['ir.model.fields'].browse(unique(field_ids))) + # If we delete a selection field, and some of its values have ondelete='cascade', + # we expect the records with that value to be deleted. If we delete the field first, + # the column is dropped and the selection is gone, and thus the records above will not + # be deleted. delete(self.env['ir.model.fields.selection'].browse(unique(selection_ids)).exists()) + delete(self.env['ir.model.fields'].browse(unique(field_ids))) relations = self.env['ir.model.relation'].search([('module', 'in', modules.ids)]) relations._module_data_uninstall() |
