Pergunta

Este é provavelmente insultingly simples e digno de uma risada Nelson Muntz, mas eu estou tendo um verdadeiro momento braindead tryng para fazer muitos para muitos conexões através de várias relações modelo.

Eu tenho os seguintes modelos (simplificado para sua diversão!):

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

Imaginging os seguintes usuários:

  • Alice tem 3 documentos, um dos quais é privadas (ou seja, apenas amigos podem ver isto). Ela é amigo de Bob, é ignorando Mallory e é apático no sentido de Eva (nenhum significado armazenados relacionamento).
  • Mallory tem 2 documentos, tanto públicos e é apático para com todos.
  • Bob tem 1 documento que é público e também é apático direcção todos.
  • Eva está ignorando Alice e é apático para Mallory e Bob

Os usuários à procura de documentos deve produzir o seguinte:

  • Bob procurando documentos deve veja 6, como Alice fez dele um amigo e ele pode ver sua privado documentos.
  • Alice procura de documentos devem veja 4, Bobs 1 e sua 3. Ela não ver documentos públicos de Mallory como Alice está ignorando Mallory.
  • busca Mallory para documentos vê 5 - as públicas de Alice, sua própria 2 e Bobs 1. Alice ignorá-la não tem influência sobre o que Mallory pode ver, apenas que Alice não vê de Mallory docs.
  • Eva procurando documentos vê 3 - Mallory e documentos públicos de Bob como ela ignorou Alice.

Basicamente, eu estou tendo uma luta mentais para descobrir os filtros para retornar os querysets que eu descrevi acima. Alguém tem alguma idéia?

Editar

Graças à Ferdinands resposta abaixo eu era capaz de porca até o que eu queria com o início que ele me deu. Primeiro, queremos obter uma lista de pessoas que me friended que é uma pesquisa inversa através do relacionamento muitos para muitos:

friendly_authors = self.user.user_friends.all()

Get todas as pessoas que eu ignorados:

my_ignored = UserProfile.objects.get(user=self.user).ignored.all()

Obter uma lista de documentos que podem ver - docs que são visíveis, o meu, ou escritos por pessoas que me friended mas quem eu não ter ignorado:

docs = Document.objects.filter(
    (Q(viewable=True) | Q(author=self.user) | Q(author__in=friendly_authors))
     & ~Q(author__in=my_ignored)
)
Foi útil?

Solução

É um pouco complicado, talvez você está procurando algo assim:

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

Isso gera o seguinte SQL (eu fiz alguma formatação na saída original):

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
        )
    )
)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top