2

我在一个名为mainDjango 1.3 的应用程序中使用以下数据初始化了这些模型:

from django.db.models import Model, FloatField, CharField, ForeignKey, Q

class Customer(Model):
    name = CharField(max_length=64)

class Order(Model):
    customer = ForeignKey(Customer)
    order_total = FloatField()
c = Customer(name="John Smith")
c.save()

Order(customer=c, order_id=9).save()
Order(customer=c, order_id=13).save()

如何使用Q()对象构造查询以查找拥有订单 9 和订单 13 的客户?

在不使用Q()对象的情况下,我可以使用该.filter()方法两次来获得我想要的结果。如您所见,它使用两个JOINs 来查找两个外键:

queryset = Customer.objects.filter(order__order_id=9).filter(order__order_id=13)

return HttpResponse("%s\n\n%s" % (queryset, queryset.query), content_type="text/plain")
[<Customer: Customer object>]

SELECT "main_customer"."id", "main_customer"."name"
FROM "main_customer"
INNER JOIN "main_order" ON ("main_customer"."id" = "main_order"."customer_id")
INNER JOIN "main_order" T3 ON ("main_customer"."id" = T3."customer_id")
WHERE ("main_order"."order_id" = 9  AND T3."order_id" = 13 )

我尝试使用Q()以下对象做同样的事情。它没有理解我指的是两个不同的订单,一个 ID 为 9,一个 ID 为 13,而是认为我正在寻找一个 ID 为 9 和 13 的订单。这显然是不可能的,因此它不返回任何结果:

q = Q(order__order_id=9) & Q(order__order_id=13)
queryset = Customer.objects.filter(q)

return HttpResponse("%s\n\n%s" % (queryset, queryset.query), content_type="text/plain")
[]

SELECT "main_customer"."id", "main_customer"."name"
FROM "main_customer"
INNER JOIN "main_order" ON ("main_customer"."id" = "main_order"."customer_id")
WHERE ("main_order"."order_id" = 9  AND "main_order"."order_id" = 13 )

我希望 Django 的引擎能够等效地解释这两个查询,但显然Q()对象的处理方式不同。如何使用对象通过多个外键引用过滤对象,而不是多次Q()调用?.filter()

4

2 回答 2

2

我想我找到了解决这种情况的方法:

使用filter( ~(~Q(A) | ~Q(B)) )而不是filter(Q(A) & Q(B))似乎产生了预期的结果。

这是基于这样一个事实,A and B即等效于not ( not(A) or not(B) )并启用对多个ForeignKey仅使用Q对象的过滤(最终传递给单个对象filter)。它相当丑陋,但最终仍然只使用一个数据库查询。

如上所述,最初的问题来自.filter(A).filter(B)and的不同行为filter(A & B)(参见文档中的示例),并且filter(Q(A) & Q(B))仅复制了其中一种用途。

在我的案例中生成的 SQL 查询过于复杂,无法确保此解决方法完美运行,但我想我会分享这个想法,因为我的测试证明它有效。如果您确定证明/打破它,请告诉我。

于 2016-09-20T13:19:18.150 回答
1

在 Django 文档中找到了对此的解释。我观察到的行为是预期的行为;如果将多值关系的查询词应用于同一调用.filter()/.exclude()与应用于不同调用时,它们的处理方式将有所不同。

如果我在同一个.filter()调用中应用这两个查询词......

queryset = Customer.objects.filter(Q(order__order_id=9) & Q(order__order_id=13))
# or equivalently: .objects.filter(Q(order__order_id=9), Q(order__order_id=13))

...然后它只返回Customers 拥有满足两个约束的任何订单,order_id=9 AND order_id=13Order这些术语在任何给定时间都必须指代相同的术语。

另一方面,如果我使用两个不同的.filter()调用来应用查询术语......

queryset = Customer.objects.filter(order__order_id=9).filter(order__order_id=13)

...他们不需要引用相同的Order. 这可以看作是一个两步操作:所有Customers 的集合被过滤到拥有任何Order满足 s 的那些order_id=9。然后将该结果集进一步过滤到那些Customer也拥有任何Order满意的 s order_id=13。在这两种情况下可能是相同Order的。没关系; 这些步骤彼此隔离。

可能有一些方法可以Q()通过一次调用从对象中获取此行为.filter(),但看起来这不是 Django 的 ORM 的使用方式。

于 2012-09-18T17:33:17.793 回答