Python Django queryset:如何排除任何相关对象满足条件的对象
在进行一个困难的查询时,我偶然发现django QuerySet的一个奇怪行为,我想知道是否有人知道如何改进这个查询 基本上我有一个这样的模型:Python Django queryset:如何排除任何相关对象满足条件的对象,python,django,postgresql,Python,Django,Postgresql,在进行一个困难的查询时,我偶然发现django QuerySet的一个奇怪行为,我想知道是否有人知道如何改进这个查询 基本上我有一个这样的模型: class Product(models.Model): pass class Stock(models.Model): product_id = models.ForeignKey(Product) date = models.DateField() initial_stock = models.SmallInteg
class Product(models.Model):
pass
class Stock(models.Model):
product_id = models.ForeignKey(Product)
date = models.DateField()
initial_stock = models.SmallIntegerField()
num_ordered = models.SmallIntegerField()
我想选择所有在任何日期都不可用的产品,这意味着没有与产品相关的库存对象的初始库存字段大于num\u ordered字段。所以一开始,我做了:
Product.objects.exclude(stock__initial_stock__gt=F('stock__num_ordered')).distinct()
但我检查了一下,这个查询翻译为:
SELECT DISTINCT *
FROM "product"
LEFT OUTER JOIN "stock"
ON ("product"."id" = "stock"."product_id")
WHERE NOT ("product"."id" IN (
SELECT U1."product_id" AS Col1
FROM "product" U0
INNER JOIN "stock" U1
ON (U0."id" = U1."product_id")
WHERE (U1."initial_stock" > (U1."num_ordered") AND U1."id" = ("stock"."id"))
))
SELECT *
FROM "product"
LEFT OUTER JOIN "stock"
ON ("product"."id" = "stock"."product_id")
LEFT OUTER JOIN "stock" T3
ON ("product"."id" = T3."product_id")
WHERE NOT ("product"."id" IN (
SELECT U1."product_id" AS Col1
FROM "product" U0
INNER JOIN "stock" U1
ON (U0."id" = U1."product_id")
WHERE (U1."initial_stock" > (U1."num_ordered") AND U1."id" = ("stock"."id")) )
)) AND NOT ("product"."id" IN (
SELECT U1."product_id" AS Col1
FROM "product" U0
INNER JOIN "stock" U1
ON (U0."id" = U1."product_id")
WHERE U1."initial_stock" > (U1."num_ordered"))
))
它在stock上进行左连接,然后过滤掉初始库存大于订购数量的行,然后将不同的行返回给我
如你所见,当我有一个缺货的库存对象和另一个没有缺货的库存对象时,它不起作用
在过滤之后,我被遗漏了一个在另一个日期实际可用的产品
经过多次尝试,我认为这是可行的:
Product.objects.exclude(
stock__initial_stock__gt=F('stock__num_ordered')
).exclude(
stock__initial_stock__gt=F('stock__num_ordered')
).distinct()
因为它的意思是:
SELECT DISTINCT *
FROM "product"
LEFT OUTER JOIN "stock"
ON ("product"."id" = "stock"."product_id")
WHERE NOT ("product"."id" IN (
SELECT U1."product_id" AS Col1
FROM "product" U0
INNER JOIN "stock" U1
ON (U0."id" = U1."product_id")
WHERE (U1."initial_stock" > (U1."num_ordered") AND U1."id" = ("stock"."id"))
))
SELECT *
FROM "product"
LEFT OUTER JOIN "stock"
ON ("product"."id" = "stock"."product_id")
LEFT OUTER JOIN "stock" T3
ON ("product"."id" = T3."product_id")
WHERE NOT ("product"."id" IN (
SELECT U1."product_id" AS Col1
FROM "product" U0
INNER JOIN "stock" U1
ON (U0."id" = U1."product_id")
WHERE (U1."initial_stock" > (U1."num_ordered") AND U1."id" = ("stock"."id")) )
)) AND NOT ("product"."id" IN (
SELECT U1."product_id" AS Col1
FROM "product" U0
INNER JOIN "stock" U1
ON (U0."id" = U1."product_id")
WHERE U1."initial_stock" > (U1."num_ordered"))
))
这“有效”,但感觉像一个奇怪的黑客,似乎不是很简单的事情效率
你们有没有遇到过同样的问题,并想出了更好的办法
谢谢
编辑:
感谢@dirkgroten的回答,为了比较,让我写下sql查询结果:
SELECT *,
EXISTS(
SELECT *
FROM "stock" U0
WHERE (U0."product_id" = ("product"."id") AND U0."initial_stock" > (U0."num_ordered"))
) AS "has_stock"
FROM "product"
WHERE EXISTS(
SELECT *
FROM "stock" U0
WHERE (U0."product_id" = ("product"."id") AND U0."initial_stock" > (U0."num_ordered"))
) = false
这两个查询的执行时间似乎相同,即使您的查询看起来更好。
虽然我很困惑为什么注释的以下过滤器不使用注释创建的列,而不是在WHERE中再次执行查询
关于我的答案,我不明白的是,为什么在一种情况下,有一个额外的过滤器,并且U1.id=stock.id,而在另一种情况下没有。django在queryset API中是否有奇怪的行为 您最好使用一个:
from django.db.models import OuterRef, Exists
in_stock = Stock.objects.filter(
product_id=OuterRef('pk'),
initial_stock__gt=F('num_ordered'))
out_of_stock_products = Product.objects.annotate(has_stock=Exists(in_stock))\
.filter(has_stock=False)