[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) <adt@odoo.com>
This commit is contained in:
Adrian Torres
2020-08-11 08:34:37 +00:00
parent d6e20b474b
commit 422ca9563e
+11 -4
View File
@@ -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)