使用跨越关系的字段查找在 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 模型上进行链式过滤和排除的主要内容,如果未能解决你的问题,请参考以下文章

Django - 使用 Q 跨越空关系的查询集

Django-多态模型在1.7上进行迁移时遇到问题

按外键/相关字段分组 django 查询集

Django 1.7.3 - 查找字段引用的模型失败

数据模型图解分析(用户订单商品)

如何使用 django 表单/模型来表示字段之间的选择?