Django: как фильтровать кверисет по комбинации полей связанных объектов?
У меня есть три связанные модели, которые выглядят следующим образом:
class Library(models.Model):
name = models.CharField(max_length=200, blank=False)
class Meta:
verbose_name = 'library'
verbose_name_plural = 'libraries'
class Section(models.Model):
name = models.CharField(max_length=200, blank=False)
library = models.ForeignField(Library, related_name='sections', on_delete=models.CASCADE, null=False)
class Meta:
verbose_name = 'section'
verbose_name_plural = 'sections'
class Book(models.Model):
title = models.CharField(max_length=200, blank=False)
section = models.ForeignField(Section, related_name='books', on_delete=models.CASCADE, null=False)
is_available = models.BooleanField(default=True)
class Meta:
verbose_name = 'book'
verbose_name_plural = 'books'
Допустим, мне нужно отфильтровать все библиотеки, в которых есть книги с названием "Властелин колец".
Если я создам такой запрос
queryset = Library.objects.filter(sections__books__title="The Lord of the Rings", sections__books__is_available=True).distinct()
в него будут включены библиотеки, в которых есть моя книга, но она недоступна, потому что условие фильтра не применяется к одной и той же книге.
Как указать, что мне нужно объединить оба фильтра для поиска связанных объектов?
В него войдут библиотеки, в которых есть моя книга, но она недоступна, потому что условие фильтра не применяется к этой книге.
Это применимо к той же книге. Если вы укажете это в вызове same .filter(…)
[Django-doc], это будет применяться к same book.
Таким образом, запрос будет выглядеть следующим образом:
SELECT DISTINCT library.*
FROM library
LEFT OUTER JOIN section ON section.library_id = library.id
LEFT OUTER JOIN book ON book.section_id = section.id
WHERE book.title = 'The Lord of the Rings'
AND book.is_available
Если вы хотите применить его к другому Book
, то вы указываете его в двух вызовах .filter(…)
, так что Library.objects.filter(sections__books__title='The Lord of the Rings').filter(sections__books__is_available=True).distinct()
, и, таким образом, всего четыре LEFT OUTER JOIN
. Но это не то, что вам нужно. Если вы хотите добавить дополнительную фильтрацию, то вы можете сделать это в том же вызове .filter(…)
, или в том же Q
объекте [Django-doc].