[IMP] base: batch creation of custom related fields

Before this commit, trying to create a field related to another field in
the same batch raises an error, because the computation of the field
'related_field_id' cannot find the target field in the registry.

The new approach consists in getting the target field from the database
instead.  It works when creating fields in batch, because all records
are inserted into the database before the field is computated.

This costs extra queries, but it allows to batch field creation and
avoids multiple calls to setup_models() when creating a model with
partner field in Studio. (see odoo/enterprise#21746)

The creation of a simple custom related field (related="x.y") costs 3
additional queries to determine the target field:
 - 2 queries to get a given field on a given model
 - 1 query to get the first field's comodel

Part-of: odoo/odoo#78514
This commit is contained in:
Raphael Collet
2021-10-22 17:34:18 +00:00
committed by Xavier-Do
parent e966beb3ca
commit 76f699ca0b
2 changed files with 13 additions and 14 deletions
+11 -12
View File
@@ -524,8 +524,7 @@ class IrModelFields(models.Model):
def _compute_related_field_id(self):
for rec in self:
if rec.state == 'manual' and rec.related:
field = rec._related_field()
rec.related_field_id = self._get(field.model_name, field.name)
rec.related_field_id = rec._related_field()
else:
rec.related_field_id = False
@@ -578,17 +577,17 @@ class IrModelFields(models.Model):
]
def _related_field(self):
""" Return the ``Field`` instance corresponding to ``self.related``. """
""" Return the ``ir.model.fields`` record corresponding to ``self.related``. """
names = self.related.split(".")
last = len(names) - 1
model = self.env[self.model or self.model_id.model]
model_name = self.model
for index, name in enumerate(names):
field = model._fields.get(name)
if field is None:
field = self._get(model_name, name)
if not field:
raise UserError(_("Unknown field name '%s' in related field '%s'") % (name, self.related))
if index < last and not field.relational:
model_name = field.relation
if index < last and not field.relation:
raise UserError(_("Non-relational field name '%s' in related field '%s'") % (name, self.related))
model = model[name]
return field
@api.constrains('related')
@@ -596,9 +595,9 @@ class IrModelFields(models.Model):
for rec in self:
if rec.state == 'manual' and rec.related:
field = rec._related_field()
if field.type != rec.ttype:
if field.ttype != rec.ttype:
raise ValidationError(_("Related field '%s' does not have type '%s'") % (rec.related, rec.ttype))
if field.relational and field.comodel_name != rec.relation:
if field.relation != rec.relation:
raise ValidationError(_("Related field '%s' does not have comodel '%s'") % (rec.related, rec.relation))
@api.onchange('related')
@@ -608,8 +607,8 @@ class IrModelFields(models.Model):
field = self._related_field()
except UserError as e:
return {'warning': {'title': _("Warning"), 'message': e}}
self.ttype = field.type
self.relation = field.comodel_name
self.ttype = field.ttype
self.relation = field.relation
self.readonly = True
@api.constrains('depends')
+2 -2
View File
@@ -514,8 +514,8 @@ class TestCustomFields(common.TransactionCase):
'store': True,
})
# same with a related field, it only takes 2 extra queries
with self.assertQueryCount(query_count + 2):
# same with a related field, it only takes 5 extra queries
with self.assertQueryCount(query_count + 5):
self.env.registry.clear_caches()
self.env['ir.model.fields'].create({
'model_id': model_id,