From 364aabd3f1be53e37fa3f13266b159cdf9a5a439 Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Mon, 4 May 2026 16:14:16 +0200 Subject: 🐛 criteria search: fix search with precise and open term for the same criteria MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- archaeological_operations/tests.py | 7 +++- ishtar_common/views_item.py | 67 +++++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/archaeological_operations/tests.py b/archaeological_operations/tests.py index 953229dbc..dc98ba25e 100644 --- a/archaeological_operations/tests.py +++ b/archaeological_operations/tests.py @@ -3177,7 +3177,7 @@ class OperationSearchTest(TestCase, OperationInitTest, SearchText, StatisticsTes operation_3 = models.Operation.objects.get(pk=self.operations[2].pk) operation_1.common_name = "Opération : Château de Fougères" operation_1.save() - operation_2.common_name = "Opération : Fougère filicophyta et " "herbe à chat" + operation_2.common_name = "Opération : Fougère filicophyta et herbe à chat" operation_2.save() operation_3.common_name = "Opération : Château Filicophyta" operation_3.save() @@ -3348,6 +3348,11 @@ class OperationSearchTest(TestCase, OperationInitTest, SearchText, StatisticsTes (f'{search_name_q}="Foug*" {search_name_q}="Jossel*"', 2), ] self._test_search(c, result, context="Many name open search") + # open and precise search + result = [ + (f'{search_name_q}="Foug*" {search_name_q}="Opération : Château de Josselin"', 2), + ] + self._test_search(c, result, context="Many name open search") # non hierarchic search search_remain_q = str(pgettext("key for text search", "remain")) diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index eb37c9d62..40734a087 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -1695,7 +1695,7 @@ def _manage_relation_types(relation_types, dct, query, or_reqs): if k.endswith("year"): k += "__exact" alt_dct[rtype_prefix + "right_relations__right_record__" + k] = val - query |= Q(**alt_dct) + query |= Q(**__reapply_filters(alt_dct)) for k, or_req in or_reqs: altor_dct = alt_dct.copy() altor_dct.pop(k) @@ -1704,10 +1704,23 @@ def _manage_relation_types(relation_types, dct, query, or_reqs): if j == "year": j = "year__exact" altor_dct[rtype_prefix + "right_relations__right_record__" + j] = val - query |= Q(**altor_dct) + query |= Q(**__reapply_filters(altor_dct)) return query +def __reapply_filters(dct): + """ + reapply filters "__iexact", "__icontains" + dct[k] = (filter, value) -> dct[k + filter] = value + """ + new_dct = {} + for k, value in dct.items(): + if isinstance(value, tuple): + k, value = k + value[0], value[1] + new_dct[k] = value + return new_dct + + def _construct_query(relation_types, dct, or_reqs, and_reqs, excluded_relation=False): # excluded -> reverse logic if excluded_relation: @@ -1724,17 +1737,31 @@ def _construct_query(relation_types, dct, or_reqs, and_reqs, excluded_relation=F dct = {} # manage multi value not already managed + # regroup same keys with different filters + filters = ("__iexact", "__icontains", "__isnull") for key in list(dct.keys()): - if isinstance(dct[key], str) and ";" in dct[key]: - values = [v for v in dct[key].split(";") if v] - if not values: - dct.pop(key) - continue - dct[key] = values[0] - if len(values) == 1: - continue - for v in values[1:]: - or_reqs.append((key, {key: v})) + base_key, q_filter = key, "" + for filtr in filters: + if key.endswith(filtr): + base_key = key[:-len(filtr)] + q_filter = filtr + break + if not isinstance(dct[key], str): + continue + # if filter -> value = (filter, value) + values = [v if not q_filter else (q_filter, v) + for v in dct[key].split(";")] + dct.pop(key) + if not values: + continue + if base_key in dct: + or_reqs.append((base_key, {base_key: values[0]})) + else: + dct[base_key] = values[0] + if len(values) == 1: + continue + for v in values[1:]: + or_reqs.append((base_key, {base_key: v})) for k in list(dct.keys()): if type(k) not in (list, tuple): @@ -1746,17 +1773,19 @@ def _construct_query(relation_types, dct, or_reqs, and_reqs, excluded_relation=F for other_key in k[1:]: or_reqs.append((first_key, {other_key: value})) - query = Q(**dct) + # generate query + query = Q(**__reapply_filters(dct)) + for or_req in or_reqs: - alt_dct = dct.copy() + base_dct = dct.copy() if isinstance(or_req, (tuple, list)): k, or_req = or_req - if k in alt_dct: - alt_dct.pop(k) - alt_dct.update(or_req) - query |= Q(**alt_dct) + if k in base_dct: + base_dct.pop(k) + base_dct.update(or_req) + query |= Q(**__reapply_filters(base_dct)) else: - query |= (Q(**alt_dct) & Q(or_req)) + query |= (Q(**__reapply_filters(base_dct)) & Q(or_req)) query = _manage_relation_types(relation_types, dct, query, or_reqs) done = [] -- cgit v1.2.3