changeset ee23aca64b26 in trytond:default details: https://hg.tryton.org/trytond?cmd=changeset&node=ee23aca64b26 description: Use specific search domain for reference field searches
issue9997 review324781002 diffstat: trytond/tests/test_tools.py | 59 ++++++++++++++++++++++++++++++++++++-- trytond/tools/domain_inversion.py | 57 +++++++++++++++++++++++++++++++++--- 2 files changed, 106 insertions(+), 10 deletions(-) diffs (145 lines): diff -r dd0bb6655e6d -r ee23aca64b26 trytond/tests/test_tools.py --- a/trytond/tests/test_tools.py Mon Apr 12 20:39:23 2021 +0200 +++ b/trytond/tests/test_tools.py Mon Apr 12 20:54:03 2021 +0200 @@ -739,16 +739,67 @@ domain = [['x', 'like', 'A%']] self.assertEqual( prepare_reference_domain(domain, 'x'), - [['x', 'like', 'A%']]) + [[]]) - domain = [['x.y', 'like', 'A%', 'model']] + domain = [['x', '=', 'A']] self.assertEqual( - prepare_reference_domain(domain, 'x'), [['y', 'like', 'A%']]) + prepare_reference_domain(domain, 'x'), + [[]]) domain = [['x.y', 'child_of', [1], 'model', 'parent']] self.assertEqual( prepare_reference_domain(domain, 'x'), - [['y', 'child_of', [1], 'parent']]) + [['x.y', 'child_of', [1], 'model', 'parent']]) + + domain = [['x.y', 'like', 'A%', 'model']] + self.assertEqual( + prepare_reference_domain(domain, 'x'), + [['x.y', 'like', 'A%', 'model']]) + + domain = [['x', '=', 'model,1']] + self.assertEqual( + prepare_reference_domain(domain, 'x'), + [['x.id', '=', 1, 'model']]) + + domain = [['x', '!=', 'model,1']] + self.assertEqual( + prepare_reference_domain(domain, 'x'), + [['x.id', '!=', 1, 'model']]) + + domain = [['x', '=', 'model,%']] + self.assertEqual( + prepare_reference_domain(domain, 'x'), + [['x.id', '!=', None, 'model']]) + + domain = [['x', '!=', 'model,%']] + self.assertEqual( + prepare_reference_domain(domain, 'x'), + [['x', 'not like', 'model,%']]) + + domain = [['x', 'in', + ['model_a,1', 'model_b,%', 'model_c,3', 'model_a,2']]] + self.assertEqual( + prepare_reference_domain(domain, 'x'), + [['OR', + ['x.id', 'in', [1, 2], 'model_a'], + ['x.id', '!=', None, 'model_b'], + ['x.id', 'in', [3], 'model_c'], + ]]) + + domain = [['x', 'not in', + ['model_a,1', 'model_b,%', 'model_c,3', 'model_a,2']]] + self.assertEqual( + prepare_reference_domain(domain, 'x'), + [['AND', + ['x.id', 'not in', [1, 2], 'model_a'], + ['x', 'not like', 'model_b,%'], + ['x.id', 'not in', [3], 'model_c'], + ]]) + + domain = [['x', 'in', ['model_a,1', 'foo']]] + self.assertEqual( + prepare_reference_domain(domain, 'x'), + [[]]) def test_extract_models(self): domain = [['x', 'like', 'A%']] diff -r dd0bb6655e6d -r ee23aca64b26 trytond/tools/domain_inversion.py --- a/trytond/tools/domain_inversion.py Mon Apr 12 20:39:23 2021 +0200 +++ b/trytond/tools/domain_inversion.py Mon Apr 12 20:54:03 2021 +0200 @@ -170,15 +170,60 @@ def prepare_reference_domain(domain, reference): "convert domain to replace reference fields by their local part" + + def value2reference(value): + model, ref_id = None, None + if isinstance(value, str) and ',' in value: + model, ref_id = value.split(',', 1) + if ref_id != '%': + try: + ref_id = int(ref_id) + except ValueError: + model, ref_id = None, value + elif (isinstance(value, (list, tuple)) + and len(value) == 2 + and isinstance(value[0], str) + and (isinstance(value[1], int) or value[1] == '%')): + model, ref_id = value + else: + ref_id = value + return model, ref_id + if domain in ('AND', 'OR'): return domain elif is_leaf(domain): - # When a Reference field is using the dotted notation the model - # specified must be removed from the clause - if domain[0].count('.') and len(domain) > 3: - local_name, target_name = domain[0].split('.', 1) - if local_name == reference: - return [target_name] + list(domain[1:3] + domain[4:]) + if domain[0] == reference: + if domain[1] in {'=', '!='}: + model, ref_id = value2reference(domain[2]) + if model is not None: + if ref_id == '%': + if domain[1] == '=': + return [reference + '.id', '!=', None, model] + else: + return [reference, 'not like', domain[2]] + return [reference + '.id', domain[1], ref_id, model] + elif domain[1] in {'in', 'not in'}: + model_values = {} + for value in domain[2]: + model, ref_id = value2reference(value) + if model is None: + break + model_values.setdefault(model, []).append(ref_id) + else: + new_domain = ['OR'] if domain[1] == 'in' else ['AND'] + for model, ref_ids in model_values.items(): + if '%' in ref_ids: + if domain[1] == 'in': + new_domain.append( + [reference + '.id', '!=', None, model]) + else: + new_domain.append( + [reference, 'not like', model + ',%']) + else: + new_domain.append( + [reference + '.id', domain[1], ref_ids, model]) + return new_domain + return [] return domain else: return [prepare_reference_domain(d, reference) for d in domain]