'How to search and filter on multiple related models?

I am creating an search and filter system in which user can type and search related courses and institutions. This is my model structure:

Model Structure:

    Institutions:
     - Name
     - Id
     - email
     - address
    ...... etc.

    Courses:
     - owner = models.ForeignKey(Institution, on_delete=models.CASCADE)
     - name
     - ref
     - license
     - overview

..... etc

Now by using the above, i have created a search bar in which user needs to type something and then press "search" button or else press "Enter" key. This will show search results. My search code:

query = request.GET.get('q')
courses = Course.objects.filter(name__icontains=query)
institution= Institution.objects.filter(name__icontains=query)

queryset_chain = chain(courses,institution)

So the problem here is that, I want to display the result as follows.

scenario 1:

Lets say that course and Institution has same name like "tech".

When user search for "tech", I need to show result as follows:


Institution Name

  • Course name

scenario 2:

Lets say that course has name as "tech" but not Institution has that name.

When user search for "tech", I need to show result as follows: In this case, I need to show the Course name and also correspondant Institution name:


Institution Name

  • Course name

scenario 3:

where Institution has "tech" as name. But no course name.


Institution Name

  • any random Course name

How to handle this kind of clubbed search and filter ?



Solution 1:[1]

Since I cannot replicate your app, I can only give you an idea of how to solve this. Course model is the child of the Institution model, you can apply prefetch_related() or select_related() to prevent N+1 queries. These codes sum up all three scenarios using the Q() object from Django since you can use the OR -> | expression on it.

from django.db.models import Q

query = request.GET.get('q')
courses = Course.objects.select_related('institution') \
                        .filter(Q(institution_set__name__icontains=query) |
                                Q(course_set__name__icontains=query))

You also don't need to use chain, try regroup. I'm not sure what result you expect for the third scenario, but maybe adding the condition will do good.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1