Django - filtering on related objects

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 %}

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.


The best practice would be

from django.db.models import Prefetch


event_list = Event.objects.all().prefetch_related(Prefetch(<related_name>, queryset=Rating.objects.filter(<criteria>)))

<related_name> = 'ratings' if:

class Rating(models.Model):
    event = models.ForeignKey(Event, related_name='ratings')

This returns events and filtered ratings for those event objects

Tags:

Django

Filter