如果有一些模型如:
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。以这种方式分组是否可接受/可靠?
对不起史诗更新:(
答案 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()
))