覆盖 Django DateField 上的查询以更改搜索的日期

Override query on Django DateField to change date searched for

我在 Django 中有一个代表一周的模型。我想让某人输入任何日期并让模型自动保存一周的开始。很简单,覆盖保存即可。

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())

今天是星期三,获取周对象,其中日期设置为一周的开始。它需要适用于任何日期,而不仅仅是今天。

我知道我们可以在管理器中覆盖 get_queryset,但是有没有办法编辑实际搜索的内容?我能找到的每个经理示例都只是以静态方式更改了查询集。或者我最好的选择是尝试子类化 DateField?

(注意上面的代码是输入的,简化的,可能包含错误,但它在我的实际代码中有效)

在数据库级别,您可以根据 start_date 进行排序,而不是默认的 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 <strong>WeekField</strong>(DateField):
    def <strong>to_python</strong>(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 = <strong>WeekField()</strong>

然后例如使用 .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)}]>

我没有对此进行广泛测试,但我想大部分功能都已涵盖。如果还有未涵盖的用例,请 ping 我。