Django запрос с относительным временем с использованием часов базы данных

Я пытаюсь найти все объекты ORM, обновленные за последние 30 секунд. Стандартный способ сделать это был бы следующим:

    reftime = timezone.now() - relativedelta(seconds=30)
    queryset = MyModel.objects.filter(updated_at__gte=reftime)

Это работает до тех пор, пока все серверы приложений, на которых работает приложение django, синхронизируют свои часы, что звучит просто, но на практике это не так. Перекос часов реален.

К счастью, сервер базы данных имеет единственные часы, на которые мы можем положиться как на окончательные часы для такого рода вещей. А в django есть замечательный объект Now() в django.db.models.functions, который, кажется, должен быть полезен для такого рода вещей. Но я никогда не видел ни одного примера кода для этого метода, который бы делал какие-либо математические вычисления для сдвига времени на что-то вроде 30 секунд. Например, это НЕ РАБОТАЕТ:

    reftime = django.db.models.functions.Now() - relativedelta(seconds=30)

Как я могу сделать запрос в django ORM, используя временную метку относительно часов SQL сервера?

Вы не должны использовать relativedelta, а только timedelta [Python-doc], так:

from datetime import timedelta
from django.db.models.functions import Now

queryset = MyModel.objects.filter(updated_at__gte=Now()-timedelta(seconds=30))

Django реализовал логику для вычитания timedelta из класса Now и, таким образом, создания SQL выражения для этого. Это не случай для relativedelta.

Действительно, мы можем показать запросы, которые делает Django с помощью:

>>> print(Event.objects.filter(updated_at__gte=Now()-relativedelta(seconds=30)).query)
SELECT `app_name_mymodel`.`id`, `app_name_mymodel`.`updated_at` FROM `app_name_mymodel` WHERE `app_name_mymodel`.`updated_at` >= (CURRENT_TIMESTAMP - relativedelta(seconds=+30))
>>> print(Event.objects.filter(updated_at__gte=Now()-timedelta(seconds=30)).query)
SELECT `app_name_mymodel`.`id`, `app_name_mymodel`.`updated_at` FROM `app_name_mymodel` WHERE `app_name_mymodel`.`updated_at` >= (CURRENT_TIMESTAMP - INTERVAL 30000000 MICROSECOND)

Для relativedelta он просто использует str(…) объекта, тогда как для timedelta объекта он "понимает" этот тип и преобразует его в соответствующий эквивалент диалекта SQL.

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