【问题标题】:django querset filter foreign key select first recorddjango queryset filter 外键选择第一条记录
【发布时间】:2017-03-15 00:39:02
【问题描述】:

我有一个如下所示的历史模型

class History(models.Model):

    class Meta:
        app_label = 'subscription'
        ordering = ['-start_datetime']

    subscription = models.ForeignKey(Subscription, related_name='history')

    FREE = 'free'
    Premium = 'premium'
    SUBSCRIPTION_TYPE_CHOICES = ((FREE, 'Free'), (Premium, 'Premium'),)
    name = models.CharField(max_length=32, choices=SUBSCRIPTION_TYPE_CHOICES, default=FREE)

    start_datetime = models.DateTimeField(db_index=True)
    end_datetime = models.DateTimeField(db_index=True, blank=True, null=True)
    cancelled_datetime = models.DateTimeField(blank=True, null=True)

现在我有一个如下所示的查询集过滤

users = get_user_model().objects.all()
queryset = users.exclude(subscription__history__end_datetime__lt=timezone.now())

问题在于,在上面的排除中,它正在检查 end_datetime 以获取特定历史对象的所有行。但我只想将它与历史对象的第一行进行比较。

以下是特定历史对象的外观。所以我想写一个查询集过滤器,它只能在第一行进行日期时间比较。

【问题讨论】:

    标签: django django-queryset foreign-key-relationship


    【解决方案1】:

    我尝试了 django 的所有表达式之王(aggregatequeryconditional)但无法解决问题,所以我选择了RawSQL,它解决了问题。

    我已经使用下面的 SQL 来选择第一行,然后比较 end_datetime

    SELECT (end_datetime > %s OR end_datetime IS NULL) AS result 
    FROM subscription_history 
    ORDER BY start_datetime DESC 
    LIMIT 1;
    

    如果在接下来的 2 天内没有找到带有查询集过滤器链接的解决方案,我将选择我的答案作为接受。

    【讨论】:

      【解决方案2】:

      您可以为此使用Model Manager method。文档不是那么描述性的,但您可以按照以下方式做一些事情:

      class SubscriptionManager(models.Manager):
          def my_filter(self):
              # You'd want to make this a smaller query most likely
              subscriptions = Subscription.objects.all()
              results = []
      
              for subscription in subscriptions:
                  sub_history = subscription.history_set.first()  
                  if sub_history.end_datetime > timezone.now:
                      results.append(subscription)
      
              return results
      
      
      class History(models.Model):
          subscription = models.ForeignKey(Subscription)
          end_datetime = models.DateTimeField(db_index=True, blank=True, null=True)
          objects = SubscriptionManager()
      

      然后:queryset = Subscription.objects().my_filter()

      不是可复制粘贴的答案,但显示了管理器的使用。鉴于您要查找的内容的特殊性,我认为没有办法仅通过普通的 filter()exclude() 获得它。

      在不知道您的最终目标是什么的情况下,很难说这是否可行,但您是否考虑过在订阅模型中添加一个属性来指示您要查找的内容?例如,如果您要让所有订阅即将结束的人:

      class Subscription(models.Model):
          @property
          def ending(self):
              if self.end_datetime > timezone.now:
                  return True
              else:
                  return False
      

      然后在您的代码中:queryset = users.filter(subscription_ending=True)

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-03
      • 1970-01-01
      • 2020-05-04
      • 2015-11-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多