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]