Добавление дополнительного поля ManyToMany в QuerySet в django - PullRequest
1 голос
/ 06 января 2012

Предположим, у меня есть следующие модели:

class Thing(models.Model):
    name = models.CharField(max_length=100)
    ratings = models.ManyToManyField('auth.User', through='Rating')

class Rating(models.Model):
    user = models.ForeignKey('auth.User')
    thing = models.ForeignKey('Thing')
    rating = models.IntegerField()

Так что у меня много вещей, и каждый пользователь может оценить каждую вещь.У меня также есть представление, показывающее список всех вещей (и их огромное количество) с рейтингом, который пользователь присвоил каждому из них.Мне нужен способ получить все данные из базы данных: объекты Thing с дополнительным полем user_rating, взятым не более чем из одного (потому что у нас есть фиксированный пользователь) связанный объект рейтинга.

Тривиальное решение выглядит так:

things = Thing.objects.all()
for thing in things:
    try:
        thing.user_rating = thing.ratings.objects.get(user=request.user).rating
    except Rating.DoesNotExist:
        thing.user_rating = None

Но недостаток этого подхода очевиден: если у нас будет 500 вещей, мы сделаем 501 запрос к базе данных.За одну страницу.На пользователя.И это самая просматриваемая страница сайта.Эта задача легко решается с помощью SQL JOIN, но на практике у меня есть более сложная схема, и я, безусловно, выиграю от фреймворка модели Django.Итак, вопрос: возможно ли сделать этот Django-способ?Было бы очень странно, если бы не это, учитывая, что такие задачи очень распространены.

Как я понял, ни annotate(), ни select_related() мне здесь не помогут.

Ответы [ 2 ]

3 голосов
/ 07 января 2012

Полагаю, вам стоит попробовать это: https://docs.djangoproject.com/en/1.3/ref/models/querysets/#extra

Пример

result = Thing.objects.all().extra(select={'rating': 'select rating from ratings where thing_id = id'})

Ваш набор результатов получает новое поле «рейтинг» для каждого объекта «вещь».

Я использую этот подход в одном из моих недавних проектов. Он производит один сложный запрос вместо n + 1 запросов.

Надеюсь, это поможет:)

0 голосов
/ 06 января 2012

Поскольку вы планируете отображать все на одной странице. Я могу думать об этом подходе. Вы можете попробовать это:

Получить все рейтинги, данные текущим пользователем и получить все вещи.

Теперь попробуйте создать словарь, подобный этому:

thing_dict = {}

for thing in Thing.objects.all():
    thing_dict[thing] = None
for rating in Rating.objects.filter(user = request.user):
    thing_dict[rating.thing] = rating

Теперь thing_dict содержит все записи модели Thing в качестве ключей и имеет свой рейтинг в качестве значения.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...