У меня есть следующие модели:
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
пусто И 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
не право queryset. Я думаю его на самом деле возврат более суженного фильтра (так как никакие записи не присутствуют), но я не уверен что. Согласно этому (django ссылка), потому что я ссылаюсь на связанную модель, я думаю исключить работы над исходным queryset (не тот от фильтра), но я не добираюсь точно как.
Хорошо, я просто думал об этом, которое я думаю работы, но чувствует себя неаккуратным (Существует ли лучший путь?):
qs = Order_type.objects.filter(order__id__in=[o.id for o in orders_qs])
Происходит то, что запрос exclude() все портит. По сути, он исключает все типы заказов, в которых есть хотя бы один заказ без статуса, что почти наверняка не то, чего вы хотите.
Самое простое решение в вашем случае - использовать order__status__gt=''
в аргументах filter(). Однако вам также потребуется добавить distinct()
в конец вашего запроса, потому что в противном случае вы получите QuerySet с несколькими экземплярами одного и того же Order_type, если в нем есть более одного Order, соответствующего запросу. Это должно работать:
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()
Дополнение (правка):
Вот фактический SQL, который Django выдает для вышеуказанных наборов запросов:
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 при очень маленьком наборе данных).