2012-10-12 314 views
6

Soy nuevo en Python y Django, así que tenga paciencia conmigo.Consulta compleja con Django (publicaciones de todos los amigos)

I tienen los siguientes modelos:

class User(models.Model): 
    name = models.CharField(max_length = 50) 
    ... 

class Post(models.Model): 
    userBy = models.ForeignKey(User, related_name='post_user') 
    userWall = models.ForeignKey(User, related_name='receive_user') 
    timestamp = models.DateTimeField() 
    post = models.TextField() 

class Friend(models.Model): 
    user1 = models.ForeignKey(User, related_name='request_user') 
    user2 = models.ForeignKey(User, related_name='accept_user') 
    isApproved = models.BooleanField() 
    class Meta: 
     unique_together = (('user1', 'user2'),) 

Sé que esto puede no ser la mejor/manera más fácil de manejar con Django, pero he aprendido de esta manera y quiero mantenerlo así.

Ahora, todo lo que quiero hacer es obtener toda la publicación de una persona y sus amigos. La pregunta ahora es ¿cómo hacerlo con los filtros de Django?

creo en SQL se vería algo como esto:

SELECT p.* FORM Post p, Friend f 
WHERE p.userBy=THEUSER OR (
    (f.user1=THEUSER AND f.user2=p.userBy) OR 
    (f.user2=THEUSER AND f.user1=p.userBy) 
) 

sin ninguna garantía de exactitud, sólo para dar una idea del resultado que estoy buscando.

Respuesta

8
from django.db.models import Q 

Post.objects.filter(\ 
    Q(userBy=some_user) | \ 
    Q(userBy__accept_user__user1=some_user) | \ 
    Q(userBy__request_user__user2=some_user)).distinct() 

ACTUALIZACIÓN

Lo siento, fue mi culpa. No presté atención a tus valores related_name. Ver el código actualizado arriba. El uso de userBy__accept_user o userBy__request_user solo no funcionará porque será una referencia al Friend que no se puede comparar con User. Lo que estamos haciendo aquí es seguir la relación inversa al Friend y luego, una vez allí, ver si el otro usuario en la solicitud de amistad es el usuario en cuestión.

Esto también ilustra la importancia de describir las relaciones inversas de forma adecuada. Muchas personas cometen el mismo error que han cometido aquí y nombran el related_name después del modelo al que están creando el FK (User), cuando en realidad, cuando estamos hablando de revertir el FK, ahora estamos hablando de Friend. Simplemente, sus nombres relacionados tendrían más sentido como algo como: friend_requests y accepted_friends.

+0

¿hay una clave externa del usuario que tiene que apuntar a amigos? – cesar09

+0

No. El bit "__friend" sigue la relación al revés, ya que 'Friend' tiene un FK para' User'. –

+0

Para obtener más información, consulte la documentación de [consultas que abarcan las relaciones] (https://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships). –

0
(with User u) 
friends_u1 = Friend.objects.filter(user1 = u).getlist('user2_id', flat=True) 
friends_u2 = Friend.objects.filter(user2 = u).getlist('user1_id', flat=True) 
friends_and_user = friends_u1+friends_u2+u.id 

Post.objects.filter(userBy__id__in = friends_and_user) 
+0

El enfoque estándar es usar el objeto 'Q'. ¿Podría aclarar por qué eligió un enfoque diferente? – Tadeck

+0

¿De dónde sacas "el enfoque estándar es usar un objeto Q"? En ninguna parte de la documentación dice eso. Además, para un novato django, este enfoque desglosado puede ser más fácil de entender. – Colleen

+4

De la experiencia. Está reuniendo todos los ID primero, luego lo está usando para una consulta. Considere el caso en el que tendría 500 amigos, la consulta podría ser enorme. Sí, esta solución podría ser utilizada por Django novato (o por alguien que sepa _por qué_y _cuando_ podría ser mejor), pero esto no se trata de qué es más fácil de entender, sino de qué es lo mejor (o al menos "bueno"). Cuando se trata de la parte del "enfoque estándar": [lea la documentación sobre consultas complejas en Django] (https://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q- objetos). – Tadeck

Cuestiones relacionadas