summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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