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

Cau*_*ons 14 python django django-queryset

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

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

result = queryset_1 | queryset_2
Run Code Online (Sandbox Code Playgroud)

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

result = list(itertools.chain(queryset_1, queryset_2))
Run Code Online (Sandbox Code Playgroud)

你觉得我该怎么办?

小智 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', ...)
)
Run Code Online (Sandbox Code Playgroud)

  • 这样做的另一个好处是允许过滤结果查询集。IMO,这是迄今为止的最佳答案! (2认同)

小智 6

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

result = list(queryset_1) + list(queryset_2)
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我正在对拆分为多个查询的同一模型进行复杂的搜索。每个都检索匹配特定条件的记录,并且每个都以特定方式排序。结果必须包括来自每个查询集的结果,并且必须保持每个查询集的顺序。因此,我不能在这里使用 `Q` 对象,因为我不允许在同一个查询上执行多个 `order_by()`。我想避免在每个查询集上调用 `list()` 以避免访问数据库,在内存中获取太多对象。 (2认同)

Ori*_*uce 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')
Run Code Online (Sandbox Code Playgroud)

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