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