通过ManyToManyField = Value订购django查询集

时间:2016-05-04 15:46:34

标签: python django django-models

如果有一些模型如:

class Tag(models.Model):
    name = models.CharField()

class Thing(models.Model):
    title = models.CharField()
    tags = models.ManyToManyField(Tag)

我可以做一个过滤器:

Thing.objects.filter(tags__name='foo')
Thing.objects.filter(tags__name__in=['foo', 'bar'])

但是可以在标签值上订购查询集吗?

Thing.objects.order_by(tags__name='foo')
Thing.objects.order_by(tags__name__in=['foo','bar'])

在这个例子中,我期望(或者喜欢)将是所有东西模型,但是在他们拥有我知道的标签/标签的地方进行排序。我不想过滤掉它们,但要把它们带到最顶层。

我收集这可以使用FIELD运算符,但似乎我只能使它在该模型表中的列上工作,例如标题,但不在链表上。

谢谢!

编辑:在接受以下解决方案后,我意识到了它的错误/限制。

如果某个特定的Thing有多个标签,那么(由于在SQL中幕后完成左连接),它将为该事件生成一个条目,用于它拥有的每个标签。对于匹配与否匹配的每个标记,使用True或False。

在查询集中添加.distinct()只会略有帮助,每个Thing限制为最多2行(即一个标记= True,一个标记= False)。

我知道我需要在SQL中做什么,这是MAX()CASE(),然后是GROUP BY Thing的主键,这意味着每个Thing会得到一行,如果有的话已经有任何标记匹配,标记为True(否则为False)。

我认为人们通常会实现这种的方式是使用.values()这样:

Thing.objects.values('pk').annotate(tagged=Max(Case(...)))

但结果只是pk和标记,我需要整个Thing模型作为结果。所以我设法实现了我想要的目标,因此:

from django.db.models import Case, When, Max, BooleanField

tags = ['music'] # for example

queryset = Thing.objects.all().annotate(tagged=Max(Case(
    When(tags__name__in=tags, then=True),
    default=False,
    output_field=BooleanField()
)))
queryset.query.group_by = ['pk']
queryset.order_by('-tagged')

这似乎有效,但按机制分组感觉很怪异/ hacky。以这种方式分组是否可接受/可靠?

对不起史诗更新:(

1 个答案:

答案 0 :(得分:4)

我尝试使用conditional值注释查询,当标记位于您提供的列表中时,该值会变为

from django.db.models import Case, When, IntegerField

Thing.objects.annotate(tag_is_known=Case(
    When(tags__name__in=['foo', 'bar'], then=1),
    default=0,
    output_field=IntegerField()
))

接下来,我们使用我们称之为tag_is_known的注释与order_by()进行排序:

Thing.objects.annotate(tag_is_known=...).order_by('tag_is_known')

布尔版

Thing.objects.annotate(tag_is_known=Case(
    When(tags__name__in=['foo', 'bar'], then=True),
    default=False,
    output_field=BooleanField()
))