Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - filtering on related objects

Tags:

django

filter

For my Django app I have Events, Ratings, and Users. Ratings are related to Events and Users through a foreign keys. When displaying a list of Events I want to filter the ratings of the Event by a user_id so I know if an event has been rated by the user.

If I do:

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

(request.user.id gives the user_id of the current logged in user) ...then I only get the events that are rated by the user and not the entire list of events.

What I need can be generated through the custom SQL:

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

Is there an easier way so I don't have to use custom SQL?

like image 831
Tim Graham Avatar asked Nov 02 '08 00:11

Tim Graham


2 Answers

The filter method is for filtering which objects are returned based on the specified criteria, so it's not what you want here. One option is to do a second query to retrieve all ratings for given Event objects for the current User.

Models:

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

View:

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],
}

Template:

{% for event, user_rating in events %}
  {% if user_rating %} ... {% endif %}
{% endfor %}
like image 72
Jonny Buchanan Avatar answered Oct 27 '22 09:10

Jonny Buchanan


In addition to S.Lott's suggestion, you may consider using select_related() to limit the number of database queries; otherwise your template will do a query on each event's pass through the loop.

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

The depth parameter is not required, but if your other models have additional foreign keys it will limit the number of joins.

like image 24
Daniel Naab Avatar answered Oct 27 '22 09:10

Daniel Naab