2013-03-08 44 views
1

我需要查找狀態=已完成的所有訂單商品的訂單。它看起來像這樣:基於FK的Django查詢 - 獲取全部,不是任何

FINISHED_STATUSES = [17,18,19] 
if active_tab == 'outstanding': 
    orders = orders.exclude(items__status__in=FINISHED_STATUSES) 

然而,這個查詢只給我訂單與完成狀態的任何訂單項目。我該如何做這個查詢,以便我只檢索那些訂單項目全部具有完整狀態的訂單商品?

+0

我不確定爲什麼有人投票結束這個問題。我認爲它有好處。 – Brandon 2013-03-08 01:55:44

回答

1

我能得到一個有點hackish的方式完成這件事。首先,我添加了一個額外的布爾列,is_finished。然後,找到至少有一個未完成物品的訂單:

orders = orders.filter(items__status__is_finished=False) 

這會給我所有未完成的訂單。

做的是獲得成品訂單相反:

orders = orders.exclude(items__status__is_finished=False) 
+0

這可能是解決您的問題的最佳方法。我不認爲它是駭人聽聞 - 我會認爲它重構你的設計。如果您將來遇到類似情況,請查看我的修訂答案。 – pyrospade 2013-03-09 08:46:03

3

我認爲你需要在這裏做原始查詢:

設置你的訂單和物品模型,訂單和項目:

# raw query 
sql = """\ 
    select `orders`.* from `%{orders_table}s` as `orders` 
    join `%{items_table}s` as `items` 
    on `items`.`%{item_order_fk}s` = `orders`.`%{order_pk}s` 
    where `items`.`%{status_field}s` in (%{status_list}s) 
    group by `orders`.`%{orders_pk}s` 
    having count(*) = %{status_count)s; 
""" % { 
     "orders_table": Orders._meta.db_table, 
     "items_table": Items._meta.db_table, 
     "order_pk":  Orders._meta.pk.colum, 
     "item_order_fk":Items._meta.get_field("order").colum, 
     "status_field": Items._meta.get_field("status").colum, 
     "status_list": str(FINISHED_STATUSES)[1:-1], 
     "status_count": len(FINISHED_STATUSES), 
    } 

orders = Orders.objects.raw(sql) 
+0

好的答案,因爲Django ORM不支持group by。 – Brandon 2013-03-08 03:55:33

0

添加布爾領域是一個好主意。這樣您就可以在模型中明確定義您的業務規則。

現在,假設您仍然希望這樣做,而不訴諸增加字段。鑑於不同的情況,這很可能是一個要求。不幸的是,你不能真正在Django ORM中使用子查詢或任意連接。但是,您可以構建Q對象,並使用filter()annotate()在having子句中進行隱式聯接。

from django.db.models.aggregates import Count 
from django.db.models import Q 
from functools import reduce 
from operator import or_ 

total_items_by_orders = Orders.objects.annotate(
    item_count=Count('items')) 
finished_items_by_orders = Orders.objects.filter(
    items__status__in=FINISHED_STATUSES).annotate(
    item_count=Count('items')) 
orders = total_items_by_orders.exclude(
    reduce(or_, (Q(id=o.id, item_count=o.item_count) 
       for o in finished_items_by_orders))) 

請注意,使用原始SQL雖然較不優雅,但通常效率更高。