Django ORM: получать первое измерение за каждый день в большом наборе данных - PullRequest
1 голос

У меня есть проект Django по замерам температуры. Я использую PostgreSQL как серверную часть базы данных. Допустим, моя модель:

class TemperatureMeasurement(models.Model):
    time = models.DateTimeField(db_index=True)
    temperature = models.FloatField(null=False)

Я проводил измерения каждую минуту в течение последних 6 месяцев или около того, что означает, что в этой таблице у меня около 270 тысяч строк. Я пишу API, который должен возвращать первую запись температуры за каждый день для заданного диапазона дат. У меня есть что-то вроде этого:

def TemperatureBetween(APIView):
    # ...
    def get(self, request, *args, **kwargs):
        date_from = datetime.strptime(kwargs['date_from'], '%Y-%m-%d')
        date_to = datetime.strptime(kwargs['date_to'], '%Y-%m-%d')

        all_measurements = TemperatureMeasurement.objects.filter(
            time__gte=date_from,
            time__lt=date_to,
        ).order_by('time')

        r = []
        current_day = date_from

        while current_day < date_to:
            day_measurement = all_measurements.filter(
                time__gte=current_day,
            ).first()
            r.append([day_measurement.time, day_measurement.temperature])
            current_day += timedelta(hours=24)

        return Response(r)        

Я знаю, что этот подход, вероятно, далек от оптимального, потому что, как я понимаю, я делаю как минимум столько же запросов к базе данных, сколько дней во временном диапазоне (об этом мне и сообщает Django Панель инструментов отладки). Я читал об объектах Q() из Django, но не уверен, как их использовать в этом случае. Я думал что-то вроде:

        # ...
        query = Q()
        while current_day < date_to:
            query |= Q(time__gte=current_day).first()
            current_day += timedelta(hours=24)

        temperature_measurements = TemperatureMeasurement.objects.filter(query)
        # ... process data...

Но это не работает, поскольку объект Q() не имеет атрибута first. Можно ли как-нибудь оптимизировать этот запрос?

Спасибо.

1 Ответ

0 голосов
/ 28 мая 2020

Попробуйте это

from django.db.models.functions import TruncDay

daily_first_measurement = (TemperatureMeasurement.objects
                           .filter(time__gte=date_from, time__lt=date_to)
                           .annotate(day=TruncDay(time))
                           .order_by('day', 'time')
                           .distinct('day')
)

Это вернет первую запись TemperatureMeasurement для каждого дня в диапазоне от date_from до date_to. Для других, читающих это, postgresql необходим в качестве базовой базы данных для этого запроса (чтобы воспользоваться преимуществами distinct).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...