Django - фильтрация на связанных объектах
Для моего приложения Django у меня есть события, рейтинги и пользователи. Рейтинги относятся к событиям и пользователям через внешние ключи. При отображении списка событий я хочу отфильтровать рейтинги Event_ user_id, поэтому я знаю, было ли событие оценено пользователем.
Если я это сделаю:
event_list = Event.objects.filter(rating__user=request.user.id)
(request.user.id дает user_id текущего зарегистрированного пользователя)... тогда я получаю только события, которые оцениваются пользователем, а не весь список событий.
То, что мне нужно, может быть создано через пользовательский SQL:
SELECT *
FROM `events_event`
LEFT OUTER JOIN (
SELECT *
FROM `events_rating`
WHERE user_id = ##
) AS temp
ON events_event.id = temp.user_id
Есть ли более простой способ, поэтому мне не нужно использовать собственный SQL?
Ответы
Ответ 1
Метод filter
предназначен для фильтрации того, какие объекты возвращаются на основе указанных критериев, так что это не то, что вы хотите здесь. Один из вариантов - выполнить второй запрос для получения всех оценок для заданных объектов Event
для текущего User
.
Модели:
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()
Вид:
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],
}
Шаблон:
{% for event, user_rating in events %}
{% if user_rating %} ... {% endif %}
{% endfor %}
Ответ 2
В дополнение к предложению S.Lott вы можете использовать select_related() для ограничения количества запросов к базе данных; в противном случае ваш шаблон будет выполнять запрос по каждому событию, проходящему через цикл.
Event.objects.all().select_related(depth=1)
Параметр глубины не требуется, но если ваши другие модели имеют дополнительные внешние ключи, это ограничит количество соединений.
Ответ 3
Чтобы наилучшим образом использовать Django, вам нужно избегать попыток сделать соединения.
"Левое внешнее соединение" на самом деле представляет собой список объектов с необязательными отношениями.
Это просто список событий Event.objects.all()
. Некоторые объекты событий имеют рейтинг, другие - нет.
Вы получаете список событий в своем представлении. Вы обрабатываете необязательные отношения в своем шаблоне.
{% for e in event_list %}
{{ e }}
{% if e.rating_set.all %}{{ e.rating_set }}{% endif %}
{% endfor %}
- точка сбрасывания.
Ответ 4
Думаю, вам нужно сделать что-то вроде этого.
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})