我有一个过滤器,该过滤器应返回带有2个对象的查询集,并且应具有一个不同的字段。例如:
obj_1 = (name='John', age='23', is_fielder=True)
obj_2 = (name='John', age='23', is_fielder=False)
这两个对象的模型相同,但是主键不同。我尝试使用以下过滤器:
qs = Model.objects.filter(name='John', age='23').annotate(is_fielder=F('plays__outdoor_game_role')=='Fielder')
我第一次使用注释,但这给了我以下错误:
TypeError: QuerySet.annotate() received non-expression(s): False.
我是Django的新手,所以我在做错什么,获取上面所示的必需对象的注释应该是什么?
答案 0 :(得分:0)
您可以将Case/When
与annotate
一起使用
from django.db.models import Case, BooleanField, Value, When
Model.objects.filter(name='John', age='23').annotate(
is_fielder=Case(
When(plays__outdoor_game_role='Fielder', then=Value(True)),
default=Value(False),
output_field=BooleanField(),
),
)
答案 1 :(得分:0)
@ktowen 的解决方案运行良好,非常简单。 这是我正在使用的另一个解决方案,希望它也有帮助。
queryset = queryset.annotate(is_fielder=ExpressionWrapper(
Q(plays__outdoor_game_role='Fielder'),
output_field=BooleanField(),
),)
这里给不熟悉 Django ORM 的人一些解释:
注释 动态创建一个新列/字段,在本例中为 is_fielder
。这意味着您的模型中没有名为 is_fielder
的字段,而您可以在添加此“注释”后像 plays.outdor_game_role.is_fielder
一样使用它。 Annotate
非常有用且灵活,几乎可以与其他所有表达式结合使用,应该是 Django ORM 中的必备方法。
ExpressionWrapper 基本上为您提供了空间来包装更复杂的条件组合,以类似 ExpressionWrapper(expression, output_field)
的格式使用。当您组合不同类型的字段或想要指定输出类型时,它很有用,因为 Django 无法自动判断。
Q object 是一个常用的指定条件的表达式,我认为最强大的部分是可以链接条件:
filter(Q(condition1) & Q(condition2))
filter(Q(condition1) | Q(condition2))
filter(~Q(condition))
可以在如下正常条件下使用 Q:
(Q(condition1)|id__in=[list])
重点是Q对象必须排在第一位,否则将无法工作。
Case When(then) 可以简单地解释为 if con1 elif con2 elif con3 ...
。它非常强大,而且我个人喜欢用它来自定义查询集的排序对象。
例如,您需要返回一个观看历史条目的查询集,这些条目必须按照用户观看的顺序排列。您可以使用 for 循环来保持顺序,但这会生成大量类似的查询。 Case When 的一种更优雅的方式是:
item_ids = [list]
ordering = Case(*[When(pk=pk, then=pos)
for pos, pk in enumerate(item_ids)])
watch_history = Item.objects.filter(id__in=item_ids)\
.order_by(ordering)
如您所见,通过使用 Case When(then)
可以绑定那些非常具体的关系,可以将其视为 1) 精确/精确的条件表达式和 2) 在连续多个条件的情况下特别有用。