Django QuertySet.annotate()接收到非表达式-如何基于模型字段添加派生字段?

Django QuertySet.annotate()接收到非表达式-如何基于模型字段添加派生字段?,django,django-rest-framework,django-queryset,Django,Django Rest Framework,Django Queryset,第一次和Django在一起。正在尝试向queryset添加批注: class EnrollmentManager(models.Manager.from_queryset(EnrollmentCustomQuerySet)): COURSE_DURATION = datetime.timedelta(days=183) def get_queryset(self): """Overrides the models.Manager method""" lookba

第一次和Django在一起。正在尝试向queryset添加批注:

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
目前,我正试图在返回的queryset上添加一个额外的“computed”字段,该字段硬编码为True,属性/字段应被称为
已过期

如果我可以让它工作,那么Value(True)需要是基于以下表达式的派生值:

F('enrolled') < lookback
在贝壳里我可以看到:

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
所以这是可能的……但我如何用计算替换硬编码的“真”呢

更新

在阅读报告时,它指出:

“使用提供的查询表达式列表注释QuerySet中的每个对象。表达式可以是简单值、对模型(或任何相关模型)上字段的引用,也可以是在与查询集中的对象相关的对象上计算的聚合表达式(平均值、总和等)。”

一个简单的值-那么,不是一个简单的计算值吗


这让我觉得这是不可能的…

这似乎是一个非常好的
案例
表达式的用例。我建议您尽可能熟悉它,它们非常有用

我还没有测试过这个,但它应该可以工作。我假设他们第一次注册的时候,注册是一个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()
        )
    )
您也不必预先计算lookback变量。请查看ExpressionWrappers,它解决了这个问题

ExpressionWrapper(
    TruncDate(F('date1')) + datetime.timedelta(days=365),
    output_field=DateField(),
)
ExpressionWrapper(
    TruncDate(F('date1')) + datetime.timedelta(days=365),
    output_field=DateField(),
)