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].

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