Python 如何链接Django查询集以保持单个顺序

Python 如何链接Django查询集以保持单个顺序,python,django,django-queryset,Python,Django,Django Queryset,我想在Django中附加或链接几个QuerySet,保留每个QuerySet的顺序(而不是结果)。我使用第三方库对结果进行分页,它只接受列表或查询集。我尝试过以下几种选择: Queryset join:不保留单个Queryset中的顺序,因此我无法使用此选项 result = queryset_1 | queryset_2 使用itertools:在chain对象上调用list()实际上会计算查询集,这可能会导致大量开销。不是吗 result = list(itertools.chain(qu

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

Queryset join:不保留单个Queryset中的顺序,因此我无法使用此选项

result = queryset_1 | queryset_2
使用itertools:在chain对象上调用
list()
实际上会计算查询集,这可能会导致大量开销。不是吗

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

您认为我应该怎么做?

如果查询集属于不同的模型,您必须将它们计算到列表中,然后您可以添加:

result = list(queryset_1) + list(queryset_2)
如果它们是相同的模型,则应使用和“order_by”(“queryset_1字段”、“queryset_2字段”)组合查询


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

如果需要将两个查询集合并到第三个查询集,下面是一个示例,使用
\u result\u 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)空状态优先,按国家/地区排序
  • (查询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
    
    它可能效率不高,但可以工作:)。

    对于Django 1.11(于2017年4月4日发布),请使用union()进行此操作,文档如下:

    以下是2.1版的链接:

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

    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
    

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

    如果两个查询集具有公共字段,则可以按该字段排序组合查询集。此操作期间不计算查询集

    例如:

    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')
    
    因此,受此启发,我在我的项目(Django 2.2)中做了如下工作:

    来自django.db导入模型的
    
    从。模型导入MyModel
    #使用常量值向每个查询添加一个额外字段
    queryset_0=MyModel.objects.annotate(
    qs_order=models.Value(0,models.IntegerField())
    )
    #每个常数基本上都应该作为我们想要
    #求你留下来
    queryset_1=MyModel.objects.annotate(
    qs_order=models.Value(1,models.IntegerField())
    )
    [...]
    queryset\u n=MyModel.objects.annotate(
    qs_order=models.Value(n,models.IntegerField())
    )
    #最后,我通过那个额外的字段对联合结果进行排序。
    union=queryset_0.union(
    queryset_1,
    queryset_2,
    [...], 
    查询设置(n).订购人('qs\U order')
    

    有了它,我可以根据需要对结果联合进行排序,而无需更改任何私有属性,同时只对查询集求值一次。

    此解决方案可防止重复:

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

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

    我正在对拆分为多个查询的同一模型进行复杂搜索。每个都检索符合特定条件的记录,并且每个记录都以特定的方式排序。结果必须包括每个查询集的结果,并且必须保持每个查询集的顺序。因此,我不能在这里使用
    Q
    对象,因为我不允许对同一个查询执行多个
    order\u by()
    。我希望避免对每个查询集调用
    list()
    ,以避免访问数据库,从而在内存中获取过多的对象。您认为是否可以编写一个纯SQL查询,该查询将返回一组完全按照您的需要排序和筛选的行?如果不是,那么一个查询集也不能做到这一点。例如,如果以不兼容的方式排列两个结果集。如果您使用来自两个顺序明确的结果集的复杂联接来解决此不兼容问题,那么django ORM无法做到这一点。我不想像使用django的ORM那样深入纯SQL。我问这个问题只是想看看是否有更好的方法来替代我正在做的事情(目前使用的是第二个示例),即使用有限的查询来避免内存中有数千个对象。我不是建议您使用纯SQL。对于“在没有复杂联接的情况下是否可以进行单个SQL查询”这一问题的答案与“是否可以进行单个查询集”的答案相同。如果您可以编写这样一个查询,您可以考虑如何编写类似的QS。是的,我可以用SQL进行查询,但我会使用SQL联合(虽然我不知道它们是否保持顺序),这似乎比在两个查询集上调用list()要高效得多。如果你迭代最小的,这似乎更好。我不喜欢的是修改私有属性\u result\u缓存。。。您确定这是安全的吗?此方法仍将评估所有查询集,这与创建问题作者不希望看到的查询集列表相同。向下投票,因为这不会保留查询集的顺序。但这不是作者希望看到的。他不想按单个字段排序。由于每个子查询都是以特定的方式选择的,因此他希望保持每个子查询使用的顺序。我现在面临着同样的问题。这会导致不一致的结果。这种方法会给我带来麻烦。应用联合后,无法从查询中筛选或获取值。\u列表。是的。。。这就是工会的问题所在。正如docs()中所述,您不能对结果查询集进行筛选。您必须在联合之前进行筛选…联合的行为是删除重复项。但是在应用了这个注释之后,重复的命令出现了。@SandeepBalagopal,因为你必须使用UNION ALL,但我认为Django ORM没有这个。。。如果我曾经有过这个用例,我想我将不得不放弃