Поиск "Много ко многим" в Django
-
19-09-2019 - |
Вопрос
Это, вероятно, оскорбительно просто и достойно смеха Нельсона Манца, но у меня сейчас настоящий головокружительный момент, когда я пытаюсь установить много-много связей в различных моделях взаимоотношений.
У меня есть следующие модели (упрощенные для вашего удовольствия!):
class Document(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(User, blank=True)
content = models.TextField(blank=True)
private = models.BooleanField(default=False)
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
friends = models.ManyToManyField(User, symmetrical=False,
related_name='user_friends')
ignored = models.ManyToManyField(User, symmetrical=False,
related_name='user_ignored')
Представляя следующих пользователей:
- У Алисы есть 3 документа, 1 из которых приватный (то есть его могут видеть только друзья ).Она дружит с Бобом, игнорирует Мэллори и апатична по отношению к Еве (имеется в виду отсутствие сохраненных отношений).
- У Мэллори есть 2 документа, оба общедоступные и она безразлична ко всем.
- У Боба есть 1 документ, который является общедоступным и он также безразличен ко всем.
- Ева игнорирует Элис и безразлична к Мэллори и Бобу
Пользователи, выполняющие поиск документов, должны выдавать следующее:
- Боб, ищущий документы, должен просмотреть 6, поскольку Алиса подружилась с ним и он может просматривать ее личные документы.
- Алиса, ищущая документы, должна увидеть 4, Бобс - 1, а она - 3.Она не видит общедоступные документы Мэллори как Элис игнорирует Мэллори.
- Мэллори, ищущая документы, видит 5 - общедоступные документы Алисы, ее собственные 2 и Bobs 1.Игнорирование Элис ее не имеет никакого отношения к тому, что Мэллори может видеть, просто то, что Элис не видит документы Мэллори .
- Ева, ищущая документы, видит 3 - Общедоступные документы Мэллори и Боба как она проигнорировала Элис.
По сути, я испытываю умственную борьбу, выясняя фильтры для возврата наборов запросов, которые я описал выше.У кого-нибудь есть какие-нибудь идеи?
Редактировать
Благодаря приведенному ниже ответу Фердинанда я смог добиться того, чего хотел, с того старта, который он мне дал.Во-первых, мы хотим получить список людей, которые добавили меня в друзья, который представляет собой обратный поиск по отношениям "Многие ко многим":
friendly_authors = self.user.user_friends.all()
Собери всех людей, которых я игнорировал:
my_ignored = UserProfile.objects.get(user=self.user).ignored.all()
Получите список документов, которые я могу просмотреть - документы, доступные для просмотра, мои или написанные людьми, которые были моими друзьями, но которых я не игнорировал:
docs = Document.objects.filter(
(Q(viewable=True) | Q(author=self.user) | Q(author__in=friendly_authors))
& ~Q(author__in=my_ignored)
)
Решение
Это немного сложно, может быть, вы ищете что-то подобное:
>>> from django.db.models import Q
>>> me = User.objects.get(pk=1)
>>> my_friends = UserProfile.objects.get(user=me).friends.all()
>>> docs = Document.objects.filter(
... Q(author=me) | (
... Q(author__in=my_friends)
... & ~Q(author__userprofile__ignored=me)
... )
... )
Это генерирует следующий SQL (я выполнил некоторое форматирование исходного вывода):
SELECT "myapp_document".*
FROM "myapp_document" WHERE (
"myapp_document"."author_id" = %s
OR (
"myapp_document"."author_id" IN (
SELECT U0."id" FROM "myapp_user" U0
INNER JOIN "myapp_userprofile_friends" U1
ON (U0."id" = U1."user_id")
WHERE U1."userprofile_id" = %s
)
AND NOT (
"myapp_document"."author_id" IN (
SELECT U2."user_id" FROM "myapp_userprofile" U2
INNER JOIN "myapp_userprofile_ignored" U3
ON (U2."id" = U3."userprofile_id")
WHERE U3."user_id" = %s
)
AND "myapp_document"."author_id" IS NOT NULL
)
)
)