【问题标题】:Filter model object by difference in days按天数差异过滤模型对象
【发布时间】:2020-10-09 17:58:49
【问题描述】:

我的模型有一个日期字段,我想按过去 7 天过滤模型。

class Count(models.Model):
    task = models.ForeignKey(Task, related_name = 'counts', on_delete = models.CASCADE)
    start_time = models.DateTimeField(null = True, blank = True)
    end_time = models.DateTimeField(null = True, blank = True)
    time_spent = models.PositiveIntegerField()
    deleted = models.BooleanField(default = False)

    class Meta():
        ordering = ['accesses']

    def __str__(self):
        return f'{self.task.department} - {self.accesses.first().user} [{self.time_spent} min]'

    def stamped_date(self):
        if not self.start_time:
            return self.accesses.first().date
        return self.start_time

    def users(self):
        return list(set(map(lambda x: x.user, list(self.accesses.all()))))

我需要过滤过去 7 天内所有具有“stamped_date”的计数。

我试图做什么(在模型中):

    def daysBy(self):
        return (datetime.now() - self.stamped_date()).days

像这样过滤:

Count.objects.filter(daysBy__let = 7)

但是,datetime.now() 需要一个时区对象,否则会抛出以下错误:

TypeError: can't subtract offset-naive and offset-aware datetimes

我也觉得这可能不是实现我目标的最正确方法,请用更好的方法纠正我。

或者..给我一种插入与 TIME_ZONE 设置相关的时区对象的方法。

【问题讨论】:

    标签: python django


    【解决方案1】:

    来自Time Zones - Django Documentation

    启用时区支持时 (USE_TZ=True),Django 使用时区感知的日期时间对象。如果您的代码创建了日期时间对象,他们也应该知道。在这种模式下,上面的例子变成:

       from django.utils import timezone
    
       now = timezone.now()
    

    我希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      您可以像这样使用范围运算符来实现此查询。

      oldDate = datetime.now() - datetime.timedelta(days =7)
      Count.objects.filter(start_time__range=(oldDate, datetime.now()))
      

      编辑:要使用过滤器方法实现这种查询,您的方法生成的值应该是模型字段。您可以通过覆盖这样的保存方法来实现这一点:

      # Create the stamped date model field
      stamped_date = models.DateTimeField(null=True)
      
      #override the save method for your custom saving logic
      def save(self, *args, **kwargs):
              if not self.start_time:
                  self.stamped_date = self.accesses.first().date
              else:
                  self.stamped_date = self.start_time
              super().save(*args, **kwargs)  # Call the "real" save() method.
      
      

      Django docs

      【讨论】:

      • 谢谢,这是一个很好的答案,但不幸的是不是我需要的,因为我刚刚发现我无法过滤模型的方法..
      • @Ricardo 为什么不将方法字段转换为模型字段,您可以通过覆盖保存方法来添加计算逻辑。
      • 阿什塔纳感谢您的帮助!我已经在研究它(我不知道覆盖保存方法)。我成功了:)谢谢
      【解决方案3】:

      如果 stamped_date 在 7 天内,您想要过滤查询集并获得计数

      如果这是构建模型管理器来处理此问题的标准要求,那么您会为自己伸张正义

      你的目标是什么。因为通过这样做,您可以在返回的查询集上调用 count()。

      import datetime
      
          Count.objects.get_stamped_date().count()
      

      Model Manager 会这样,

          class CountManager(models.Manager):
      
              def get_stamped_date(self):
                  todays_date = timezone.now()
                  seven_days_ago = datetime.timedelta(days=7)
                  qs = super().get_queryset()
                  count = qs.filter(start_time__range=[seven_days_ago, todays_date])
      
              return count
      

      然后更新您的模型以包含管理器

      Count(models.Models):
          objects = CountManager()
      

      说实话,你的 stamped_date 应该是一个字段属性,它应该与你的模型的 save() 方法一起使用,所以你为什么这样做有点令人困惑。在保存方法(标准)下,每次没有开始时间时让模型实例检查并保存您的 self.access 日期时间字段,我在这里看不到对 stamped_date 字段的引用

      也只是为了顶部的樱桃,可能会更好地允许模型经理说多少天,所以它不固定为 7 天。

      【讨论】:

      • 嗨@Quintin,令人印象深刻的答案!你如何覆盖保存方法?这是一个很好的例子:stackoverflow.com/questions/4269605/… 吗?我是 webdev 的新手,所以请原谅我缺乏关于这些基本主题的信息。顺便问一下,你从哪里得到你展示的查询集过滤器上的“start_date”?
      • 抱歉打错了。应该是 start_time。作了相应的修改。上面的保存主题是完美的。只需检查 save 方法中是否有带有 if 语句的 stamped_date 字段,如果为空,则分配一个值,例如 save 示例。在你的代码之后总是在你的保存方法中返回 super
      • 那行不通,因为我需要过滤“stamped_date”,而不是“start_date”,这不是必填字段。无论如何,我已经用 save() 方法做到了!非常感谢
      猜你喜欢
      • 1970-01-01
      • 2014-08-22
      • 1970-01-01
      • 2018-03-23
      • 1970-01-01
      • 2021-10-23
      • 1970-01-01
      • 2013-03-16
      相关资源
      最近更新 更多