def get_search_results()

in src/olympia/amo/admin.py [0:0]


    def get_search_results(self, request, queryset, search_term):
        """
        Return a tuple containing a queryset to implement the search,
        and a boolean indicating if the results may contain duplicates.

        Originally copied from Django's, but with the following differences:
        - The operator joining the query parts is dynamic: if the search term
          contain a comma and no space, then the comma is used as the separator
          instead, and the query parts are joined by OR, not AND, allowing
          admins to search by a list of ids, emails or usernames and find all
          objects in that list.
        - If the search terms are all numeric and there is more than one, then
          we also restrict the fields we search to the one returned by
          get_search_id_field(request) using a __in ORM lookup directly.
        - If the search terms are all IP addresses, a special search for
          objects matching those IPs is triggered
        - If the queryset has a `for_count` property, then we use that to do
          some optimizations, removing annotations that are only needed for
          display purposes.
        """

        # Apply keyword searches.
        def construct_search(field_name):
            if field_name.startswith('^'):
                return '%s__istartswith' % field_name[1:]
            elif field_name.startswith('='):
                return '%s__iexact' % field_name[1:]
            elif field_name.startswith('@'):
                return '%s__icontains' % field_name[1:]
            # Use field_name if it includes a lookup.
            opts = queryset.model._meta
            lookup_fields = field_name.split(models.constants.LOOKUP_SEP)
            # Go through the fields, following all relations.
            prev_field = None
            for path_part in lookup_fields:
                if path_part == 'pk':
                    path_part = opts.pk.name
                try:
                    field = opts.get_field(path_part)
                except FieldDoesNotExist:
                    # Use valid query lookups.
                    if prev_field and prev_field.get_lookup(path_part):
                        return field_name
                else:
                    prev_field = field
                    if hasattr(field, 'get_path_info'):
                        # Update opts to follow the relation.
                        opts = field.get_path_info()[-1].to_opts
            # Otherwise, use the field with icontains.
            return '%s__icontains' % field_name

        if self.search_by_ip_actions:
            ips_and_networks = self.ip_addresses_and_networks_from_query(search_term)
            # If self.search_by_ip_actions is truthy, then we can call
            # get_queryset_with_related_ips(), which will add IP
            # annotations if needed (either because we're doing an IP search
            # or because the known_ip_addresses field is in list_display)
            queryset, may_have_duplicates = self.get_queryset_with_related_ips(
                request, queryset, ips_and_networks
            )
            # ... We can return here early if we were indeed searching by IP.
            if ips_and_networks:
                return queryset, may_have_duplicates
        else:
            may_have_duplicates = False

        search_fields = self.get_search_fields(request)
        filters = []
        joining_operator = operator.and_
        if not (search_fields and search_term):
            # return early if we have nothing special to do
            return queryset, may_have_duplicates
        # Do our custom logic if a `,` is present. Note that our custom search
        # form (AMOModelAdminChangeListSearchForm) does some preliminary
        # cleaning when it sees a comma, trimming whitespace around each term.
        if ',' in search_term:
            separator = ','
            joining_operator = operator.or_
        else:
            separator = None
        # We support `*` as a wildcard character for our `__like` lookups.
        search_term = search_term.replace('*', '%')
        search_terms = search_term.split(separator)
        if (
            (search_id_field := self.get_search_id_field(request))
            and len(search_terms) >= self.minimum_search_terms_to_search_by_id
            and all(term.isnumeric() for term in search_terms)
        ):
            # if we have at least minimum_search_terms_to_search_by_id terms
            # they are all numeric, we're doing a bulk id search
            queryset = queryset.filter(**{f'{search_id_field}__in': search_terms})
        else:
            orm_lookups = [
                construct_search(str(search_field)) for search_field in search_fields
            ]
            for bit in search_terms:
                or_queries = [
                    models.Q(**{orm_lookup: bit}) for orm_lookup in orm_lookups
                ]

                q_for_this_term = models.Q(functools.reduce(operator.or_, or_queries))
                filters.append(q_for_this_term)

            may_have_duplicates |= any(
                # Use our own lookup_spawns_duplicates(), not django's.
                self.lookup_spawns_duplicates(self.opts, search_spec)
                for search_spec in orm_lookups
            )

            if filters:
                queryset = queryset.filter(functools.reduce(joining_operator, filters))
        return queryset, may_have_duplicates