Фильтр Django по часу дня, когда информация о часовом поясе хранится в базе данных - PullRequest
0 голосов
/ 31 октября 2019

Со следующей структурой модели:

class Place(models.Model):
    ...
    timezone = TimeZoneField()  # Defined in a package called django-timezone-utils
    ...

class Session(models.Model):
    ...
    place = model.ForeignKey(Place, ...)
    created = models.DateTimeField(auto_now_add=True)  # Saved in utc
    ...

Я хочу получить все сеансы, созданные в 10-й час дня. Основной подход к этому:

Session.objects.filter(created__hour=10)

Но при этом, как и ожидалось, результаты фильтруются по времени UTC. Как я могу получить объекты Sessions, созданные в 10-й час дня, в часовом поясе места, к которому они подключены? Обратите внимание, что могут быть сеансы, выполненные в разных местах с разными часовыми поясами, но я хочу, чтобы сеансы, созданные внутри 10-го часа дня в их локальных часовых поясах.

Что-то вроде аннотирования каждого результата созданным временем вместный часовой пояс, а затем может сработать фильтрация по этому аннотированному значению, но я не знаю, как мне создать эту аннотацию

1 Ответ

0 голосов
/ 31 октября 2019

Я сделал то, что хотел, определив пользовательскую функцию базы данных, которая преобразует поле даты и времени в часовой пояс, который также определен в соответствующем столбце. Теперь работает только с postgres (используя выражение «в часовом поясе»), но может быть обновлен для использования других механизмов БД, также используя их функции преобразования часового пояса.

из django.db.models import Func, DateTimeField

class ConvertToTimezone(Func):
    """
    Custom SQL expression to convert time to timezone stored in database column
    """

    output_field = DateTimeField()

    def __init__(self, datetime_field, timezone_field, **extra):
        expressions = datetime_field, timezone_field
        super(ConvertToTimezone, self).__init__(*expressions, **extra)

    def as_sql(self, compiler, connection, fn=None, template=None, arg_joiner=None, **extra_context):
        params = []
        sql_parts = []
        for arg in self.source_expressions:
            arg_sql, arg_params = compiler.compile(arg)
            sql_parts.append(arg_sql)
            params.extend(arg_params)

        return "%s AT TIME ZONE %s" % tuple(sql_parts), params

И вот как я его использую:

Session.objects.annotate(created_local=ConvertToTimezone('created', 'place__timezone')).filter(created_local__hour=10)

Я не очень хорошо понимаю, как работают функции базы данных Django и выражения базы данных, поэтому я рад (и будубудьте готовы услышать) любые отзывы / улучшения к решению

...