基于FK的Django查询 - 得到所有,而不是任何

时间:2013-03-08 01:05:27

标签: python mysql django

我需要查找包含status = completed的所有订单商品的订单。它看起来像这样:

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

但是,此查询仅向我提供具有已完成状态的任何订单商品的订单。我如何进行查询,以便仅检索具有已完成状态的所有订单商品的订单?

3 个答案:

答案 0 :(得分: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)

答案 1 :(得分:1)

我能够通过一种hackish方式完成这项工作。首先,我添加了一个额外的布尔列is_finished。然后,查找包含至少一个未完成项目的订单:

orders = orders.filter(items__status__is_finished=False)

这给了我所有未完成的订单。

与此相反的是获得完成的订单:

orders = orders.exclude(items__status__is_finished=False)

答案 2 :(得分: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虽然不那么优雅,但通常会更有效率。