Django - агрегирование запросов по дате и среднему значению - PullRequest
0 голосов
/ 28 февраля 2019

У меня есть приложение, в котором Gym связано со многими Surveys (многие-ко-многим), а Survey имеет множество Answers.

. Answer имеетчисловой value, и он связан с Survey и Gym.

models.py

class DateTimeModel(models.Model):
    creation_date = models.DateTimeField(verbose_name=_('Creation Date'), auto_now_add=True, db_index=True)
    edit_date = models.DateTimeField(verbose_name=_('Last Edit Date'), auto_now=True, db_index=True)
    ...


class Gym(DateTimeModel):
    name = models.CharField(max_length=250)
    ...


class Survey(DateTimeModel):
    text = models.CharField(max_length=500)
    valid_from = models.DateTimeField()
    valid_to = models.DateTimeField()
    gyms = models.ManyToManyField(Gym)
    ...


class Answer(DateTimeModel):
    value = models.IntegerField()
    survey = models.ForeignKey(Survey, on_delete=models.CASCADE)
    gym = models.ForeignKey(Gym, on_delete=models.PROTECT)
    ...

Мне нужно получить голосараспределение по дням для конкретного спортзала.Это легко сделать:

views.py

class VotesDistributionByDayViewSet(APIView):
    @staticmethod
    def get(request, gym_id, survey_id):
        votes_by_date = Answer.objects.filter(gym_id=gym_id, survey_id=survey_id)\
            .annotate(day=TruncDay('creation_date'))\
            .values("day")\
            .annotate(count=Count('gym_id'))\
            .order_by('day')

        return Response({
            'votes_by_gym': votes_by_date,
        })

Возвращает следующий ответ (, который является правильным ):

{
    "votes_by_gym": [
        {
            "day": "2018-06-11T00:00:00+02:00",
            "count": 15
        },
        {
            "day": "2018-06-12T00:00:00+02:00",
            "count": 6
        },
        {
            "day": "2018-06-13T00:00:00+02:00",
            "count": 17
        },
        {
            "day": "2018-06-14T00:00:00+02:00",
            "count": 12
        },
        ...

Теперь мне нужно вернуть среднее количество ответов по всем залам за день.Я попытался следующий запрос:

        avg_votes_by_date = Answer.objects\
            .exclude(gym_id=8) \
            .filter(survey_id=survey_id) \
            .annotate(day=TruncDay('creation_date')) \
            .values("day") \
            .annotate(count_answers=Count('gym_id')) \
            .annotate(count_gym=Count('gym_id', distinct=True)) \
            .order_by('day')

, который возвращает следующий ответ:

"avg_votes_by_date": [
        {
            "day": "2018-06-11T00:00:00+02:00",
            "count_answers": 15,
            "count_gym": 1
        },
        {
            "day": "2018-06-12T00:00:00+02:00",
            "count_answers": 6,
            "count_gym": 1
        },
        {
            "day": "2018-06-13T00:00:00+02:00",
            "count_answers": 17,
            "count_gym": 1
        },
        {
            "day": "2018-06-14T00:00:00+02:00",
            "count_answers": 12,
            "count_gym": 2
        },
        {
            "day": "2018-06-15T00:00:00+02:00",
            "count_answers": 29,
            "count_gym": 2
        },

Как я могу добавить третий ключ с результатом count_answers / count_gym?Есть ли лучший способ получить это среднее значение?

1 Ответ

0 голосов
/ 28 февраля 2019

Для второго запроса я предлагаю вам аннотировать и «count_answers», и «count_gym» среди выбранных значений, а затем делить на приведение:

avg_votes_by_date = Answer.objects \
    .exclude(gym_id=8) \
    .filter(survey_id=survey_id) \
    .annotate(day=TruncDay('creation_date')) \
    .values('day') \
    .annotate(count_answers=Count('gym_id')) \
    .annotate(count_gym=Count('gym_id', distinct=True)) \
    .annotate(avg=Cast(F('count_answers'), FloatField()) / Cast(F('count_gym'), FloatField())) \
    .order_by('day')
...