diff --git a/addons/account/models/sequence_mixin.py b/addons/account/models/sequence_mixin.py index 09977376e43..60d4195d797 100644 --- a/addons/account/models/sequence_mixin.py +++ b/addons/account/models/sequence_mixin.py @@ -45,10 +45,7 @@ class SequenceMixin(models.AbstractModel): field=sql.Identifier(self._sequence_field), )) - def __init__(self, pool, cr): - api.constrains(self._sequence_field, self._sequence_date_field)(type(self)._constrains_date_sequence) - return super().__init__(pool, cr) - + @api.constrains(lambda self: (self._sequence_field, self._sequence_date_field)) def _constrains_date_sequence(self): # Make it possible to bypass the constraint to allow edition of already messed up documents. # /!\ Do not use this to completely disable the constraint as it will make this mixin unreliable. diff --git a/odoo/api.py b/odoo/api.py index b98a4db613b..27440797ead 100644 --- a/odoo/api.py +++ b/odoo/api.py @@ -127,7 +127,12 @@ def constrains(*args): sure a constraint will always be triggered (e.g. to test the absence of value). + One may also pass a single function as argument. In that case, the field + names are given by calling the function with a model instance. + """ + if args and callable(args[0]): + args = args[0] return attrsetter('_constrains', args) diff --git a/odoo/models.py b/odoo/models.py index a07d4cccdc3..0b1861ea932 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -750,9 +750,18 @@ class BaseModel(metaclass=MetaModel): def is_constraint(func): return callable(func) and hasattr(func, '_constrains') + def wrap(func, names): + # wrap func into a proxy function with explicit '_constrains' + @api.constrains(*names) + def wrapper(self): + return func(self) + return wrapper + cls = type(self) methods = [] for attr, func in getmembers(cls, is_constraint): + if callable(func._constrains): + func = wrap(func, func._constrains(self)) for name in func._constrains: field = cls._fields.get(name) if not field: