diff options
| -rw-r--r-- | odoo/addons/base/tests/test_ir_actions.py | 45 | ||||
| -rw-r--r-- | odoo/fields.py | 31 |
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 |
