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; 23Person.objects.filter(Q(active_stuffs__id=some_id)).count()
-& gt; 16Person.objects.filter(Q(cancelled_stuffs__id=some_id)).count()
-& gt; 0Person.objects.filter(Q(waiting_stuffs__id=some_id)).count()
-& gt; 6
Экземпляр Stuff
может быть только в одном из вариантов active_stuffs
, cancelled_stuffs
или waiting_stuffs
. Я проверил экземпляр Person
, который дублируется, а экземпляр Stuff
, который я ищу, находится только в поле waiting_stuffs
... Итак, откуда мог взяться этот дубликат?
Запрос содержит LEFT OUTER JOIN
s, таким образом:
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))
Вы получаете идентификатор, который входит в две группы