diff options
| author | Raphael Collet <rco@odoo.com> | 2021-01-12 09:45:11 +0000 |
|---|---|---|
| committer | Raphael Collet <rco@odoo.com> | 2021-01-12 14:03:27 +0000 |
| commit | 25760348859f594abb796b5bc7b28f63e298cd53 (patch) | |
| tree | 19b44da9232340d56edc8ef29be5bd03b600bd0b /odoo | |
| parent | 9dd7e037239ff7b124d36f176fe4c66091e2cc4e (diff) | |
[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#64411
X-original-commit: 3b39d7efa94484b3bbf04fce756394a7e424f625
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Co-authored-by: Thibault Delavallée <tde@odoo.com>
Diffstat (limited to 'odoo')
| -rw-r--r-- | odoo/addons/test_new_api/models/test_new_api.py | 6 | ||||
| -rw-r--r-- | odoo/addons/test_new_api/tests/test_new_fields.py | 43 | ||||
| -rw-r--r-- | odoo/models.py | 13 |
3 files changed, 47 insertions, 15 deletions
diff --git a/odoo/addons/test_new_api/models/test_new_api.py b/odoo/addons/test_new_api/models/test_new_api.py index b02ef005015..df13ab303f9 100644 --- a/odoo/addons/test_new_api/models/test_new_api.py +++ b/odoo/addons/test_new_api/models/test_new_api.py @@ -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. """ diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py index f77bcc261b0..45a7f8a4c35 100644 --- a/odoo/addons/test_new_api/tests/test_new_fields.py +++ b/odoo/addons/test_new_api/tests/test_new_fields.py @@ -553,7 +553,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) @@ -629,16 +628,6 @@ class TestFields(TransactionCaseWithUserDemo): with self.assertRaises(AccessError): foo.with_user(user).display_name = 'Forbidden' - def test_13_inverse_access(self): - """ test access rights on inverse fields """ - foo = self.env['test_new_api.category'].create({'name': 'Foo'}) - user = self.env['res.users'].create({'name': 'Foo', 'login': 'foo'}) - self.assertFalse(user.has_group('base.group_system')) - # add group on non-stored inverse field - self.patch(type(foo).display_name, 'groups', 'base.group_system') - with self.assertRaises(AccessError): - foo.with_user(user).display_name = 'Forbidden' - def test_14_search(self): """ test search on computed fields """ discussion = self.env.ref('test_new_api.discussion_0') @@ -683,6 +672,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'] diff --git a/odoo/models.py b/odoo/models.py index be36edcdc79..d9adffc1bbe 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -1244,10 +1244,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 @@ -3680,7 +3685,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 @@ -3895,7 +3900,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() |
