Implementing a search across multiple models in Django
For my project Scifi-Zone, I wanted to implement a search function across multiple models. Due to the structure of my project, this proved more challenging than I expected. Below is an excerpt of my project TESTING.md that describes the challenges I encountered and how I finally got the search function to work in the way I intended it to.
While I have integrated both tickets and packages into the tickets app, they use different models and templates. In my views, I therefore use filters to select only tickets or packages for the respective view, and each view has its own template.
Changing a view to select all objects from the Ticket class for the search and only display tickets or packages in the template worked. But for the search result display, I had to include if-statements to check whether the result referred to a ticket or a package. I didn’t like the approach. I felt it was overly complicated. Furthermore, this led to ‘NoReverseMatch’-errors, because my detail pages for tickets use the request and the tickets_id as parameters, while my detail pages for packages need an additional parameter package_name.
This led me to Django-Watson, which enabled me to register each model I want to be included for my search results. I had to heavily customize the search_results template. Obviously, it didn’t match my site’s styling. And, for some reason I don’t understand, the result display links back to the search query.
Using SQLite, I found that tickets and packages have the content_type_id 15 in the watson_searchentry table, and actors use the content_type_id 17. With this information, I could modify the template and let the search results for actors, tickets, and packages each link to the corresponding page using the correct template. I happily committed and pushed the code to my repo.
To my utter dismay, my deployed site displayed an almost ‘naked’ site for the search results and the links linked back to the search query. I soon realized that I had styled and modified the search_results template inside my virtual environment, and that this file, of course, had not been part of my commit. So I had to find a way to make Watson use my customized template. Placing the file in my projects root directory, the project-level template directory, and inside the base template didn’t work. Creating a custom context-processor to make it available didn’t work, because I failed to access the watson search results. Trying to use the template in one of my existing views failed.
I got the search function to work and use my customized template by creating a new app, importing Watson, and copying my template inside the app. And still, the problems didn’t end… The search suddenly didn’t find any results. I realized that the content_type_id values must differ in the production PostgreSQL database. But I didn’t know how to access these values. So I changed my logic to check result.title instead of content_type_id. This succeeded insofar as now search results were found again. But now results for tickets and packages linked to the ticket detail page, so that packages were displayed with the wrong template.
I found a way to access the PostgreSQL database with PostgreSQL. This showed me that in this database tickets and packages have the content_type_id 14, and actors have the content_type_id 16. Now I was able to check both for the content_type_id and whether result.title included either “Ticket” or “Package” and use the correct template for each type of object.
At long last, the search function works as intended!