在Django中比较两个字段而不使用F对象

0 投票
2 回答
719 浏览
提问于 2025-04-16 14:43

我刚来这里,正在学习网页应用和搜索引擎优化。StackOverflow对我学习Python和Django帮助很大,非常感谢这个社区。现在我有个问题想问大家!

我有几个Django模型:

class Subscription(models.Model):
   hotel = models.ForeignKey("Hotel", related_name="subscriptions")
   tier = models.ForeignKey("Tier")
   enquiry_count = models.PositiveIntegerField(default=0)
   start_date = models.DateField(null=True, blank=True)
   end_date = models.DateField(null=True, blank=True)

还有:

class Tier(models.Model):
   name = models.CharField(max_length=32)
   enquiry_limit = models.PositiveIntegerField(default=0)

我还有一个酒店模型,这里我会用一个非常简单的形式展示:

class Hotel(models.Model):
   name = models.CharField("Hotel Name", max_length=128)
   address = models.TextField("Address", blank=True)
   town = models.CharField(max_length=64)
   star = models.PositiveIntegerField(default=0, null=True, blank=True)    

每个酒店都需要订阅才能出现在我的搜索结果中。每个订阅都有一个等级和一个查询限制。订阅会在到达结束日期或者查询次数达到上限时结束,也就是说,当它的查询次数达到这个等级的限制时。

我找到了一种简单的方法来实现这个功能,使用了F对象和排除条件,这在我的开发机器上运行得很好:

self.premium_hotels = Hotel.objects.select_related().exclude(
   Q(subscriptions__end_date__lte=datetime.date.today()) | Q(subscriptions__enquiry_count__gte=F('subscriptions__tier__enquiry_limit')))

不过,这在网站的正式版本上不行,因为它运行的是Django 1.0。有没有什么建议可以在不使用F对象的情况下实现这个查询呢?

我知道最明显的解决办法是升级,但我们需要立即推出这个功能,而我需要时间来准备和测试,才能升级到Django 1.3。

提前谢谢大家!

2 个回答

0

在查询集中使用 extra 来插入自定义的SQL语句(可以查看条件和表格):

http://docs.djangoproject.com/en/dev/ref/models/querysets/#extra

例如(大致上 - 你需要确保SQL中的表名等匹配):


self.premium_hotels = Hotel.objects.select_related().exclude(
   Q(subscriptions__end_date__lte=datetime.date.today())).extra(tables=["myapp_tier"], where=['myapp_subscriptions.enquiry_count 

最好的办法是在本地运行你的查询 - 查看它生成了什么SQL,然后用这些信息来确定在调用 extra 时该放什么。

0

如果没有F对象,你就无法一步到位地完成查询。你可以选择不考虑enquiry_limit,直接运行查询,然后再处理结果,把那些超过限制的酒店剔除掉。

下面的代码应该可以实现这个功能。

hotels = Hotel.objects.select_related().exclude(
    Q(subscriptions__end_date__lte=datetime.date.today()))

self.premium_hotels = []
for h in hotels:
    for sub in h.subscriptions.filter(start_date__lte=datetime.now(), end_date__gte=datetime.now()):
        if sub.enquiry_count < sub.tier.enquiry_limit:
            self.premium_hotels.append(h)
            break

撰写回答