如何链接Django查询集保留个别订单

时间:2013-08-14 15:08:06

标签: python django django-queryset

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

Queryset join :不保留单个查询集中的排序,因此我无法使用它。

result = queryset_1 | queryset_2

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

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

你觉得我该怎么办?

8 个答案:

答案 0 :(得分:6)

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

result = list(queryset_1) + list(queryset_2)

如果它们是同一型号,您应该使用Q object和' order_by(" queryset_1字段"," queryset_2字段")&组合查询#39;

正确答案在很大程度上取决于您希望将这些结合起来以及如何使用结果的原因。

答案 1 :(得分:3)

对于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

答案 2 :(得分:2)

以下是对我来说更好的解决方案:  (它可以防止重复

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', ...)

答案 3 :(得分: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

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

答案 4 :(得分: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)非空状态排在第二位,按国家/地区排序
  3. 我想合并查询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
    

    它可能效率不高,但它有效:)。

答案 5 :(得分:0)

如果两个查询集具有公共字段,则可以按该字段排序组合的查询集。在执行此操作期间不会评估查询集。

例如:

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')

答案 6 :(得分:0)

因此,受Peter's answer的启发,这就是我在项目中所做的(顺便说一下,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')

有了这个,我可以按需要对结果联合进行排序,而无需更改任何私有属性,而只评估一次查询集。

答案 7 :(得分:0)

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