From fc55d649d69a4b1084cb7b4acc3c93f461517589 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 13 Feb 2018 12:10:51 +0100 Subject: [PATCH] [FIX] base: don't recompute while performing the bulk of the unlink If a record is deleted *and* has property fields, the deletion of the property fields will trigger the various recomputes before the record itself has actually been deleted, and thus stuff which depend(ed) on that record will be recomputed under the assumption that the record still exists, which probably will not work. Under the assumption that the same issue would occur when deleting more than one batch of records (unlinking the data, values or attachment would also trigger a recompute before the proper end of the function and could lead to incoherence, e.g. some of the records being deleted being visible to recomputes), lift the entire effective body of the unlink into a norecompute context, to ensure that only the very final recompute (from the top-level unlink) is actually triggered. opw-807036 --- odoo/models.py | 79 +++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/odoo/models.py b/odoo/models.py index 03f676812d2..4433286258c 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -3388,53 +3388,54 @@ class BaseModel(object): raise UserError(_('Unable to delete this document because it is used as a default property')) # Delete the records' properties. - self.env['ir.property'].search([('res_id', 'in', refs)]).unlink() + with self.env.norecompute(): + self.env['ir.property'].search([('res_id', 'in', refs)]).unlink() - self.delete_workflow() + self.delete_workflow() - self.check_access_rule('unlink') + self.check_access_rule('unlink') - cr = self._cr - Data = self.env['ir.model.data'].sudo().with_context({}) - Values = self.env['ir.values'] - Attachment = self.env['ir.attachment'] + cr = self._cr + Data = self.env['ir.model.data'].sudo().with_context({}) + Values = self.env['ir.values'] + Attachment = self.env['ir.attachment'] - for sub_ids in cr.split_for_in_conditions(self.ids): - query = "DELETE FROM %s WHERE id IN %%s" % self._table - cr.execute(query, (sub_ids,)) + for sub_ids in cr.split_for_in_conditions(self.ids): + query = "DELETE FROM %s WHERE id IN %%s" % self._table + cr.execute(query, (sub_ids,)) - # Removing the ir_model_data reference if the record being deleted - # is a record created by xml/csv file, as these are not connected - # with real database foreign keys, and would be dangling references. - # - # Note: the following steps are performed as superuser to avoid - # access rights restrictions, and with no context to avoid possible - # side-effects during admin calls. - data = Data.search([('model', '=', self._name), ('res_id', 'in', sub_ids)]) - if data: - data.unlink() + # Removing the ir_model_data reference if the record being deleted + # is a record created by xml/csv file, as these are not connected + # with real database foreign keys, and would be dangling references. + # + # Note: the following steps are performed as superuser to avoid + # access rights restrictions, and with no context to avoid possible + # side-effects during admin calls. + data = Data.search([('model', '=', self._name), ('res_id', 'in', sub_ids)]) + if data: + data.unlink() - # For the same reason, remove the relevant records in ir_values - refs = ['%s,%s' % (self._name, i) for i in sub_ids] - values = Values.search(['|', ('value', 'in', refs), - '&', ('model', '=', self._name), - ('res_id', 'in', sub_ids)]) - if values: - values.unlink() + # For the same reason, remove the relevant records in ir_values + refs = ['%s,%s' % (self._name, i) for i in sub_ids] + values = Values.search(['|', ('value', 'in', refs), + '&', ('model', '=', self._name), + ('res_id', 'in', sub_ids)]) + if values: + values.unlink() - # For the same reason, remove the relevant records in ir_attachment - # (the search is performed with sql as the search method of - # ir_attachment is overridden to hide attachments of deleted - # records) - query = 'SELECT id FROM ir_attachment WHERE res_model=%s AND res_id IN %s' - cr.execute(query, (self._name, sub_ids)) - attachments = Attachment.browse([row[0] for row in cr.fetchall()]) - if attachments: - attachments.unlink() + # For the same reason, remove the relevant records in ir_attachment + # (the search is performed with sql as the search method of + # ir_attachment is overridden to hide attachments of deleted + # records) + query = 'SELECT id FROM ir_attachment WHERE res_model=%s AND res_id IN %s' + cr.execute(query, (self._name, sub_ids)) + attachments = Attachment.browse([row[0] for row in cr.fetchall()]) + if attachments: + attachments.unlink() - # invalidate the *whole* cache, since the orm does not handle all - # changes made in the database, like cascading delete! - self.invalidate_cache() + # invalidate the *whole* cache, since the orm does not handle all + # changes made in the database, like cascading delete! + self.invalidate_cache() # recompute new-style fields if self.env.recompute and self._context.get('recompute', True):