Django ORM выдает дубликаты в отфильтрованном наборе запросов

У меня есть приложение на django. Я использую ORM для выполнения некоторых запросов. Похоже, что в моем результате есть несколько дубликатов.

Хотя я могу просто добавить distinct(), я хотел бы понять, что происходит.

Вот мои модели:

class Person(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    active_stuffs = models.ManyToManyField(Stuff, related_name="persons")
    waiting_stuffs = models.ManyToManyField(Stuff, related_name="persons_waiting")
    cancelled_stuffs = models.ManyToManyField(Stuff, related_name="persons_cancelled")
    # ... other fields

class Stuff(models.Model):
    name = models.CharField(null=False, blank=False, max_length=150,)
    # ... other fields

Вот запрос:

queryset = Person.objects.filter(
    Q(active_stuffs__id=some_id)
    | Q(cancelled_stuffs__id=some_id)
    | Q(waiting_stuffs__id=some_id)
)

Чего я не понимаю, так это следующих результатов:

  • queryset.count() -& gt; 23
  • Person.objects.filter(Q(active_stuffs__id=some_id)).count() -& gt; 16
  • Person.objects.filter(Q(cancelled_stuffs__id=some_id)).count() -& gt; 0
  • Person.objects.filter(Q(waiting_stuffs__id=some_id)).count() -& gt; 6

Экземпляр Stuff может быть только в одном из вариантов active_stuffs, cancelled_stuffs или waiting_stuffs. Я проверил экземпляр Person, который дублируется, а экземпляр Stuff, который я ищу, находится только в поле waiting_stuffs... Итак, откуда мог взяться этот дубликат?

Запрос содержит LEFT OUTER JOINs, таким образом:

SELECT person.*
FROM person
LEFT OUTER JOIN person_active_stuffs ON person_active_stuffs.person_id = person.id
LEFT OUTER JOIN person_waiting_stuffs ON person_waiting_stuffs.person_id = person.id
LEFT OUTER JOIN person_cancelled_stuffs ON person_cancelled_stuffs.person_id = person.id
WHERE person_active_stuffs.stuff_id IN some_ids
  OR person_waiting_stuffs.stuff_id IN some_ids
  OR person_cancelled_stuffs.stuff_id IN some_ids

таким образом, получается три LEFT OUTER JOIN, и они могут действовать как "множители" друг друга.

Если один и тот же идентификатор встречается в active_stuffs и waiting_stuffs, он может быть засчитан дважды.

Проблема, по-видимому, в другом. Дубликатов нет, скорее один или несколько человек являются частью нескольких групп.

Напечатайте идентификаторы для разных групп, и вы обнаружите проблему.

active_staff = set(Person.objects.filter(
    active_stuffs__id=some_id
).values_list('id', flat=True))
waiting_staff = set(Person.objects.filter(
    waiting_stuffs__id=some_id
).values_list('id', flat=True))

print(active_staff.intersection(waiting_staff))

Вы получаете идентификатор, который входит в две группы

Вернуться на верх