如何将不同模型的两个查询集合并为一个?
How to combine two Querysets into one from different models?
我需要将来自不同模型的两个查询集合并为一个。我在网上找到的最推荐的解决方案是:
要使用 |
和 &
,但这仅适用于同一模型的查询集
要使用 chain
和 list
,但这会带走一些查询集方法,并且由于我项目中代码的结构方式,我无法更改它。我试过了,没用。
我读到了 Q,但我不清楚如何应用它来完成我需要的。
基本上我需要将来自不同模型的两个查询集组合成第三个查询集,而不是列表。他们共享一些字段名称,也许这对 Q
有用。例如:
queryset_1 = Cars.objects.all().filter(color='red')
queryset_2 = Horses.objects.all()
queryset_3 = queryset_1 + queryset_2
queryset_3.order_by('date')
不幸的是,您不能在数据库或查询集级别执行此操作,因为这两者不存在于同一数据库中table。您可以在 python 端执行此操作(尽管速度较慢且计算量更大)。
假设 Cars 和 Horses 都有一个 "date" 属性,你可以这样做:
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)
另一种选择(在数据库级别效率低下)是使它们都继承自相同的非抽象模型。
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)
有了它,你就可以做到
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")
在遍历它们时根据需要抓取每个特定项目(类似于 Wagtail 处理页面的方式,您创建了一种基于父对象抓取对象的便捷方法 class)
如果你真的需要这样的查询集,这可能是模型设计不正确的症状,任何快速修复都可能比修改模型更难以维护。我的建议是设置一个单独的模型来替换 Car 和 Horse(即使某些字段对于每种情况的实例将保持为空),并使用单独的字段让您轻松识别两者。例如:
class Vehicle(models.Model):
category = models.IntegerField(
choices=[(10, 'Horse'), (20, 'Car'), (30, 'Bicycle')]
)
# other fields here...
我需要将来自不同模型的两个查询集合并为一个。我在网上找到的最推荐的解决方案是:
要使用 |
和 &
,但这仅适用于同一模型的查询集
要使用 chain
和 list
,但这会带走一些查询集方法,并且由于我项目中代码的结构方式,我无法更改它。我试过了,没用。
我读到了 Q,但我不清楚如何应用它来完成我需要的。
基本上我需要将来自不同模型的两个查询集组合成第三个查询集,而不是列表。他们共享一些字段名称,也许这对 Q
有用。例如:
queryset_1 = Cars.objects.all().filter(color='red')
queryset_2 = Horses.objects.all()
queryset_3 = queryset_1 + queryset_2
queryset_3.order_by('date')
不幸的是,您不能在数据库或查询集级别执行此操作,因为这两者不存在于同一数据库中table。您可以在 python 端执行此操作(尽管速度较慢且计算量更大)。
假设 Cars 和 Horses 都有一个 "date" 属性,你可以这样做:
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)
另一种选择(在数据库级别效率低下)是使它们都继承自相同的非抽象模型。
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)
有了它,你就可以做到
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")
在遍历它们时根据需要抓取每个特定项目(类似于 Wagtail 处理页面的方式,您创建了一种基于父对象抓取对象的便捷方法 class)
如果你真的需要这样的查询集,这可能是模型设计不正确的症状,任何快速修复都可能比修改模型更难以维护。我的建议是设置一个单独的模型来替换 Car 和 Horse(即使某些字段对于每种情况的实例将保持为空),并使用单独的字段让您轻松识别两者。例如:
class Vehicle(models.Model):
category = models.IntegerField(
choices=[(10, 'Horse'), (20, 'Car'), (30, 'Bicycle')]
)
# other fields here...