From 1d5a5e491e78a7a40af2d35d6e5f540307712b4f Mon Sep 17 00:00:00 2001 From: tdruez Date: Wed, 8 Jan 2025 10:15:58 +0100 Subject: [PATCH] Improve the QuerySearchFilter to provide multiple search fields #1520 Signed-off-by: tdruez --- scanpipe/filters.py | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/scanpipe/filters.py b/scanpipe/filters.py index 2f6cf7195..320a84e42 100644 --- a/scanpipe/filters.py +++ b/scanpipe/filters.py @@ -266,7 +266,7 @@ def filter_for_lookup(cls, field, lookup_type): return super().filter_for_lookup(field, lookup_type) -def parse_query_string_to_lookups(query_string, default_lookup_expr, default_field): +def parse_query_string_to_lookups(query_string, default_lookup_expr, search_fields): """Parse a query string and convert it into queryset lookups using Q objects.""" lookups = Q() terms = shlex.split(query_string) @@ -295,11 +295,14 @@ def parse_query_string_to_lookups(query_string, default_lookup_expr, default_fie field_name = field_name[1:] negated = True + lookups &= Q( + **{f"{field_name}__{lookup_expr}": search_value}, _negated=negated + ) + else: search_value = term - field_name = default_field - - lookups &= Q(**{f"{field_name}__{lookup_expr}": search_value}, _negated=negated) + for field_name in search_fields: + lookups |= Q(**{f"{field_name}__{lookup_expr}": search_value}) return lookups @@ -323,6 +326,10 @@ class QuerySearchFilter(django_filters.CharFilter): field_class = QuerySearchField + def __init__(self, search_fields=None, lookup_expr="icontains", *args, **kwargs): + super().__init__(lookup_expr=lookup_expr, *args, **kwargs) + self.search_fields = search_fields or [] + def filter(self, qs, value): if not value: return qs @@ -330,11 +337,11 @@ def filter(self, qs, value): lookups = parse_query_string_to_lookups( query_string=value, default_lookup_expr=self.lookup_expr, - default_field=self.field_name, + search_fields=self.search_fields, ) try: - return qs.filter(lookups) + return qs.filter(lookups).distinct() except FieldError: return qs.none() @@ -347,7 +354,7 @@ class ProjectFilterSet(FilterSetUtilsMixin, django_filters.FilterSet): ] search = QuerySearchFilter( - label="Search", field_name="name", lookup_expr="icontains" + label="Search", search_fields=["name", "labels__name"], lookup_expr="icontains" ) sort = django_filters.OrderingFilter( label="Sort", @@ -508,7 +515,7 @@ class ResourceFilterSet(FilterSetUtilsMixin, django_filters.FilterSet): search = QuerySearchFilter( label="Search", - field_name="path", + search_fields=["path"], lookup_expr="icontains", ) sort = django_filters.OrderingFilter( @@ -615,15 +622,7 @@ def filter(self, qs, value): if value.startswith("pkg:"): return qs.for_package_url(value) - if ":" in value: - return super().filter(qs, value) - - search_fields = ["type", "namespace", "name", "version"] - lookups = Q() - for field_names in search_fields: - lookups |= Q(**{f"{field_names}__{self.lookup_expr}": value}) - - return qs.filter(lookups) + return super().filter(qs, value) class GroupOrderingFilter(django_filters.OrderingFilter): @@ -662,7 +661,9 @@ class PackageFilterSet(FilterSetUtilsMixin, django_filters.FilterSet): ] search = DiscoveredPackageSearchFilter( - label="Search", field_name="name", lookup_expr="icontains" + label="Search", + search_fields=["type", "namespace", "name", "version"], + lookup_expr="icontains", ) sort = GroupOrderingFilter( label="Sort", @@ -746,7 +747,7 @@ class DependencyFilterSet(FilterSetUtilsMixin, django_filters.FilterSet): ] search = QuerySearchFilter( - label="Search", field_name="name", lookup_expr="icontains" + label="Search", search_fields=["name"], lookup_expr="icontains" ) sort = GroupOrderingFilter( label="Sort", @@ -803,7 +804,7 @@ class Meta: class ProjectMessageFilterSet(FilterSetUtilsMixin, django_filters.FilterSet): search = QuerySearchFilter( - label="Search", field_name="description", lookup_expr="icontains" + label="Search", search_fields=["description"], lookup_expr="icontains" ) sort = django_filters.OrderingFilter( label="Sort", @@ -855,7 +856,7 @@ class RelationFilterSet(FilterSetUtilsMixin, django_filters.FilterSet): search = QuerySearchFilter( label="Search", - field_name="to_resource__path", + search_fields=["to_resource__path"], lookup_expr="icontains", ) sort = django_filters.OrderingFilter(