django querset过滤外键选择第一条记录

时间:2016-11-01 19:39:37

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

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

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。但我只想将它与第一行历史对象进行比较。

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

enter image description here

2 个答案:

答案 0 :(得分:0)

您可以使用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)

答案 1 :(得分:0)

我已经尝试过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;

如果在接下来的两天内未找到带有queryset过滤器链接的解决方案,我会选择接受的答案。