使用跨越关系的字段查找在 django 模型上进行链式过滤和排除
Posted
技术标签:
【中文标题】使用跨越关系的字段查找在 django 模型上进行链式过滤和排除【英文标题】:chain filter and exclude on django model with field lookups that span relationships 【发布时间】:2011-03-17 10:55:34 【问题描述】:我有以下型号:
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 为空 AND 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】:发生的事情是 exclude() 查询让你搞砸了。基本上,它排除了至少有一个没有状态的订单的任何 Order_type,这几乎肯定不是你想要发生的。
在您的情况下,最简单的解决方案是在您的 filter() 参数中使用 order__status__gt=''
。但是,您还需要将distinct()
附加到查询的末尾,否则如果查询集有多个与查询匹配的订单,您将获得具有相同 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
(您仍然需要distinct()
)。所以这也可以:
qs = Order_type.objects.filter(order__in=Order.objects.filter(
order_date__lte=datetime.date.today(),
processed_time__isnull=True).exclude(status='')).distinct()
附录(编辑):
这是 Django 为上述查询集发出的实际 SQL:
SELECT DISTINCT "testapp_order_type"."id", "testapp_order_type"."description"
FROM "testapp_order_type"
LEFT OUTER JOIN "testapp_order"
ON ("testapp_order_type"."id" = "testapp_order"."type_id")
WHERE ("testapp_order"."order_date" <= E'2010-07-18'
AND "testapp_order"."processed_time" IS NULL
AND "testapp_order"."status" > E'' );
SELECT DISTINCT "testapp_order_type"."id", "testapp_order_type"."description"
FROM "testapp_order_type"
INNER JOIN "testapp_order"
ON ("testapp_order_type"."id" = "testapp_order"."type_id")
WHERE "testapp_order"."id" IN
(SELECT U0."id" FROM "testapp_order" U0
WHERE (U0."order_date" <= E'2010-07-18'
AND U0."processed_time" IS NULL
AND NOT (U0."status" = E'' )));
EXPLAIN 显示第二个查询的成本略高(28.99 的成本与非常小的数据集的 28.64 相比)。
【讨论】:
感谢您的建议和非常详细的分析。很有用以上是关于使用跨越关系的字段查找在 django 模型上进行链式过滤和排除的主要内容,如果未能解决你的问题,请参考以下文章