DjangoREST通过过滤注释相关条目的数量

时间:2018-12-02 20:05:41

标签: django orm django-rest-framework

所以我在Django REST中做一个小项目,而我仍然习惯于ORM。我正在处理以下Django模型:

class Session(models.Model):
    date = models.DateTimeField(default=now, blank=False)
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='efforts', on_delete=models.CASCADE)

在给定特定月份的情况下,我对每个所有者的会话次数特别感兴趣。如下:

Session.objects.filter(date__gte=start, date__lt=end).values('owner').annotate(num_sessions=Count('owner')).order_by("owner")

此查询产生以下结构的结果:

(owner, count)

在序列化程序中,我想使用所有者检索用户名,以构建响应。我绝对没有运气。我的序列化器现在看起来像这样:

class SessionAggregateSerializer(serializers.ModelSerializer):
    num_sessions = serializers.IntegerField(read_only=True)
    owner = serializers.IntegerField(read_only=True)

    class Meta:
        model = Session
        fields = ('id', 'owner', 'num_sessions')

有关如何处理此问题的任何建议?对于常规查询,我使用以下行来检索用户名,但这在当前设置中不再适用:

owner = serializers.ReadOnlyField(source='owner.username')

2 个答案:

答案 0 :(得分:1)

我认为您可以使用SerializerMethodFielddocumentation):

class SessionAggregateSerializer(serializers.ModelSerializer):
        ...
        username = serializers.SerializerMethodField()
        class Meta:
            model = Session
            fields = ('id', 'username', 'num_sessions')

        def get_username(self, obj):
          return obj.owner.username

更新

   class UserSerializer(serializers.ModelSerializer):
        ...
        sessions = serializers.SerializerMethodField()
        class Meta:
            model = User
            fields = ('id', 'username', 'sessions')

        def get_sessions(self, obj):
          return obj.efforts.all().count()  # or obj.efforts.filter(efforts__date__gte=....).count()

答案 1 :(得分:0)

因此,使用例更加具体:

会话通过“努力”关系附加到用户。给定开始时间和结束时间,我想为用户显示一个列表,以及附加的会话数-带有开始日期和结束日期之间的日期。会话由分支进一步标识。对于用户来说是相同的,但是用户可能属于多个分支。因此,应该进一步细化计数,以将会话单独包含在出现在请求者用户对象的分支中的分支中。此外,会话计数为零的用户应省略,以保持结果简洁。

会话模型如下:

class Session(models.Model):
    date = models.DateTimeField(default=now, blank=False)
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='efforts', on_delete=models.CASCADE)
    branch = models.ForeignKey(Branch, related_name='hour_branch', on_delete=models.CASCADE)
    status = models.IntegerField(default=1, blank=False, validators=[MinValueValidator(0), MaxValueValidator(3)])

用户模型如下所示(为简洁起见,省略了各个字段)。

class User(AbstractUser):
    branches = models.ManyToManyField(Branch)

上面的答案并不令人满意,因为它不允许我根据需要过滤用户计数中的会话。因此,为什么我最终使用conditional expression使用以下解决方案:

有关视图的代码:

class AggregateSessionList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSessionsSerializer
    permission_classes = (permissions.IsAdminUser,)

    def get_queryset(self):
        today=datetime.date.today()
        month=self.request.query_params.get('month', today.month)
        year=self.request.query_params.get('year', today.year)

        start = datetime.date(year=year, month=month, day=1)
        end = datetime.datetime(year=start.year + int(start.month / 12), month=int((start.month % 12) + 1), day=1)

        branches = self.request.user.branches.all()
        return User.objects.annotate(
            sessions=Count(Case(
                When(efforts__date__gt=start, efforts__date__lt=end, efforts__branch__in=branches, then=1),
                output_field=IntegerField()))).filter(sessions__gt=0)

序列化程序的代码:

class UserSessionsSerializer(serializers.ModelSerializer):
    sessions = serializers.IntegerField()

    class Meta:
        model = User
        fields = ('username', 'sessions')

以为我会分享此解决方案,因为找不到类似的东西。

编辑

此解决方案中对ruddra的回答更受欢迎的另一个好处是,仅执行一个数据库查询。这与ruddra的答案相反,后者对每个用户执行一个附加查询(这对我的要求而言不可行)。