DjangoREST комментирует количество связанных записей с фильтрацией - PullRequest
0 голосов
/ 02 декабря 2018

Итак, я делаю небольшой проект в 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 голосов
/ 03 декабря 2018

Итак, чтобы сделать пример использования более конкретным:

Сессии присоединяются к пользователям через отношения «усилия».Учитывая время начала и окончания, я хотел отобразить список для пользователей с указанием количества сессий - с датой между датой начала и окончания - прикрепленной к ним.Сессии далее идентифицируются веткой.Это то же самое для пользователей, однако пользователи могут принадлежать к нескольким веткам.Следовательно, счетчик должен быть дополнительно детализирован, чтобы принимать только сеансы в счетчик с ветвью, которая появляется в ветвях для пользовательского объекта запросчиков.Кроме того, пользователи с числом сеансов, равным нулю, должны быть опущены, чтобы сохранить результат кратким.

Модель сеанса выглядит следующим образом:

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)

Ответ выше был неудовлетворительным, так как он не позволял мне фильтровать сеансы, которые учитываются для пользователя по желанию.Поэтому я и выбрал следующее решение, используя условное выражение :

Код для рассматриваемого представления:

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')

Думал, что поделюсь этим решением, поскольку не могу найти ничего похожего.

РЕДАКТИРОВАТЬ

Дополнительным преимуществом этого решения в пользу ответа Руддры является то, чтовыполняется только один запрос к БД.Это в отличие от ответа Руддры, где для каждого пользователя выполняется дополнительный запрос (что не представляется возможным для моих требований).

0 голосов
/ 02 декабря 2018

Я думаю, вы можете использовать SerializerMethodField ( документация ):

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()
...