Django, если любой из многих ко многим имеет значение False

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

class Category(CustomModel):
    name = models.CharField(max_length=32, unique=True, null=False, blank=False)
    ...
    is_hidden = models.BooleanField(default=False)

class Person(CustomModel):
    first_name = models.CharField(max_length=64, null=False, blank=False)
    ...
    is_hidden = models.BooleanField(default=False)

class Book(CustomModel):
    title = models.CharField(max_length=128, null=False, blank=False)
    authors = models.ManyToManyField(Person, null=False, blank=False)
    translators = models.ManyToManyField(Person, null=True, blank=True)
    ...
    categories = models.ManyToManyField(Category, null=False, blank=False)
    ...
    is_hidden = models.BooleanField(default=False)

Теперь я попробовал эти запросы, чтобы исключить все книги, содержащие либо is_hidden=True, authors__is_hidden=True, translators__is_hidden=True, либо categories__is_hidden=True:

так:

books = Book.objects.filter(
                is_hidden=False,
                authors__is_hidden=False,
                translators__is_hidden=False,
                categories__is_hidden=False).distinct()

и это:

books = Book.objects.filter(
                is_hidden=False,
                authors__is_hidden__in=[False],
                translators__is_hidden__in=[False],
                categories__is_hidden__in=[False]).distinct()

но я не могу найти способ достичь этой цели. Я хочу достичь своей цели без цикла, так как количество записей может достигать 80K. Как составить запрос?

Один из возможных способов достижения цели - использовать объект Q() для построения сложного запроса с логическими операторами. Например, вы можете использовать оператор ~ для отрицания условия, а оператор | - для объединения нескольких условий с помощью логики ИЛИ. Вот пример запроса, который исключает все книги, имеющие любой скрытый атрибут:

from django.db.models import Q

books = Book.objects.filter(~Q(is_hidden=True) & ~Q(authors__is_hidden=True) | ~Q(translators__is_hidden=True) | ~Q(categories__is_hidden=True)).distinct()[^1^][1][^2^][2]

Этот запрос возвращает все книги, которые не скрыты и не имеют скрытых авторов, переводчиков или категорий. Метод distinct() используется для того, чтобы избежать дублирования результатов. Подробнее об объекте Q() и о том, как его использовать, можно прочитать в документации Django: https://docs.djangoproject.com/en/3.2/topics/db/queries/#complex-lookups-with-q-objects

Вы можете использовать объекты Q, например, так:

from django.db.models import Q

books = Book.objects.filter(
    is_hidden=False
).exclude(
    Q(authors__is_hidden=True) |
    Q(translators__is_hidden=True) |
    Q(categories__is_hidden=True)
).distinct()

Это может помочь вам получать только те книги, которые не скрыты и не имеют скрытых авторов, переводчиков или категорий.

Проверьте документацию тоже.

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