I need to combine two Querysets from different models into one. The most suggested solutions I've found online are:
To use |
and &
, but this works only in querysets from the same model
To use chain
and list
, but this takes away some of the queryset methods, and because of the way the code in my project is structured I cannot change that. I tried it and didn't work.
I read about Q, but it's not clear to me how I can apply it to accomplish what I need.
Basically I need to combine two querysets from different models into a third queryset, not a list. They share some fields names, perhaps that's useful with Q
. In example:
queryset_1 = Cars.objects.all().filter(color='red')
queryset_2 = Horses.objects.all()
queryset_3 = queryset_1 + queryset_2
queryset_3.order_by('date')
You can't do it at the database or queryset level unfortunately because those two things don't exist in the same database table. You can do it on the python side (though it's slower and more computationally intensive).
Assuming that both Cars and Horses have a "date" attribute, you can do this:
cars = Cars.objects.all().filter(color='red')
horses = Horses.objects.all()
all_things = list(cars) + list(horses)
sorted_things = sorted(all_things, key=lambda x: x.date)
Another option (which is inefficient on the database level) would be to make them all inherit from the same non-abstract model.
class Item(models.Model):
date = models.DateTimeFiedl()
item_type = models.CharField(max_length=255)
def get_instance(self):
if self.item_type = 'car':
return Car.objects.get(id=self.id)
elif self.item_type == 'horse':
return Horse.objects.get(id=self.id)
class Car(Item):
color = models.CharField(max_length=12)
def save(self, *args, **kwargs):
self.item_type = 'car'
super(Car, self).save(*args, **kwargs)
class Horse(Item):
breed = models.CharField(max_length=25)
def save(self, *args, **kwargs):
self.item_type = 'horse'
super(Horse, self).save(*args, **kwargs)
With that, you can do
items = Item.objects.all().order_by('date')
for item in items:
print(type(item)) # Always "Item"
actual_item = item.get_instance()
if type(actual_item) == Car:
print("I am a car")
else:
print("I am a horse")
While iterating through them grab each specific item as needed (similar to how Wagtail handles pages, you make a convenience method for grabbing an object based on its parent class)