2008-11-02 26 views
13

Para mi aplicación Django tengo Eventos, Valoraciones y Usuarios. Las clasificaciones se relacionan con Eventos y Usuarios a través de claves externas. Al mostrar una lista de eventos, deseo filtrar las calificaciones del evento por un user_id para saber si un evento ha sido calificado por el usuario.Django: filtrado en objetos relacionados

Si hago:

event_list = Event.objects.filter(rating__user=request.user.id) 

(request.user.id da user_id de la corriente usuario conectado) ... entonces solo me dan los eventos que están clasificados por el usuario y no a la totalidad lista de eventos.

Lo que necesito se pueden generar a través del SQL personalizado:

SELECT * 
FROM `events_event` 
LEFT OUTER JOIN (
    SELECT * 
    FROM `events_rating` 
    WHERE user_id = ## 
) AS temp 
ON events_event.id = temp.user_id 

¿Existe una manera más fácil, así que no tengo que usar SQL personalizada?

Respuesta

15

El método filter es para filtrar los objetos que se devuelven según los criterios especificados, por lo que no es lo que desea aquí. Una opción es hacer una segunda consulta para recuperar todas las clasificaciones para objetos Event dados para el User actual.

Modelos:

import collections 

from django.db import models 

class RatingManager(models.Manager): 
    def get_for_user(self, events, user): 
     ratings = self.filter(event__in=[event.id for event in events], 
           user=user) 
     rating_dict = collections.defaultdict(lambda: None) 
     for rating in ratings: 
      rating_dict[rating.event_id] = rating 
     return rating_dict 

class Rating(models.Model): 
    # ... 
    objects = RatingManager() 

Ver:

events = Event.objects.all() 
user_ratings = Rating.objects.get_for_user(events, request.user) 
context = { 
    'events': [(event, user_ratings[event.id]) for event in events], 
} 

Plantilla:

{% for event, user_rating in events %} 
    {% if user_rating %} ... {% endif %} 
{% endfor %} 
1

Para hacer el mejor uso de Django, debes evitar intentar unir.

Una "combinación externa izquierda" es en realidad una lista de objetos con relaciones opcionales.

Es simplemente una lista de Eventos, Event.objects.all(). Algunos objetos de Evento tienen una calificación, otros no.

Obtendrá la lista de eventos en su vista. Usted maneja las relaciones opcionales en su plantilla.

{% for e in event_list %} 
    {{ e }} 
    {% if e.rating_set.all %}{{ e.rating_set }}{% endif %} 
{% endfor %} 

es un punto de partida.

+0

rating_set es el administrador que gestiona la relación con la calificación, por lo que siempre existirá. Incluso si le agregó una .count o .all, eso le daría detalles de todas las clasificaciones, no solo las del usuario actual. –

+0

No es mi pregunta :-) e.rating_set sería un django.db.models.fields.related.RelatedManager, por lo que {% if e.rating_set%} siempre pasaría. Tratar de iterarlo le daría TypeError: el objeto 'RelatedManager' no es iterable.Tendría que usar e.rating_set.all para obtener su comportamiento deseado. –

4

Además de la sugerencia de S.Lott, puede considerar el uso de select_related() para limitar el número de consultas a la base de datos; de lo contrario, su plantilla hará una consulta sobre el pase de cada evento a través del ciclo.

Event.objects.all().select_related(depth=1) 

El parámetro de profundidad no es necesario, pero si sus otros modelos tienen claves foráneas adicionales, limitará el número de uniones.

0

Yo creo que hay que hacer algo como esto.

events=Event.objects.filter(rating__user=request.user.id) 
ratings='(select rating from ratings where user_id=%d and event_id=event_events.id '%request.user.id 
events=events.extra(select={'rating':ratings}) 
Cuestiones relacionadas