Джанго - фильтрация по связанным объектам - PullRequest
17 голосов
/ 02 ноября 2008

Для моего приложения Django у меня есть События, Рейтинги и Пользователи. Рейтинги связаны с событиями и пользователями через внешние ключи. При отображении списка событий я хочу отфильтровать оценки события по 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?

Ответы [ 4 ]

16 голосов
/ 02 ноября 2008

Метод 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 %}
4 голосов
/ 02 ноября 2008

В дополнение к предложению S.Lott, вы можете рассмотреть возможность использования select_related () для ограничения количества запросов к базе данных; в противном случае ваш шаблон будет выполнять запрос при каждом прохождении события через цикл.

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

Параметр глубины не является обязательным, но если в других ваших моделях есть дополнительные внешние ключи, это ограничит количество соединений.

1 голос
/ 02 ноября 2008

Чтобы наилучшим образом использовать Django, вы должны избегать попыток делать соединения.

«Левое внешнее соединение» - это фактически список объектов с необязательными отношениями.

Это просто список событий, Event.objects.all(). Некоторые объекты событий имеют рейтинг, а некоторые нет.

Вы получаете список событий на ваш взгляд. Вы обрабатываете необязательные отношения в своем шаблоне.

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

является отправной точкой.

0 голосов
/ 27 июля 2010

Я думаю, вы должны сделать что-то вроде этого.

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})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...