I have two separate models and would like to provide my users with a single search page that would operate over both.
class Person(models.Model):
name = models.CharField(...)
...
class Animal(models.Model):
name = models.CharField(...)
...
# I can write a simplified search view like this:
def search_view(request):
people = Person.objects.filter(name__icontains=request.GET['q']
animals = Animal.objects.filter(name__icontains=request.GET['q']
combined = itertools.chain(people, animals)
return render(request, 'results.html', {'object_list': combined}
Sorting the results require a bit more work but still seems doable, either by implementing the sorting in Python or with some cleverness when combining the two sorted querysets.
However, I can’t figure out how to handle pagination. I typically use a generic ListView where I just need to declare a paginate_by parameter but that doesn’t work here.
Has anyone ever had to tackle this issue before? Do you know of any third-party app that could make this a bit easier?
Matthew Schinckel’s blog post today covers an interesting pattern of querying multiple models through a database view and turning them back into their correct classes in Python, using Postgres’ JSON field: https://schinckel.net/2020/03/09/querysets-of-various-models/ . There are also two other patterns using proxy models and inheritance.
Oops, that was a typo on my part (I fixed it in the original post).
The two querysets are from different models with no common parent so I can’t use union() unfortunately. Changing the models might be a cleaner approach in the long run but that would be way too much work at the moment so I’m looking for alternative approaches (ideally, something that could plug seamlessly into a ListView).
The database view approach seems interesting, I’ll take a look.
Thanks!
You can union() two models with no shared parents, but it’s not necessarily a good idea.
Also in theory you could implement queryset-like class that iterates over the others together, with just enough methods implemented through ducktyping to make ti fit into a ListView / paginator. But I don’t recall seeing such a thing ever.
@bmispelon may I ask if you did succeed with this challenge?
I have a similar thingy and was going with an idea of creating a separate “index” model with generic relation, containing fields needed for search, but also a JSON field with the data to be displayed. However, this brings a problem of data duplication and maintaining the synchronization between main object and an “index” object.
Furthermore, if there is some extra annotation and filtering logic on one of the models (which is my case) - it is not possible to do proper query optimizations due to the generic relations.
I am now trying this suggestion, thanks for pointing to this package! However, it seems like performance is notably reduced (I wrapped just one model for a test, the outcome became slower).