Фильтр по полю отношений в Django ORM - PullRequest
0 голосов
/ 12 февраля 2019

У меня есть модель Media, которая имеет отношение к модели UserMedia (рейтинги пользователей).Также существует модель под названием UserMatchScore (количество совпадений пользователей), которая имеет отношение к вопросу.

В представлении, которое я запрашиваю таблицу Media, в этом представлении есть возможность получить только те носители, которые имеют мои совпаденияоценил, но я не оценил.Кроме того, среднее количество оценок возвращается на основе подмножества меня и моих матчей, а не всех пользователей, которые оценили СМИ.Я делаю это с аннотацией.

Что я делаю, так это фильтрую таблицу Media по элементам, которые я не оценил, но мои совпадения оценили, это просто, но это только половина работы.Возвращаются все медиафайлы, которые я не оценил, но мои совпадения оценили, но реляционное поле UserMedia по-прежнему содержит все рейтинги, этот не фильтруется, поэтому нет способа рассчитать среднее значение оценок для подмножества моих совпадений.,Вот запрос, который я описываю:

queryset = models.Media.objects
queryset = queryset.filter(
               Q(usermedia__user__id__in=my_matches) & ~Q(usermedia__user=user)
            )

Единственный способ получить ожидаемый результат - выполнить цикл по набору запросов и отфильтровать каждый элемент отношения UserMedia, но это слишком медленно, поэтому должно бытьсделано с запросом БД.

for el in queryset:                    
    el.usermedia_set.filter(~Q(user=user)).filter(user=my_matches)

Кто-нибудь знает, как это сделать с Django ORM?

1 Ответ

0 голосов
/ 12 февраля 2019

Используя Prefetch, вы можете получить связанные объекты для отношения (для отношений многие-ко-многим и обратного внешнего ключа), используя предварительно отфильтрованный набор запросов:

queryset = queryset.filter(
               Q(usermedia__user__id__in=my_matches) & ~Q(usermedia__user=user)
            )

prefetch = UserMedia.objects.filter(user_id__in=my_matches).exclude(user=user)
queryset = queryset.prefetch_related(
    Prefetch('usermedia_set', prefetch, to_attr='filtered_usermedia')
)

for el in queryset:
    for usermedia in el.filtered_usermedia:
        # iterate over the filtered usermedia
        # without any additional queries
        calculate_something(usermedia)                    

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

...