28

我想在 Django 中附加或链接几个查询集,保留每个查询集的顺序(而不是结果)。我正在使用第三方库对结果进行分页,它只接受列表或查询集。我试过这些选项:

Queryset join:不保留单个查询集中的顺序,所以我不能使用它。

result = queryset_1 | queryset_2

使用 itertools:调用list()链对象实际上会评估查询集,这可能会导致大量开销。不是吗?

result = list(itertools.chain(queryset_1, queryset_2))

你觉得我应该怎么走?

4

8 回答 8

13

此解决方案可防止重复:

q1 = Q(...)
q2 = Q(...)
q3 = Q(...)
qs = (
    Model.objects
    .filter(q1 | q2 | q3)
    .annotate(
        search_type_ordering=Case(
            When(q1, then=Value(2)),
            When(q2, then=Value(1)),
            When(q3, then=Value(0)),
            default=Value(-1),
            output_field=IntegerField(),
        )
    )
    .order_by('-search_type_ordering', ...)
)
于 2019-10-28T15:06:41.163 回答
9

如果查询集属于不同的模型,您必须将它们评估为列表,然后您可以追加:

result = list(queryset_1) + list(queryset_2)

如果它们是相同的模型,您应该使用Q 对象和 'order_by("queryset_1 field", "queryset_2 field")' 组合查询。

正确的答案很大程度上取决于您为什么要结合这些以及您将如何使用这些结果。

于 2013-08-14T15:58:42.867 回答
5

因此,受彼得回答的启发,这就是我在项目(Django 2.2)中所做的:

from django.db import models
from .models import MyModel

# Add an extra field to each query with a constant value
queryset_0 = MyModel.objects.annotate(
    qs_order=models.Value(0, models.IntegerField())
)

# Each constant should basically act as the position where we want the 
# queryset to stay
queryset_1 = MyModel.objects.annotate(
    qs_order=models.Value(1, models.IntegerField()) 
)

[...]

queryset_n = MyModel.objects.annotate(
    qs_order=models.Value(n, models.IntegerField()) 
)

# Finally, I ordered the union result by that extra field.
union = queryset_0.union(
    queryset_1, 
    queryset_2, 
    [...], 
    queryset_n).order_by('qs_order')

有了这个,我可以在不更改任何私有属性的情况下根据需要对生成的联合进行排序,同时只评估一次查询集。

于 2019-07-09T21:22:37.327 回答
1

我不是 100% 确定这个解决方案在所有可能的情况下都有效,但看起来结果是两个 QuerySet(在同一模型上)的并集,保留了第一个的顺序:

union = qset1.union(qset2)
union.query.extra_order_by = qset1.query.extra_order_by
union.query.order_by = qset1.query.order_by
union.query.default_ordering = qset1.query.default_ordering
union.query.get_meta().ordering = qset1.query.get_meta().ordering

我没有对它进行广泛的测试,因此在您在生产中使用该代码之前,请确保它的行为符合预期。

于 2019-01-13T23:23:03.230 回答
0

如果您需要将两个查询集合并到第三个查询集,这里是一个示例,使用_result_cache.

模型

class ImportMinAttend(models.Model):
    country=models.CharField(max_length=2, blank=False, null=False)
    status=models.CharField(max_length=5, blank=True, null=True, default=None)

在这个模型中,我想显示所有行的列表,例如:

  1. (查询1)空状态先行,按国家排序
  2. (查询 2)非空状态排在第二位,按国家排序

我想合并查询 1 和查询 2。

    #get all the objects
    queryset=ImportMinAttend.objects.all()

    #get the first queryset
    queryset_1=queryset.filter(status=None).order_by("country")
    #len or anything that hits the database
    len(queryset_1)

    #get the second queryset
    queryset_2=queryset.exclude(status=None).order_by("country")

    #append the second queryset to the first one AND PRESERVE ORDER
    for query in queryset_2:
         queryset_1._result_cache.append(query)

    #final result
    queryset=queryset_1

它可能不是很有效,但它有效:)。

于 2014-04-08T22:09:45.170 回答
0

union() 函数将多个查询集组合在一起,而不是or( |) 运算符。这避免了读取整个表的非常低效的 OUTER JOIN 查询。

于 2020-06-22T11:09:50.170 回答
0

对于 Django 1.11(2017 年 4 月 4 日发布)使用 union() ,文档在这里:

https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.union

这是版本 2.1 的链接: https ://docs.djangoproject.com/en/2.1/ref/models/querysets/#union

于 2017-04-20T15:37:56.773 回答
-1

如果两个查询集有共同的字段,您可以按该字段排序组合查询集。在此操作期间不评估查询集。

例如:

class EventsHistory(models.Model):
    id = models.IntegerField(primary_key=True)
    event_time = models.DateTimeField()
    event_id = models.IntegerField()

class EventsOperational(models.Model):
    id = models.IntegerField(primary_key=True)
    event_time = models.DateTimeField()
    event_id = models.IntegerField()

qs1 = EventsHistory.objects.all()
qs2 = EventsOperational.objects.all()

qs_combined = qs2.union(qs1).order_by('event_time')
于 2019-02-19T09:10:58.040 回答