如何将来自不同模型的两个查询集组合成一个?

Posted

技术标签:

【中文标题】如何将来自不同模型的两个查询集组合成一个?【英文标题】:How to combine two Querysets into one from different models? 【发布时间】:2018-03-30 20:23:50 【问题描述】:

我需要将来自不同模型的两个查询集合并为一个。我在网上找到的建议最多的解决方案是:

使用|&,但这仅适用于来自同一模型的查询集

要使用chainlist,但这会带走一些查询集方法,并且由于我项目中代码的结构方式,我无法更改它。我试过了,还是不行。

我读到了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')

【问题讨论】:

你不能这样做。根据定义,查询集是来自单个模型的一组对象。 【参考方案1】:

不幸的是,您不能在数据库或查询集级别执行此操作,因为这两个内容不存在于同一个数据库表中。您可以在 python 端执行此操作(尽管它更慢且计算量更大)。

假设 Cars 和 Horses 都有“日期”属性,您可以这样做:

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 处理页面的方式,您可以创建一个方便的方法来根据其父类抓取对象)

【讨论】:

【参考方案2】:

如果您真的需要这样的查询集,这可能是模型设计不正确的症状,任何快速修复都可能比重新设计模型更繁琐。我的建议是设置一个模型来替换 Car 和 Horse(即使某些字段对于每个案例的实例都将保持为空),并使用单独的字段让您轻松识别两者。例如:

class Vehicle(models.Model):
    category = models.IntegerField(
        choices=[(10, 'Horse'), (20, 'Car'), (30, 'Bicycle')]
    )
    # other fields here...

【讨论】:

你的意思,我认为,叫做数据库规范化:en.wikipedia.org/wiki/Database_normalization 正确!就是这样。 我的解决方案是您建议的方法和使用列表的混合。但是您的评论是对的,该项目在代码组织方面以一种相当奇怪的方式发展。

以上是关于如何将来自不同模型的两个查询集组合成一个?的主要内容,如果未能解决你的问题,请参考以下文章

如何编写猫鼬查询来组合来自两个模型的数据?

SQLAlchemy Flask 过滤器查询以组合来自两个模型的结果

将多个 Falcor 数据源组合成单个模型

将来自不同模型的两个查询集合并为一个查询集

如何组合两个查询并将数据放入一行?

使用javascript(客户端)将来自两个不同视频的两个剪辑组合成一个视频?