使用跨越关系的字段查找对django模型进行链过滤和排除

时间:2010-07-17 22:24:41

标签: django django-models django-views django-queryset

我有以下型号:

class Order_type(models.Model):
    description = models.CharField()

class Order(models.Model):
    type= models.ForeignKey(Order_type)
    order_date = models.DateField(default=datetime.date.today)
    status = models.CharField()
    processed_time= models.TimeField()

我想要一个包含符合此条件的订单的订单类型列表:(order_date< =今天且processed_time为空,status不为空白

我试过了:

qs = Order_type.objects.filter(order__order_date__lte=datetime.date.today(),\
     order__processed_time__isnull=True).exclude(order__status='')

这适用于原始订单列表:

orders_qs = Order.objects.filter(order_date__lte=datetime.date.today(), processed_time__isnull=True)
orders_qs = orders_qs.exclude(status='')

但是qs不是正确的查询集。我认为它实际上返回了一个更窄的过滤器(因为没有记录)但我不确定是什么。根据这个(django reference),因为我引用了一个相关的模型,我认为排除适用于原始查询集(不是过滤器中的那个),但我没有得到确切的结果。

好吧,我只是想到了这个,我觉得有用,但感觉很草率(有更好的方法吗?):

qs = Order_type.objects.filter(order__id__in=[o.id for o in orders_qs])

1 个答案:

答案 0 :(得分:6)

正在发生的事情是,exclude()查询正在为您搞砸。基本上,它排除了任何至少有一个没有状态的订单的Order_type,这几乎肯定不是你想要发生的。

在您的情况下,最简单的解决方案是在您的filter()参数中使用order__status__gt=''。但是,您还需要将distinct()附加到查询的末尾,否则如果具有多个与查询匹配的Order,则会获得具有相同Order_type的多个实例的QuerySet。这应该有效:

qs = Order_type.objects.filter(
    order__order_date__lte=datetime.date.today(),
    order__processed_time__isnull=True,
    order__status__gt='').distinct()

在旁注中,在问题结尾处给出的qs查询中,您不必说order__id__in=[o.id for o in orders_qs],您只需使用order__in=orders_qs(您还需要{ {1}})。所以这也有效:

distinct()

附录(编辑):

以下是Django为上述查询集发出的实际SQL:

qs = Order_type.objects.filter(order__in=Order.objects.filter(
    order_date__lte=datetime.date.today(),
    processed_time__isnull=True).exclude(status='')).distinct()

EXPLAIN显示,第二个查询的价格要贵得多(成本为28.99,而数据集非常小,为28.64)。