Django:无法使用预取计算属性进行注释

Django: cannot annotate using prefetch calculated attribute

目标是对每个员工在给定时间范围内的工作时间进行求和和注释。

型号:

class Employee(models.Model):
    first_name = models.CharField(max_length=64)

class WorkTime(models.Model):
    employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name="work_times")

    work_start = models.DateTimeField()
    work_end = models.DateTimeField()
    work_delta = models.IntegerField(default=0)

    def save(self, *args, **kwargs):
        self.work_delta = (self.work_end - self.work_start).seconds
        super().save(*args, **kwargs)

获取每个员工在给定日期范围内的工作时间:

queryset = Employee.objects.prefetch_related(
                Prefetch(
                    'work_times',
                    queryset=WorkTime.objects.filter(work_start__date__range=("2021-03-01", "2021-03-15"]))
                        .order_by("work_start"),
                    to_attr="filtered_work_times"
                )).all()

尝试向每个员工注释 work_delta 的总和:

queryset.annotate(work_sum=Sum("filtered_work_times__work_delta"))

这会导致 FieldError:

Cannot resolve keyword 'filtered_work_times' into field. Choices are: first_name, id, work_times

如何从这里开始?使用 Django 3.1 顺便说一句。

您不能在查询中使用 prefetch_related 值,因为预取只是 单独完成 ,Django 将首先获取当前对象,然后进行查询以获取相关对象,因此您尝试引用的字段甚至不是您要将其添加到的查询的一部分。

不要这样做,只需向聚合函数添加一个 filter [Django docs] 关键字参数即可:

from django.db.models import  Q


start_date = datetime.date(2021, 3, 1)
end_date = datetime.date(2021, 3, 15)

result = queryset.annotate(work_sum=Sum("work_times__work_delta", filter=Q(work_times__work_start__date__range=(start_date, end_date))))

你应该使用 filtering on annotations。 我没试过,但我认为以下代码可能对您有所帮助:

from django.db.models import Sum, Q

Employee.objects.annotate(
    work_sum=Sum(
        'work_times__work_delta',
        filter=Q(work_times__work_start__date__range=["2021-03-01", "2021-03-15"])
    )
)