Skip to content

Commit

Permalink
[FIX] expression: fix search on one2many field with inherited inverse…
Browse files Browse the repository at this point in the history
… field

Consider the following setting:
 - on model A, field F is computed, stored, and depends on field G
 - on model A, field one2many G to model B, with inverse field H
 - on model B, field many2one H is inherited (_inherits) from model C
 - on model C, field many2one H is stored

When adding records of model B, the field F must be recomputed.  In order to
determine which records to recompute, one searches model A with a domain like
[(G, 'in', ids)].  In expression.py, this is resolved with an SQL query like

    select H from B where id in {ids}

This query fails, since the field H is not stored in model B.  This happens in
general if H is not stored (it may be any computed field).  In that case, one
should instead browse records from B, and read field H through the ORM.

A test case has been added: it introduces a many2one field in a parent model,
and a one2many field using the inherited many2one on a child model.  The test
checks whether one can search on the one2many field.
  • Loading branch information
rco-odoo committed Sep 4, 2015
1 parent 1658bee commit f5e5bbd
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 3 deletions.
8 changes: 8 additions & 0 deletions openerp/addons/test_inherit/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class mother(models.Model):
_inherit = 'test.inherit.mother'

field_in_mother = fields.Char()
partner_id = fields.Many2one('res.partner')

# extend the name field: make it required and change its default value
name = fields.Char(required=True, default='Bar')
Expand Down Expand Up @@ -71,4 +72,11 @@ class daughter(models.Model):
# change the default value of an inherited field
name = fields.Char(default='Baz')


class res_partner(models.Model):
_inherit = 'res.partner'

# define a one2many field based on the inherited field partner_id
daughter_ids = fields.One2many('test.inherit.daughter', 'partner_id')

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
12 changes: 12 additions & 0 deletions openerp/addons/test_inherit/tests/test_inherit.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,17 @@ def test_40_selection_extension(self):
self.assertEqual(mother._columns['state'].selection,
[('a', 'A'), ('b', 'B'), ('c', 'C'), ('d', 'D')])

def test_50_search_one2many(self):
""" check search on one2many field based on inherited many2one field. """
# create a daughter record attached to partner Demo
partner_demo = self.env.ref('base.partner_demo')
daughter = self.env['test.inherit.daughter'].create({'partner_id': partner_demo.id})
self.assertEqual(daughter.partner_id, partner_demo)
self.assertIn(daughter, partner_demo.daughter_ids)

# search the partner from the daughter record
partners = self.env['res.partner'].search([('daughter_ids', 'in', daughter.ids)])
self.assertIn(partner_demo, partners)


# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
12 changes: 9 additions & 3 deletions openerp/osv/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@

import openerp.modules
from . import fields
from .. import SUPERUSER_ID
from ..models import MAGIC_COLUMNS, BaseModel
import openerp.tools as tools

Expand Down Expand Up @@ -941,11 +942,16 @@ def push_result(leaf):
call_null = False
push(create_substitution_leaf(leaf, FALSE_LEAF, model))
else:
ids2 = select_from_where(cr, column._fields_id, comodel._table, 'id', ids2, operator)
if ids2:
# determine ids1 <-- column._fields_id --- ids2
if comodel._fields[column._fields_id].store:
ids1 = select_from_where(cr, column._fields_id, comodel._table, 'id', ids2, operator)
else:
recs = comodel.browse(cr, SUPERUSER_ID, ids2, {'prefetch_fields': False})
ids1 = recs.mapped(column._fields_id).ids
if ids1:
call_null = False
o2m_op = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
push(create_substitution_leaf(leaf, ('id', o2m_op, ids2), model))
push(create_substitution_leaf(leaf, ('id', o2m_op, ids1), model))

if call_null:
o2m_op = 'in' if operator in NEGATIVE_TERM_OPERATORS else 'not in'
Expand Down

0 comments on commit f5e5bbd

Please sign in to comment.