summaryrefslogtreecommitdiff
path: root/odoo
diff options
context:
space:
mode:
authorIvan Yelizariev <iel@odoo.com>2021-01-28 16:14:41 +0000
committerRaphael Collet <rco@odoo.com>2021-02-02 15:18:41 +0000
commitd66652b4aa514546356306e3eb48eaecaba07c33 (patch)
tree0bc86672ef4a828be3e7ca7989ed4c537aef5e79 /odoo
parent7840b21483c0012174e6028a39b114ce56f06158 (diff)
[FIX] core: initialize new stored related field via SQL
Filling the new column in SQL is dramatically faster than doing it via the ORM, which iterates over all the records in the table. The test case is creating a related field for 100+ records with distinct values. Setting the field's value was taking more than 100 queries. It now takes 3 queries. task-2449313 opw-2380445 opw-2389376 https://github.com/odoo/enterprise/pull/15910 closes odoo/odoo#65421 X-original-commit: 82644c3ea20f7b5748f92ce4d0f4585335995d29 Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Diffstat (limited to 'odoo')
-rw-r--r--odoo/addons/base/tests/test_ir_actions.py45
-rw-r--r--odoo/fields.py31
2 files changed, 76 insertions, 0 deletions
diff --git a/odoo/addons/base/tests/test_ir_actions.py b/odoo/addons/base/tests/test_ir_actions.py
index 629c721aefc..76a6f6e2f7a 100644
--- a/odoo/addons/base/tests/test_ir_actions.py
+++ b/odoo/addons/base/tests/test_ir_actions.py
@@ -480,6 +480,51 @@ class TestCustomFields(common.TransactionCase):
self.assertTrue(custom_binary.attachment)
+ def test_related_field(self):
+ """ create a custom related field, and check filled values """
+ #
+ # Add a custom field equivalent to the following definition:
+ #
+ # class Partner(models.Model)
+ # _inherit = 'res.partner'
+ # x_oh_boy = fields.Char(related="country_id.code", store=True)
+ #
+
+ # pick N=100 records in comodel
+ countries = self.env['res.country'].search([('code', '!=', False)], limit=100)
+ self.assertEqual(len(countries), 100, "Not enough records in comodel 'res.country'")
+
+ # create records in model, with N distinct values for the related field
+ partners = self.env['res.partner'].create([
+ {'name': country.code, 'country_id': country.id} for country in countries
+ ])
+ partners.flush()
+
+ # determine how many queries it takes to create a non-computed field
+ query_count = self.cr.sql_log_count
+ self.env['ir.model.fields'].create({
+ 'model_id': self.env['ir.model']._get_id('res.partner'),
+ 'name': 'x_oh_box',
+ 'field_description': 'x_oh_box',
+ 'ttype': 'char',
+ })
+ query_count = self.cr.sql_log_count - query_count
+
+ # create the related field, and assert it only takes 3 extra queries
+ with self.assertQueryCount(query_count + 3):
+ self.env['ir.model.fields'].create({
+ 'model_id': self.env['ir.model']._get_id('res.partner'),
+ 'name': 'x_oh_boy',
+ 'field_description': 'x_oh_boy',
+ 'ttype': 'char',
+ 'related': 'country_id.code',
+ 'store': True,
+ })
+
+ # check the computed values
+ for partner in partners:
+ self.assertEqual(partner.x_oh_boy, partner.country_id.code)
+
def test_selection(self):
""" custom selection field """
Model = self.env[self.MODEL]
diff --git a/odoo/fields.py b/odoo/fields.py
index 8e940a3a387..b936d6c83fd 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -809,6 +809,21 @@ class Field(MetaField('DummyField', (object,), {})):
self.update_db_column(model, column)
self.update_db_notnull(model, column)
+ # optimization for computing simple related fields like 'foo_id.bar'
+ if (
+ not column
+ and len(self.related or ()) == 2
+ and self.related_field.store and not self.related_field.compute
+ ):
+ join_field = model._fields[self.related[0]]
+ if (
+ join_field.type == 'many2one'
+ and join_field.store and not join_field.compute
+ ):
+ model.pool.post_init(self.update_db_related, model)
+ # discard the "classical" computation
+ return False
+
return not column
def update_db_column(self, model, column):
@@ -859,6 +874,22 @@ class Field(MetaField('DummyField', (object,), {})):
elif not self.required and has_notnull:
sql.drop_not_null(model._cr, model._table, self.name)
+ def update_db_related(self, model):
+ """ Compute a stored related field directly in SQL. """
+ comodel = model.env[self.related_field.model_name]
+ model.env.cr.execute("""
+ UPDATE "{model_table}" AS x
+ SET "{model_field}" = y."{comodel_field}"
+ FROM "{comodel_table}" AS y
+ WHERE x."{join_field}" = y.id
+ """.format(
+ model_table=model._table,
+ model_field=self.name,
+ comodel_table=comodel._table,
+ comodel_field=self.related[1],
+ join_field=self.related[0],
+ ))
+
############################################################################
#
# Alternatively stored fields: if fields don't have a `column_type` (not