Много-много поисков в Джанго - PullRequest
12 голосов
/ 23 августа 2009

Это, вероятно, оскорбительно просто и достойно смеха Нельсона Манца, но у меня есть настоящий момент, когда я пытаюсь установить множество связей между различными модельными отношениями.

У меня есть следующие модели (упрощенно для вашего удовольствия!):

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 и Бобс 1. Алиса, игнорирующая ее, не имеет опираясь на то, что Мэллори может видеть, просто что Алиса не видит Мэллори Docs.
  • Ева ищет документы видит 3 - Публичные документы Мэллори и Боба как она проигнорировала Алису.

По сути, у меня возникла умственная борьба, когда я искал фильтры для возврата наборов запросов, которые я описал выше. У кого-нибудь есть идеи?

EDIT

Благодаря ответу Фердинанда, приведенному ниже, я смог понять, чего я хотел, с самого начала, которое он мне дал. Во-первых, мы хотим получить список людей, которые подружились со мной, и это обратный взгляд на отношения «многие ко многим»:

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)
)

1 Ответ

8 голосов
/ 23 августа 2009

Это немного сложно, может быть, вы ищете что-то вроде этого:

>>> 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
        )
    )
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...