[FIX] core: invoke constraint methods once per write
Consider a constraint method with two fields: a normal field, and a field with an inverse method. When calling write() with both fields, the constraint method should be invoked once. closes odoo/odoo#64433 X-original-commit: 25760348859f594abb796b5bc7b28f63e298cd53 Signed-off-by: Raphael Collet (rco) <rco@openerp.com> Co-authored-by: Thibault Delavallée <tde@odoo.com>
This commit is contained in:
co-authored by
Thibault Delavallée
parent
e8fea60bb8
commit
65fd4373c5
@@ -400,6 +400,7 @@ class ComputeInverse(models.Model):
|
||||
|
||||
foo = fields.Char()
|
||||
bar = fields.Char(compute='_compute_bar', inverse='_inverse_bar', store=True)
|
||||
baz = fields.Char()
|
||||
|
||||
@api.depends('foo')
|
||||
def _compute_bar(self):
|
||||
@@ -412,6 +413,11 @@ class ComputeInverse(models.Model):
|
||||
for record in self:
|
||||
record.foo = record.bar
|
||||
|
||||
@api.constrains('bar', 'baz')
|
||||
def _check_constraint(self):
|
||||
if self._context.get('log_constraint'):
|
||||
self._context.get('log', []).append('constraint')
|
||||
|
||||
|
||||
class MultiComputeInverse(models.Model):
|
||||
""" Model with the same inverse method for several fields. """
|
||||
|
||||
@@ -556,7 +556,6 @@ class TestFields(TransactionCaseWithUserDemo):
|
||||
self.assertEqual(foo1.name, 'Bar')
|
||||
self.assertEqual(foo2.name, 'Bar')
|
||||
|
||||
|
||||
# create/write on 'foo' should only invoke the compute method
|
||||
log = []
|
||||
model = self.env['test_new_api.compute.inverse'].with_context(log=log)
|
||||
@@ -676,6 +675,38 @@ class TestFields(TransactionCaseWithUserDemo):
|
||||
discussion.name = "X"
|
||||
discussion.flush()
|
||||
|
||||
def test_15_constraint_inverse(self):
|
||||
""" test constraint method on normal field and field with inverse """
|
||||
log = []
|
||||
model = self.env['test_new_api.compute.inverse'].with_context(log=log, log_constraint=True)
|
||||
|
||||
# create/write with normal field only
|
||||
log.clear()
|
||||
record = model.create({'baz': 'Hi'})
|
||||
self.assertCountEqual(log, ['constraint'])
|
||||
|
||||
log.clear()
|
||||
record.write({'baz': 'Ho'})
|
||||
self.assertCountEqual(log, ['constraint'])
|
||||
|
||||
# create/write with inverse field only
|
||||
log.clear()
|
||||
record = model.create({'bar': 'Hi'})
|
||||
self.assertCountEqual(log, ['inverse', 'constraint'])
|
||||
|
||||
log.clear()
|
||||
record.write({'bar': 'Ho'})
|
||||
self.assertCountEqual(log, ['inverse', 'constraint'])
|
||||
|
||||
# create/write with both fields only
|
||||
log.clear()
|
||||
record = model.create({'bar': 'Hi', 'baz': 'Hi'})
|
||||
self.assertCountEqual(log, ['inverse', 'constraint'])
|
||||
|
||||
log.clear()
|
||||
record.write({'bar': 'Ho', 'baz': 'Ho'})
|
||||
self.assertCountEqual(log, ['inverse', 'constraint'])
|
||||
|
||||
def test_16_compute_unassigned(self):
|
||||
model = self.env['test_new_api.compute.unassigned']
|
||||
|
||||
|
||||
+9
-4
@@ -1257,10 +1257,15 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
|
||||
|
||||
yield dbid, xid, converted, dict(extras, record=stream.index)
|
||||
|
||||
def _validate_fields(self, field_names):
|
||||
def _validate_fields(self, field_names, excluded_names=()):
|
||||
""" Invoke the constraint methods for which at least one field name is
|
||||
in ``field_names`` and none is in ``excluded_names``.
|
||||
"""
|
||||
field_names = set(field_names)
|
||||
excluded_names = set(excluded_names)
|
||||
for check in self._constraint_methods:
|
||||
if not field_names.isdisjoint(check._constrains):
|
||||
if (not field_names.isdisjoint(check._constrains)
|
||||
and excluded_names.isdisjoint(check._constrains)):
|
||||
check(self)
|
||||
|
||||
@api.model
|
||||
@@ -3681,7 +3686,7 @@ Fields:
|
||||
|
||||
# validate non-inversed fields first
|
||||
inverse_fields = [f.name for fs in determine_inverses.values() for f in fs]
|
||||
real_recs._validate_fields(set(vals) - set(inverse_fields))
|
||||
real_recs._validate_fields(vals, inverse_fields)
|
||||
|
||||
for fields in determine_inverses.values():
|
||||
# inverse records that are not being computed
|
||||
@@ -3896,7 +3901,7 @@ Fields:
|
||||
|
||||
# check Python constraints for non-stored inversed fields
|
||||
for data in data_list:
|
||||
data['record']._validate_fields(set(data['inversed']) - set(data['stored']))
|
||||
data['record']._validate_fields(data['inversed'], data['stored'])
|
||||
|
||||
if self._check_company_auto:
|
||||
records._check_company()
|
||||
|
||||
Reference in New Issue
Block a user