From 422ca9563ef20f10898377cd6dc316b131d5c268 Mon Sep 17 00:00:00 2001 From: Adrian Torres Date: Fri, 7 Aug 2020 14:51:57 +0000 Subject: [PATCH] [FIX] core: apply post-constraints only if necessary This is a followup of commit bc2bb5e03c2b32d4ee1b0597ea5889c17d2b0e0e When a module is updated, a constraint application may (temporarily) fail because the existing data does not respect the constraint, this is OK and can be fixed through hooks/migration scripts and was handled by the aforementioned commit. However when updating multiple modules, it is possible that an inheriting module will try to re-apply the failed constraint and succeed, if that is the case, when processing the `post_constraints` an already-existing constraint will be applied and raise an error. To fix this, a check is made before trying to apply the constraint, to verify that it is not already in _constraint_queue, if it is not, then we may attempt to apply it, if it is in the queue, then we may safely ignore it as it will be applied further down the registry cycle. closes odoo/odoo#55725 X-original-commit: 5225b9ce5178302af05b63029fb184e27b781815 Signed-off-by: Adrian Torres (adt) --- odoo/modules/registry.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py index 29a2d7d47cb..f660fccf52c 100644 --- a/odoo/modules/registry.py +++ b/odoo/modules/registry.py @@ -333,20 +333,27 @@ class Registry(Mapping): def post_constraint(self, func, *args, **kwargs): """ Call the given function, and delay it if it fails during an upgrade. """ try: - func(*args, **kwargs) + if (func, args, kwargs) not in self._constraint_queue: + # Module A may try to apply a constraint and fail but another module B inheriting + # from Module A may try to reapply the same constraint and succeed, however the + # constraint would already be in the _constraint_queue and would be executed again + # at the end of the registry cycle, this would fail (already-existing constraint) + # and generate an error, therefore a constraint should only be applied if it's + # not already marked as "to be applied". + func(*args, **kwargs) except Exception as e: if self._is_install: _schema.error(*e.args) else: _schema.info(*e.args) - self._constraint_queue.append(partial(func, *args, **kwargs)) + self._constraint_queue.append((func, args, kwargs)) def finalize_constraints(self): """ Call the delayed functions from above. """ while self._constraint_queue: - func = self._constraint_queue.popleft() + func, args, kwargs = self._constraint_queue.popleft() try: - func() + func(*args, **kwargs) except Exception as e: _schema.error(*e.args)