Переопределение запроса на Django DateField для изменения искомой даты

У меня есть модель в Django, которая представляет неделю. Я хотел бы, чтобы кто-то вводил любую дату, а модель автоматически сохраняла начало недели. Это легко, просто переопределите save.

class Week(models.Model):
  start_date = models.DateField()
  
  def save(self):
    self.start_date = self.start_date - datetime.timedelta(days=date.weekday())

Но я бы также хотел, чтобы кто-то мог запросить любой день и получить неделю. Например, я бы хотел, чтобы кто-то сделал следующее:

this_week = Week.objects.filter(start_date=date.today())

где сегодня среда, и получить объект week, где дата установлена для начала недели. Это должно работать для любой даты, а не только для сегодняшней.

Я знаю, что мы можем переопределить get_queryset в менеджере, но есть ли способ изменить то, что было фактически найдено? Все примеры менеджеров, которые я могу найти, просто изменяют набор запросов статическим образом. Или мне лучше всего попробовать подкласс DateField?

(Примечание код выше набран, упрощен и может содержать ошибки, но он работает в моем реальном коде)

На уровне базы данных вы можете сделать упорядочивание на основе даты начала_даты, в отличие от стандартного primary_key

class Week(models.Model):
  start_date = models.DateField()

  class Meta:
    ordering = ['start_date']

Отсюда довольно просто получить интересующую вас неделю.

week = Week.objects.filter(start_date__lte=date.today()).first()

Альтернативой использованию Meta является простое использование order_by в вашем запросе.

week = Week.objects.filter(start_date__lte=date.today()).order_by('start_date').first()

Мы можем подклассифицировать DateField, чтобы каждый раз очищать время даты до начала недели. Таким образом, это выглядит следующим образом:

from django.db.models.fields import DateField
from datetime import timedelta

class WeekField(DateField):
    def to_python(self, value):
        value = super().to_python(value)
        if value is not None:
            value -= timedelta(days=value.weekday())
        return value

Тогда мы можем создавать, фильтровать и т.д. с помощью WeekField. Таким образом, мы можем, например, указать это в модели Week:

class Week(models.Model):
    week = WeekField()

и затем, например, использовать .get_or_create(…):

>>> from datetime import date
>>> Week.objects.get_or_create(week=date.today())
(<Week: Week object (1)>, True)
>>> Week.objects.get_or_create(week=date.today())
(<Week: Week object (1)>, False)

Фильтр с неделями:

>>> Week.objects.filter(week=date(2021, 8, 31))
<QuerySet [<Week: Week object (1)>]>
>>> Week.objects.filter(week=date(2021, 8, 2))
<QuerySet []>

смотрите начало недели с .values():

>>> Week.objects.values()
<QuerySet [{'id': 1, 'week': datetime.date(2021, 8, 30)}]>

Я не тестировал это широко, но я думаю, что большая часть функциональности покрыта. Пожалуйста, напишите мне, если все еще есть сценарий использования, который не охвачен.

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