Dav*_*nes 4 python django django-models
如果有一些模型,如:
class Tag(models.Model):
name = models.CharField()
class Thing(models.Model):
title = models.CharField()
tags = models.ManyToManyField(Tag)
Run Code Online (Sandbox Code Playgroud)
我可以做一个过滤器:
Thing.objects.filter(tags__name='foo')
Thing.objects.filter(tags__name__in=['foo', 'bar'])
Run Code Online (Sandbox Code Playgroud)
但是是否可以在标签值上订购查询集?
Thing.objects.order_by(tags__name='foo')
Thing.objects.order_by(tags__name__in=['foo','bar'])
Run Code Online (Sandbox Code Playgroud)
在这个例子中,我期望(或喜欢)的是所有事物模型,但在它们有我知道的标签/标签的地方订购。我不想过滤掉它们,而是将它们带到顶部。
我认为使用 FIELD 运算符是可能的,但似乎我只能使它在该模型表中的列上工作,例如标题,但不能在链接表上工作。
谢谢!
编辑:接受以下解决方案后,我意识到它的一个错误/限制。
如果一个特定的事物有多个标签,那么(由于在 SQL 的幕后进行了左连接)它将为该事物生成一个条目,为它拥有的每个标签。每个匹配或不匹配的标签都有一个真或假。
将 .distinct() 添加到查询集仅略有帮助,限制为每个事物最多 2 行(即一个 tagged=True,一个 tagged=False)。
我知道我需要在 SQL 中做什么,即 MAX() 到 CASE(),然后 GROUP BY Thing 的主键,这意味着我将为每个 Thing 获得一行,如果有任何标签匹配,则标记将为 True(否则为 False)。
我看到的方式,人们通常达到这个样的事情是使用.values()是这样的:
Thing.objects.values('pk').annotate(tagged=Max(Case(...)))
Run Code Online (Sandbox Code Playgroud)
但结果只是 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')
Run Code Online (Sandbox Code Playgroud)
这似乎有效,但按机制分组感觉很奇怪/hacky。以这种方式分组是否可以接受/可靠?
对不起,史诗更新:(
我会尝试使用条件值注释查询,当标签在您提供的列表中时该条件值变为真
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()
))
Run Code Online (Sandbox Code Playgroud)
接下来我们使用我们调用的注解tag_is_known来排序order_by():
Thing.objects.annotate(tag_is_known=...).order_by('tag_is_known')
Run Code Online (Sandbox Code Playgroud)
布尔版本
Thing.objects.annotate(tag_is_known=Case(
When(tags__name__in=['foo', 'bar'], then=True),
default=False,
output_field=BooleanField()
))
Run Code Online (Sandbox Code Playgroud)