Multi-level inherits search fix

Trytond now looks at the whole inherits hierarchy for fields when creating
search domain on ModelSQL objects instead of just looking at the direct parent.

issue1860
review369001
This commit is contained in:
Emmanuelle Delescolle committed 2012-05-16 15:48:00 +02:00
1 parent 3ebda5c8ee
commit 6cd2e51aa3
4 files changed
+248 -28

No files matched your search

+55 -28
View File
@@ -1348,6 +1348,40 @@ class ModelSQL(ModelStorage):
domain = domain[:]
cursor = Transaction().cursor
def column_get(model, field_name):
field = model._columns.get(field_name)
if not field:
if not field_name in model._inherit_fields:
raise Exception('ValidateError', 'Field "%s" doesn\'t ' \
'exist on "%s"' % (field_name, model._name))
parent_model = pool.get(model._inherit_fields[field_name][0])
field = column_get(parent_model, field_name)
return field
def get_model_for_field(model, field):
if field in model._columns:
return model
else:
parent_model = pool.get(model._inherit_fields[field][0])
return get_model_for_field(parent_model, field)
def get_inherits_join(model, field):
if field in model._columns:
return []
itable = pool.get(model._inherit_fields[field][0])
table_query = ''
table_arg = []
if itable.table_query():
table_query, table_args = model.table_query()
table_query = '(' + table_query + ') AS '
table_join = 'LEFT JOIN ' + table_query + \
'"' + itable._table + '" ON ' \
'"%s".id = "%s"."%s"' % (itable._table, model._table,
model._inherits[itable._name])
inherits = [(table_join, table_arg)]
inherits.extend(get_inherits_join(itable, field))
return inherits
for arg in domain:
if arg[1] not in OPERATORS:
raise Exception('ValidateError', 'Argument "%s" not supported'
@@ -1358,20 +1392,13 @@ class ModelSQL(ModelStorage):
table = self
fargs = domain[i][0].split('.', 1)
if fargs[0] in self._inherit_fields:
itable = pool.get(self._inherit_fields[fargs[0]][0])
table_query = ''
table_arg = []
if itable.table_query():
table_query, table_args = self.table_query()
table_query = '(' + table_query + ') AS '
table_join = 'LEFT JOIN ' + table_query + \
'"' + itable._table + '" ON ' \
'"%s".id = "%s"."%s"' % (itable._table, self._table,
self._inherits[itable._name])
if table_join not in tables:
tables.append(table_join)
tables_args.extend(table_arg)
field = table._columns.get(fargs[0], False)
inherit_joins = get_inherits_join(self, fargs[0])
for (table_join, table_arg) in inherit_joins:
if table_join not in tables:
tables.append(table_join)
tables_args.extend(table_arg)
field = column_get(self, fargs[0])
if not field:
if not fargs[0] in self._inherit_fields:
raise Exception('ValidateError', 'Field "%s" doesn\'t ' \
@@ -1698,9 +1725,10 @@ class ModelSQL(ModelStorage):
qu1, qu2 = [], []
for arg in domain:
table = self
if len(arg) > 3:
table = get_model_for_field(self, arg[0])
if table == self and len(arg) > 3:
table = arg[3]
column = column_get(table, arg[0])
if arg[1] in ('inselect', 'notinselect'):
clause = 'IN'
if arg[1] == 'notinselect':
@@ -1711,7 +1739,7 @@ class ModelSQL(ModelStorage):
elif arg[1] in ('in', 'not in'):
if len(arg[2]) > 0:
todel = []
if table._columns[arg[0]]._type != 'boolean':
if column._type != 'boolean':
for xitem in range(len(arg[2])):
if (arg[2][xitem] is False
or arg[2][xitem] is None):
@@ -1719,7 +1747,7 @@ class ModelSQL(ModelStorage):
arg2 = arg[2][:]
for xitem in todel[::-1]:
del arg2[xitem]
arg2 = [FIELDS[table._columns[arg[0]]._type].sql_format(x)
arg2 = [FIELDS[column._type].sql_format(x)
for x in arg2]
if len(arg2):
if reduce(lambda x, y: (x
@@ -1738,7 +1766,7 @@ class ModelSQL(ModelStorage):
('%s',) * len(arg2))))
qu2 += arg2
if todel:
if table._columns[arg[0]]._type == 'boolean':
if column._type == 'boolean':
if arg[1] == 'in':
qu1[-1] = '(' + qu1[-1] + ' OR ' \
'"%s"."%s" = %%s)' % \
@@ -1759,7 +1787,7 @@ class ModelSQL(ModelStorage):
'"%s"."%s" IS NOT NULL)' % \
(table._table, arg[0])
elif todel:
if table._columns[arg[0]]._type == 'boolean':
if column._type == 'boolean':
if arg[1] == 'in':
qu1.append('("%s"."%s" = %%s)' % \
(table._table, arg[0]))
@@ -1784,7 +1812,7 @@ class ModelSQL(ModelStorage):
qu2.append(True)
else:
if (arg[2] is False or arg[2] is None) and (arg[1] == '='):
if table._columns[arg[0]]._type == 'boolean':
if column._type == 'boolean':
qu1.append('("%s"."%s" = %%s)' % \
(table._table, arg[0]))
qu2.append(False)
@@ -1798,7 +1826,7 @@ class ModelSQL(ModelStorage):
if arg[0] == 'id':
qu1.append('("%s"."%s" %s %%s)' % \
(table._table, arg[0], arg[1]))
qu2.append(FIELDS[table._columns[arg[0]]._type].\
qu2.append(FIELDS[column._type].\
sql_format(arg[2]))
else:
add_null = False
@@ -1808,21 +1836,20 @@ class ModelSQL(ModelStorage):
add_null = True
else:
qu2.append(FIELDS[
table._columns[arg[0]]._type
column._type
].sql_format(arg[2]))
elif arg[1] in ('not like', 'not ilike'):
if not arg[2]:
qu2.append('')
else:
qu2.append(FIELDS[
table._columns[arg[0]]._type
column._type
].sql_format(arg[2]))
add_null = True
else:
if arg[0] in table._columns:
qu2.append(FIELDS[
table._columns[arg[0]]._type
].sql_format(arg[2]))
qu2.append(FIELDS[
column._type
].sql_format(arg[2]))
qu1.append('("%s"."%s" %s %%s)' % (table._table,
arg[0], arg[1]))
if add_null:
+52
View File
@@ -23,3 +23,55 @@ class URLObject(ModelSQL):
URLObject()
class ModelInherits(ModelSQL):
_name = 'test.model_inherits'
name = fields.Char('Name')
ModelInherits()
class ModelInheritsSubModel(ModelSQL):
_name = 'test.submodel'
_inherits = {'test.model_inherits': 'parent_model'}
subfield = fields.Char('SubField')
parent_model = fields.Many2One('test.model_inherits', 'Parent model',
required=True)
ModelInheritsSubModel()
class ModelInheritsSubSubModel(ModelSQL):
_name = 'test.subsubmodel'
_inherits = {'test.submodel': 'parent_model'}
subsubfield = fields.Char('SubField')
parent_model = fields.Many2One('test.submodel', 'Parent model',
required=True)
ModelInheritsSubSubModel()
class ModelInheritsSubSubSubModel(ModelSQL):
_name = 'test.subsubsubmodel'
_inherits = {'test.subsubmodel': 'parent_model'}
subsubsubfield = fields.Char('SubField')
parent_model = fields.Many2One('test.subsubmodel', 'Parent model',
required=True)
ModelInheritsSubSubSubModel()
class ModelInheritsOverriddenFieldModel(ModelSQL):
_name = 'test.overriddeninheritedfieldmodel'
_inherits = {'test.subsubmodel': 'parent_model'}
subfield = fields.Integer('Overridden field')
parent_model = fields.Many2One('test.subsubmodel', 'Parent model',
required=True)
ModelInheritsOverriddenFieldModel()
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import unittest
from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
install_module
from trytond.transaction import Transaction
class ModelInheritsTestCase(unittest.TestCase):
'''
Test ModelInheritSearch
'''
def setUp(self):
install_module('test')
self.parent = POOL.get('test.model_inherits')
self.sub = POOL.get('test.submodel')
self.subsub = POOL.get('test.subsubmodel')
self.subsubsub = POOL.get('test.subsubsubmodel')
self.overridden = POOL.get('test.overriddeninheritedfieldmodel')
def create_data(self):
parent_id = self.parent.create({'name': 'parent'})
sub_id = self.sub.create({
'name': 'sub',
'subfield': 'level1',
})
subsub_id = self.subsub.create({
'name': 'subsub',
'subfield': 'level2',
'subsubfield': 'ssfcontent',
})
subsubsub_id = self.subsubsub.create({
'name': 'subsubsub',
'subfield': 'level3',
'subsubfield': 'ssfcontent2',
'subsubsubfield': 'sssfcontent',
})
overridden_id = self.overridden.create({
'name': 'overridden',
'subfield': 3,
})
return (parent_id, sub_id, subsub_id, subsubsub_id, overridden_id)
def test0010selfsearch(self):
'''
Test searching on own field
'''
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
(_parent_id, _sub_id, _subsub_id, subsubsub_id, overridden_id) = \
self.create_data()
ids = self.subsubsub.search([
('subsubsubfield', '=', 'sssfcontent'),
])
self.assertEqual(ids, [subsubsub_id])
transaction.cursor.rollback()
def test0020parentsearch(self):
'''
Test searching on parent field
'''
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
(_parent_id, _sub_id, _subsub_id, subsubsub_id, overridden_id) = \
self.create_data()
ids = self.subsubsub.search([('subsubfield', '=', 'ssfcontent2')])
self.assertEqual(ids, [subsubsub_id])
ids = self.subsubsub.search([('subsubfield', '=', 'ssfcontent')])
self.assertEqual(ids, [])
transaction.cursor.rollback()
def test0030granparentsearch(self):
'''
Test searching on granparent field
'''
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
(_parent_id, _sub_id, _subsub_id, subsubsub_id, overridden_id) = \
self.create_data()
ids = self.subsubsub.search([('subfield', '=', 'level3')])
self.assertEqual(ids, [subsubsub_id])
ids = self.subsubsub.search([('subfield', '=', 'level1')])
self.assertEqual(ids, [])
transaction.cursor.rollback()
def test0040greatgranparentsearch(self):
'''
Test searching on greatgranparent field
'''
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
(_parent_id, _sub_id, _subsub_id, subsubsub_id, overridden_id) = \
self.create_data()
ids = self.subsubsub.search([('name', '=', 'subsubsub')])
self.assertEqual(ids, [subsubsub_id])
ids = self.subsubsub.search([('name', '=', 'parent')])
self.assertEqual(ids, [])
transaction.cursor.rollback()
def test0050overriddenfieldsearch(self):
'''
Test searching on greatgranparent field
'''
with Transaction().start(DB_NAME, USER,
context=CONTEXT) as transaction:
(_parent_id, _sub_id, _subsub_id, subsubsub_id, overridden_id) = \
self.create_data()
ids = self.overridden.search([('subfield', '=', 3)])
self.assertEqual(ids, [overridden_id])
transaction.cursor.rollback()
def suite():
return unittest.TestLoader().loadTestsFromTestCase(ModelInheritsTestCase)
if __name__ == '__main__':
suite = suite()
unittest.TextTestRunner(verbosity=2).run(suite)
+2
View File
@@ -251,6 +251,8 @@ def all_suite():
suite_.addTests(test_mixins.suite())
import trytond.tests.test_wizard as test_wizard
suite_.addTests(test_wizard.suite())
import trytond.tests.test_model_inherits as test_inherits
suite_.addTests(test_inherits.suite())
return suite_