Запрос "AND" в Django: Обратный ForeignKey или ManyToMany дает неожиданный результат

Я обнаружил разные результаты по запросу "AND" в зависимости от того, был ли это обратный или прямой запрос.

В запросе FORWARD вы можете сделать следующее:

Model.objects.filter(x=1) & Model.objects.filter(y=2)
Model.objects.filter(x=1, y=2)
Model.objects.filter(Q(x=1) & Q(y=2))

Все они дают один и тот же результат: QuerySet, удовлетворяющий обоим условиям (x=1 и y=2).

Но если у меня есть запрос REVERSE FOREIGNKEY или MANY TO MANY "AND", происходит что-то другое.


Вот что я сделал и чего ожидаю.

У меня есть эти модели:

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

class Author(models.Model):
    name = models.CharField(max_length=100)
    country = models.ForeignKey(Country, on_delete=models.CASCADE)

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    authors = models.ManyToManyField(Author)

Допустим, у меня есть такие данные:

Headline Pub Date Year Authors Blog
Pop 1 2023 Juan Pop
Pop 2 2023 Emma Pop
Pop 3 2010 Juan, Walmer Pop
Other 2023 John Beatles

Я хочу сделать запрос "AND", чтобы получить авторов, которые удовлетворяют и заголовку, содержащему слово "Pop", и Pub Date Year, равному 2023.

Ожидаемый результат: Хуан и Эмма.

Headline Pub Date Year Authors Blog
Pop 1 2023 Juan Pop
Pop 2 2023 Emma Pop

Мы можем использовать следующие методы:

  1. Многочисленные аргументы внутри фильтра.
Author.objects.filter(entry__headline__contains="Pop", entry__pub_date__year=2023)
  1. Q объект
Author.objects.filter(Q(entry__headline__contains="Pop") & Q(entry__pub_date__year=2023))

--> Эти два дают одинаковый ответ (ожидаемый результат: Хуан и Эмма) и создают один и тот же SQL:

SELECT  "blog_author"."id", "blog_author"."name", "blog_author"."email", "blog_author"."country_id" 

FROM "blog_author"

INNER JOIN "blog_entry_authors"

ON ("blog_author"."id" = "blog_entry_authors"."author_id")

INNER JOIN "blog_entry"

ON ("blog_entry_authors"."entry_id" = "blog_entry"."id")

WHERE ("blog_entry"."headline" LIKE %Pop% ESCAPE '\' AND "blog_entry"."pub_date" BETWEEN 2023-01-01 AND 2023-12-31)
  1. ЗДЕСЬ ПРОБЛЕМА: С этим третьим вариантом результат другой. И я не понимаю почему.
Author.objects.filter(entry__headline__contains="Pop") & Author.objects.filter(entry__pub_date__year=2023)

Результат другой, запрос AND получает таких авторов: Хуан, Эмма, Хуан. Я подумал, что это также должно быть: Хуан и Эмма.

Но почему это не так?

Вот созданный SQL (который отличается от двух предыдущих)

SELECT "blog_author"."id", "blog_author"."name", "blog_author"."email", "blog_author"."country_id" 

FROM "blog_author"

INNER JOIN "blog_entry_authors"

ON ("blog_author"."id" = "blog_entry_authors"."author_id")

INNER JOIN "blog_entry"

ON ("blog_entry_authors"."entry_id" = "blog_entry"."id")

LEFT OUTER JOIN "blog_entry_authors" T4

ON ("blog_author"."id" = T4."author_id")

LEFT OUTER JOIN "blog_entry" T5 ON (T4."entry_id" = T5."id")

WHERE ("blog_entry"."headline" LIKE %Pop% ESCAPE '\' AND T5."pub_date" BETWEEN 2023-01-01 AND 2023-12-31)
>>>

ВОПРОСЫ:

  1. Почему третий вариант дает другой результат, чем два предыдущих?
  2. Является ли это ожидаемым результатом (Хуан, Эмма, Хуан?)
Вернуться на верх