Django / DRF: аннотируйте модель дополнительной (вычисляемой) информацией

Я добавляю на свой сайт очень простую функцию голосования по дорожной карте, где люди могут добавлять запросы на функции, а затем голосовать за предложения друг друга. Основы довольно просты:

# models.py
class FeatureRequest(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField()
    author = models.ForeignKey(User, editable=False, on_delete=models.CASCADE)
    is_implemented = models.BooleanField(default=False, editable=False, db_index=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)


class Vote(models.Model):
    feature = models.ForeignKey(FeatureRequest, editable=False, on_delete=models.CASCADE)
    user = models.ForeignKey(User, editable=False, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ["feature", "user"]

#views.py
class RoadmapController(viewsets.ModelViewSet):
    permission_classes = (AuthorOrReadOnlyPermission,)
    serializer_class = FeatureRequestSerializer

    def get_queryset(self):
        return FeatureRequest.objects.filter(is_implemented=False)

    def perform_create(self, serializer):
        return serializer.save(author=self.request.user)

# serializers.py
class FeatureRequestSerializer(serializers.ModelSerializer):
    class Meta:
        model = FeatureRequest
        fields = "__all__"

Существует также отдельное представление для фактического размещения и удаления голосов, но это не нужно для моего вопроса.

Я хочу, чтобы ответ списка запросов функций включал количество голосов за каждую из них и булево значение, если пользователь, вошедший в систему, проголосовал за нее:

[
  {
    "title": "This is a feature request", 
    "description": "Foo bar", 
    "author": 1, 
    "number_of_votes": 1, 
    "has_my_vote": true
  }
]

Я уже понял, что могу изменить свой кверисет на FeatureRequest.objects.filter(is_implemented=False).annotate(number_of_votes=Count("vote")), добавить number_of_votes = serializers.ReadOnlyField() в сериализатор, и количество голосов будет видно. Это лучший способ? Я предполагаю, что он добавляет запрос для каждого запроса функции.

Но больше всего я не знаю, как добавить булево "has_my_vote" к результату (и как сохранить его работоспособность, конечно).

После некоторых проб и ошибок я пришел к следующему:

    def get_queryset(self):
        return (
            FeatureRequest.objects.filter(is_implemented=False)
            .annotate(number_of_votes=Count("vote"))
            .annotate(has_my_vote=Exists(Vote.objects.filter(feature=OuterRef("pk"), user=self.request.user)))
        )

Это работает, и делает только один запрос вместо одного плюс один или два в соответствии с запросом функции.

Вернуться на верх