From 0a2cc7162d211b70fe503f4bf496862e0827c6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 26 Oct 2020 21:27:13 +0100 Subject: [PATCH] Construct recursively Tree search_rec_name with 'in' operators Each value of the 'in' operators must be processed as a '=' clause. The None values must be supported just in case it set in the 'in' values because the mixin does not allow empty name. issue9354 review304601002 --- trytond/model/tree.py | 60 ++++++++++++++++++++------------------ trytond/tests/test_tree.py | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 29 deletions(-) diff --git a/trytond/model/tree.py b/trytond/model/tree.py index 8fd36dca19..b4c3136580 100644 --- a/trytond/model/tree.py +++ b/trytond/model/tree.py @@ -38,39 +38,41 @@ def tree(parent='parent', name='name', separator=None): @classmethod def search_rec_name(cls, _, clause): - if isinstance(clause[2], str): - values = list(reversed(clause[2].split(separator))) - else: - values = [[]] - for value in clause[2]: - if value is None: - values[0].append(value) - continue - for i, v in range(reversed(value.split(separator))): - while len(values) <= i: - values.append([]) - values[i].append(v) domain = [] - field = name - for value in values: - domain.append((field, clause[1], value.strip())) - field = parent + '.' + field - if (( - clause[1].endswith('like') - and not clause[2].replace( - '%%', '__').startswith('%')) - or not clause[1].endswith('like')): - if clause[1].startswith('not') or clause[1] == '!=': + if isinstance(clause[2], str): + field = name + values = list(reversed(clause[2].split(separator))) + for value in values: + domain.append((field, clause[1], value.strip())) + field = parent + '.' + field + if (( + clause[1].endswith('like') + and not clause[2].replace( + '%%', '__').startswith('%')) + or not clause[1].endswith('like')): + if clause[1].startswith('not') or clause[1] == '!=': + operator = '!=' + domain.insert(0, 'OR') + else: + operator = '=' + top_parent = '.'.join((parent,) * len(values)) + domain.append((top_parent, operator, None)) + if (clause[1].endswith('like') + and clause[2].replace('%%', '__').endswith('%')): + ids = list(map(int, cls.search(domain, order=[]))) + domain = [(parent, 'child_of', ids)] + elif clause[2] is None: + domain.append((name, clause[1], clause[2])) + else: + if clause[1].startswith('not'): operator = '!=' - domain.insert(0, 'OR') + domain.append('AND') else: operator = '=' - top_parent = '.'.join((parent,) * len(values)) - domain.append((top_parent, operator, None)) - if (clause[1].endswith('like') - and clause[2].replace('%%', '__').endswith('%')): - ids = list(map(int, cls.search(domain, order=[]))) - domain = [(parent, 'child_of', ids)] + domain.append('OR') + for value in clause[2]: + domain.append(cls.search_rec_name( + name, (clause[0], operator, value))) return domain @classmethod diff --git a/trytond/tests/test_tree.py b/trytond/tests/test_tree.py index beb500b0f7..c04f74c774 100644 --- a/trytond/tests/test_tree.py +++ b/trytond/tests/test_tree.py @@ -69,6 +69,21 @@ class TreeMixinTestCase(unittest.TestCase): self.assertEqual(records, [parent]) + @with_transaction() + def test_search_rec_name_equals_none(self): + "Test search_rec_name equals" + pool = Pool() + Tree = pool.get('test.tree') + + parent = Tree(name="parent") + parent.save() + record = Tree(name="record", parent=parent) + record.save() + + records = Tree.search([('rec_name', '=', None)]) + + self.assertEqual(records, []) + @with_transaction() def test_search_rec_name_non_equals(self): "Test search_rec_name non equals" @@ -99,6 +114,51 @@ class TreeMixinTestCase(unittest.TestCase): self.assertEqual(records, [record]) + @with_transaction() + def test_search_rec_name_non_equals_none(self): + "Test search_rec_name equals" + pool = Pool() + Tree = pool.get('test.tree') + + parent = Tree(name="parent") + parent.save() + record = Tree(name="record", parent=parent) + record.save() + + records = Tree.search([('rec_name', '!=', None)]) + + self.assertEqual(records, [parent, record]) + + @with_transaction() + def test_search_rec_name_in(self): + "Test search_rec_name in" + pool = Pool() + Tree = pool.get('test.tree') + + parent = Tree(name="parent") + parent.save() + record = Tree(name="record", parent=parent) + record.save() + + records = Tree.search([('rec_name', 'in', ['parent / record'])]) + + self.assertEqual(records, [record]) + + @with_transaction() + def test_search_rec_name_in_toplevel(self): + "Test search_rec_name in top-level" + pool = Pool() + Tree = pool.get('test.tree') + + parent = Tree(name="parent") + parent.save() + record = Tree(name="record", parent=parent) + record.save() + + records = Tree.search([('rec_name', 'in', ['parent'])]) + + self.assertEqual(records, [parent]) + @with_transaction() def test_search_rec_name_like(self): "Test search_rec_name like"