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.