Django QuertySet.annotate() 收到非表达式 - 如何添加基于模型字段的派生字段?
Django QuertySet.annotate() received non-expression - how to add a derived field based on model field?
第一次使用 Django。尝试向查询集添加注释:
class EnrollmentManager(models.Manager.from_queryset(EnrollmentCustomQuerySet)):
COURSE_DURATION = datetime.timedelta(days=183)
def get_queryset(self):
"""Overrides the models.Manager method"""
lookback = make_aware(datetime.datetime.today() - self.COURSE_DURATION)
qs = super(EnrollmentManager, self).get_queryset().annotate( \
is_expired=(Value(True)), output_field=models.BooleanField())
return qs
目前我只是想在返回的查询集上添加一个额外的 'calculated' 字段,它被硬编码为 True,attribute/field 应该被称为 is_expired
。
如果我能让它起作用,那么 Value(True) 需要是基于此表达式的派生值:
F('enrolled') < lookback
但是由于 'enrolled' 是一个数据库字段并且 lookback
是计算出来的,我将如何做到这一点?
备注
我试过了,它执行时没有抛出错误:
qs = super(EnrollmentManager, self).get_queryset().annotate( \
is_expired=(Value(True, output_field=models.BooleanField())))
在 shell 我可以看到它:
Enrollment.objects.all()[0].is_expired -> returns True
我可以将它添加到序列化程序中:
class EnrollmentSerializer(serializers.ModelSerializer):
is_active = serializers.SerializerMethodField()
is_current = serializers.SerializerMethodField()
is_expired = serializers.SerializerMethodField()
COURSE_DURATION = datetime.timedelta(days=183)
class Meta:
model = Enrollment
fields = ('id', 'is_active', 'is_current', 'is_expired')
def get_is_expired(self, obj):
return obj.is_expired
所以这是可能的...但是我怎样才能用计算替换我的硬编码“真”?
更新
阅读 documentation,它指出:
"Annotates each object in the QuerySet with the provided list of query expressions. An expression may be a simple value, a reference to a field on the model (or any related models), or an aggregate expression (averages, sums, etc.) that has been computed over the objects that are related to the objects in the QuerySet."
一个简单的值 - 那么,不是一个简单的计算值吗?
这让我觉得这是不可能的...
这似乎是 Case
表达式的一个很好的用例。我建议您尽可能熟悉 these expression tools,它们非常有用!
我还没有测试过,但应该可以。我假设 enrolled 是他们第一次注册时的 tz 感知日期时间...
from django.db.models import Case, When, Value
def get_queryset(self):
"""Overrides the models.Manager method"""
lookback = make_aware(datetime.datetime.today() - self.COURSE_DURATION)
qs = super(EnrollmentManager, self).get_queryset().annotate(
is_expired=Case(
When(
enrolled__lt=lookback,
then=Value(True)
),
default=Value(False),
output_field=models.BooleanField()
)
)
您也不必预先计算回顾变量。查看解决此问题的 ExpressionWrappers 和 。
ExpressionWrapper(
TruncDate(F('date1')) + datetime.timedelta(days=365),
output_field=DateField(),
)
第一次使用 Django。尝试向查询集添加注释:
class EnrollmentManager(models.Manager.from_queryset(EnrollmentCustomQuerySet)):
COURSE_DURATION = datetime.timedelta(days=183)
def get_queryset(self):
"""Overrides the models.Manager method"""
lookback = make_aware(datetime.datetime.today() - self.COURSE_DURATION)
qs = super(EnrollmentManager, self).get_queryset().annotate( \
is_expired=(Value(True)), output_field=models.BooleanField())
return qs
目前我只是想在返回的查询集上添加一个额外的 'calculated' 字段,它被硬编码为 True,attribute/field 应该被称为 is_expired
。
如果我能让它起作用,那么 Value(True) 需要是基于此表达式的派生值:
F('enrolled') < lookback
但是由于 'enrolled' 是一个数据库字段并且 lookback
是计算出来的,我将如何做到这一点?
备注
我试过了,它执行时没有抛出错误:
qs = super(EnrollmentManager, self).get_queryset().annotate( \
is_expired=(Value(True, output_field=models.BooleanField())))
在 shell 我可以看到它:
Enrollment.objects.all()[0].is_expired -> returns True
我可以将它添加到序列化程序中:
class EnrollmentSerializer(serializers.ModelSerializer):
is_active = serializers.SerializerMethodField()
is_current = serializers.SerializerMethodField()
is_expired = serializers.SerializerMethodField()
COURSE_DURATION = datetime.timedelta(days=183)
class Meta:
model = Enrollment
fields = ('id', 'is_active', 'is_current', 'is_expired')
def get_is_expired(self, obj):
return obj.is_expired
所以这是可能的...但是我怎样才能用计算替换我的硬编码“真”?
更新
阅读 documentation,它指出:
"Annotates each object in the QuerySet with the provided list of query expressions. An expression may be a simple value, a reference to a field on the model (or any related models), or an aggregate expression (averages, sums, etc.) that has been computed over the objects that are related to the objects in the QuerySet."
一个简单的值 - 那么,不是一个简单的计算值吗?
这让我觉得这是不可能的...
这似乎是 Case
表达式的一个很好的用例。我建议您尽可能熟悉 these expression tools,它们非常有用!
我还没有测试过,但应该可以。我假设 enrolled 是他们第一次注册时的 tz 感知日期时间...
from django.db.models import Case, When, Value
def get_queryset(self):
"""Overrides the models.Manager method"""
lookback = make_aware(datetime.datetime.today() - self.COURSE_DURATION)
qs = super(EnrollmentManager, self).get_queryset().annotate(
is_expired=Case(
When(
enrolled__lt=lookback,
then=Value(True)
),
default=Value(False),
output_field=models.BooleanField()
)
)
您也不必预先计算回顾变量。查看解决此问题的 ExpressionWrappers 和
ExpressionWrapper(
TruncDate(F('date1')) + datetime.timedelta(days=365),
output_field=DateField(),
)