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()
Это может помочь вам получать только те книги, которые не скрыты и не имеют скрытых авторов, переводчиков или категорий.
Проверьте документацию тоже.