Результаты Django diffrenet из операции AND и последовательного фильтра - PullRequest
0 голосов
/ 09 мая 2018

Если у меня есть такие модели:

class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=20)

class PersonSession(models.Model):
    start_time = models.DateTimeField(auto_now_add=True)
    end_time = models.DateTimeField(null=True,
                                    blank=True)
    person = models.ForeignKey(Person, related_name='sessions')

class Billing(models.Model):
    DEBT = 'DE'
    BALANCED = 'BA'
    CREDIT = 'CR'

    session = models.OneToOneField(PersonSession,
                                   blank=False,
                                   null=False,
                                   related_name='billing')
    STATUS = ((BALANCED, 'Balanced'),
              (DEBT, 'Debt'),
              (CREDIT, 'Credit'))

    status = models.CharField(max_length=2,
                              choices=STATUS,
                              blank=False,
                              default=BALANCED
                              )

Два запроса ниже приводят к разным результатам:

Person.objects.filter(Q(sessions__start_time__gte='2000-02-01') & \
                      Q(sessions__start_time__lte='2000-03-01') & \
                      Q(sessions__billing__status=Billing.DEBT))
OR
Person.objects.filter(Q(sessions__start_time__gte='2000-02-01') & \
                      Q(sessions__start_time__lte='2000-03-01')).filter(
                      Q(sessions__billing__status=Billing.DEBT))

Первый генерирует двух человек 1,2, а второй генерирует всех трех человек, которые у меня есть, данные как показано ниже:

id | first_name | last_name | id |        start_time         |         end_time          | person_id | id | status | session_id 
---+------------+-----------+----+---------------------------+---------------------------+-----------+----+--------+------------
0 | person     | 0         |  0 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 |         0 |  0 | DE     |          0
0 | person     | 0         |  1 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 |         0 |  1 | BA     |          1
0 | person     | 0         |  2 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 |         0 |  2 | DE     |          2
1 | person     | 1         |  3 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 |         1 |  3 | BA     |          3
1 | person     | 1         |  4 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 |         1 |  4 | DE     |          4
1 | person     | 1         |  5 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 |         1 |  5 | DE     |          5
2 | person     | 2         |  6 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 |         2 |  6 | DE     |          6
2 | person     | 2         |  7 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 |         2 |  7 | DE     |          7
2 | person     | 2         |  8 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 |         2 |  8 | BA     |          8

Я написал пример , который создает класс и данные для них, чтобы показать разницу.

Пожалуйста, объясните, в чем разница между этими двумя запросами, и в качестве дополнительного вопроса, если в пакете django-filter я хочу определить некоторые поля для работы в качестве первого фильтра, как я могу это сделать?

1 Ответ

0 голосов
/ 09 мая 2018

Запросы, сгенерированные фильтрами, отличаются следующим образом:

>>> print Person.objects.filter(Q(sessions__start_time__gte='2000-02-01') & \
...                       Q(sessions__start_time__lte='2000-03-01')).filter(
...                       Q(sessions__billing__status=Billing.DEBT)).query
SELECT "test_filter_person"."id", "test_filter_person"."first_name", "test_filter_person"."last_name" FROM "test_filter_person" INNER JOIN "test_filter_personsession" ON ("test_filter_person"."id" = "test_filter_personsession"."person_id") INNER JOIN "test_filter_personsession" T3 ON ("test_filter_person"."id" = T3."person_id") INNER JOIN "test_filter_billing" ON (T3."id" = "test_filter_billing"."session_id") WHERE ("test_filter_personsession"."start_time" >= 2000-02-01 00:00:00+00:00 AND "test_filter_personsession"."start_time" <= 2000-03-01 00:00:00+00:00 AND "test_filter_billing"."status" = DE)
>>> print Person.objects.filter(Q(sessions__start_time__gte='2000-02-01') & \
...                       Q(sessions__start_time__lte='2000-03-01') & \
...                       Q(sessions__billing__status=Billing.DEBT)).query
SELECT "test_filter_person"."id", "test_filter_person"."first_name", "test_filter_person"."last_name" FROM "test_filter_person" INNER JOIN "test_filter_personsession" ON ("test_filter_person"."id" = "test_filter_personsession"."person_id") INNER JOIN "test_filter_billing" ON ("test_filter_personsession"."id" = "test_filter_billing"."session_id") WHERE ("test_filter_personsession"."start_time" >= 2000-02-01 00:00:00+00:00 AND "test_filter_personsession"."start_time" <= 2000-03-01 00:00:00+00:00 AND "test_filter_billing"."status" = DE)

Давайте назовем их Q1 и Q2 соответственно.

Как видите, Q1 выполняет объединение между Person, PersonSession и Billing один раз, и все условия применяются вместе в предложении WHERE.

Однако в Q2, так как первый фильтр завершается первыми двумя проверками, это приводит к первому соединению запроса между Person и PersonSession first (для Person.objects.filter(Q(sessions__start_time__gte='2000-02-01') & Q(sessions__start_time__lte='2000-03-01')), результат этого первого соединения снова присоединяется с PersonSession и Billing для выполнения второй части запроса фильтра .filter(Q(sessions__billing__status=Billing.DEBT)).

Вы можете прочитать больше здесь: разница между фильтром с несколькими аргументами и цепным фильтром в django

Также хочу обратить ваше внимание на последнюю строчку поста:

Одна таблица: Но если в запросе не используются объединенные таблицы, как, например, в Yuji и DTing. Результат тот же.

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