Search code examples
pythondjangosearchdjango-q

How to make Q lookups in django look for multiple values


Currently I have a very simple search option which goes through my pages and looks up through several fields for requested query. If I input a single word in it it works just fine, however if I input multiple words in search query, I get nothing, even if a single page contains all attributes. For example if I search for test admin, I will not get empty query, even though there's a page with title test, with content test and author admin. Is there a way to expand the search to look up each word in search query individually?

# Fetch all pages and sort them alphabetically
queryset = Page.objects.all().filter(archived=False).order_by("title")

# Get search query if applicable
query = request.GET.get("q")
if query:
    queryset = queryset.filter(
        Q(title__icontains=query) |
        Q(content__icontains=query) |
        Q(user__username__icontains=query) |
        Q(user__first_name__icontains=query) |
        Q(user__last_name__icontains=query)
    ).distinct()

Solution

  • You're asking for something like the following, where you filter by each individual word in the query:

    import operator
    
    queryset = Page.objects.all().filter(archived=False).order_by("title")
    
    query = request.GET.get("q")
    if query:
        words = query.split()
        fields = ["title__icontains", "content__icontains", "user__username__contains",
                  "user__first_name__icontains", "user__last_name__icontains"]
    
        q_expression = [Q(f,w) for f in fields for w in words]
        queryset = queryset.filter(reduce(operator.or_, q_expression))\
                           .distinct()
    

    However, at this point doing search this way gets a little bit messy. If your app grows and you need to search over several models, consider looking into an external search package such as Haystack.