Цепной фильтр и исключение в модели Django с поисками полей, которые охватывают отношения - PullRequest
4 голосов
/ 18 июля 2010

У меня есть следующие модели:

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 <= сегодня И <code>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 не правильный набор запросов. Я думаю, что это на самом деле возвращает более узкий фильтр (так как никаких записей нет), но я не уверен, что. В соответствии с этим ( ссылка на django ), поскольку я ссылаюсь на связанную модель, я думаю, что исключение работает с исходным набором запросов (а не с фильтром), но я не понимаю, как именно.

Хорошо, я только что подумал об этом, который, я думаю, работает, но чувствует себя неряшливо (Есть ли лучший способ?):

qs = Order_type.objects.filter(order__id__in=[o.id for o in orders_qs])

1 Ответ

6 голосов
/ 18 июля 2010

Что происходит, так это то, что запрос exclude () все портит. По сути, он исключает любой Order_type, у которого есть хотя бы один Order без статуса, что почти наверняка не то, чего вы хотите.

Самое простое решение в вашем случае - использовать 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 с очень маленьким набором данных).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...