让我们想象一下我有这个模型,我想通过逻辑操作n1 != n2
对它们进行排序:
class Thing(Model):
n1 = IntegerField()
n2 = IntegerField()
...
def is_different(self):
return self.n1 != self.n2
如果我按sorted
内置函数对它们进行排序,我发现它不会返回Queryset,而是返回一个列表:
things = Thing.objects.all()
sorted_things = sorted(things, key=lambda x: x.is_different())
现在,如果我使用annotate
sorted_things = things.annotate(diff=(F('n1') != F('n2'))).order_by('diff')
它引发了以下错误:AttributeError: 'bool' object has no attribute 'resolve_expression'
。
我找到了使用extra
queryset的解决方案:
sorted_things = things.extra(select={'diff': 'n1!=n2'}).order_by('diff')
但是遵循Django文档(https://docs.djangoproject.com/en/2.0/ref/models/querysets/#extra):
使用此方法作为最后的手段
这是一个旧的API,我们打算在将来的某个时候弃用。仅当您无法使用其他查询集方法表达查询时才使用它。如果您确实需要使用它,请使用QuerySet.extra关键字和您的用例提交票证(请先检查现有票证列表),以便我们可以增强QuerySet API以允许删除extra()。我们不再改进或修复此方法的错误。
然后,最佳方法是什么?
谢谢!
答案 0 :(得分:2)
您可以使用Func() expressions处理它。
from django.db.models import Func, F
class NotEqual(Func):
arg_joiner = '<>'
arity = 2
function = ''
things = Thing.objects.annotate(diff=NotEqual(F('n1'), F('n2'))).order_by('diff')
答案 1 :(得分:2)
它的一个选择是使用conditional expressions。它们提供了简单的方法来检查条件并根据它们提供一个值。在你的情况下,它看起来像:
sorted_things = things.annotate(diff=Case(When(n1=F('n2'), then=True), default=False, output_field=BooleanField())).order_by('diff')
通过结合使用Q
和ExpressionWrapper
,可以实现另一种方法。
在django中,Q
旨在在filter()
,exclude()
,Case
等内部使用,但它只会创建显然可以在任何地方使用的条件。它只有一个缺点:它没有定义输出的类型(它总是布尔值,而django可以假设在每种情况下都要使用Q
。
但ExpressionWrapper
允许你包装任何表达式并定义它的最终输出类型。这样我们就可以使用Q
,Q
和括号简单地包装&
表达式(或多个|
表达式,并手动定义它输出的类型。< / p>
请注意,这是未记录的,所以此行为可能会在将来发生变化,但我已使用django版本1.8,1.11和2.0进行了检查,并且工作正常
示例:
sorted_things = things.annotate(diff=ExpressionWrapper(Q(n1=F('n2')), output_field=BooleanField())).order_by('diff')