Django аннотирует набор запросов с количеством пересечений - PullRequest
0 голосов
/ 27 мая 2018

Djangonauts, мне нужно коснуться вашего мозга.

В двух словах, у меня есть следующие три модели:

class Location(models.Model):
    name = models.CharField(max_length=100)


class Profile(models.Model):
    locations_of_interest = models.ManyToManyField(Location)


class Question(models.Model):
    locations = models.ManyToManyField(Location)

Я хочу найти все профили, чьи местоположения интересов пересекаются сместа, указанные для определенного вопроса.Это просто:

question = Question.objects.first()

matching_profiles = Profile.objects.filter(
    locations_of_interest__in=question.locations.all()
)

Но кроме того, я также хотел бы знать, в какой степени перекрываются местоположения.

В простом Python я мог бы сделать что-то вроде этого:

question_location_names = [l['name'] for l in question.locations.all()]

for profile in matching_profiles:
    profile_location_names = [l['name'] for l in profile.locations_of_interest.all()]
    intersection = set(question_location_names).intersection(profile_location_names)
    intersection_count = len(list(intersection))
    # then proceed with this number

Тем не менее, мне кажется целесообразным выполнять операцию непосредственно в базе данных, если это возможно.

TL; DR

Поэтому мой вопрос:

Есть ли способ аннотировать набор запросов профиля этим счетчиком пересечений и таким образом выполнять операции в базе данных?

Я пробовал несколько вещей, но я не думаю, что они полезны дляте, кто читает это и может знать ответ.

1 Ответ

0 голосов
/ 28 мая 2018

Вы можете выполнить это с .annotate(..) это с Count(..) на locations_of_interest числе:

<b>from django.db.models import Count</b>

matching_profiles = Profile.objects.filter(
    locations_of_interest__in=question.locations.all()
)<b>.annotate(
    locnom=Count('locations_of_interest')
)</b>

Теперь каждый экземпляр matching_profiles будет иметь атрибут с именем locnom, которыйсодержит количество местоположений интересов, которые совпадают с фильтром.

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

РЕДАКТИРОВАТЬ : подсчет нескольких связанных непересекающихся (!) Полей

Вы можете расширить этот подход, подсчитав непересекающиеся объединения, используя distinct=True:

from django.db.models import Count

matching_profiles = Profile.objects.filter(
    locations_of_interest__in=question.locations.all(),
    <b>industries_of_interest__in=question.industries.all()</b>
)<b>.annotate(
    locnom=Count('locations_of_interest'<b>, distinct=True</b>)<b>,
    indnom=Count('industries_of_interest', distinct=True)</b>
)</b>

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

...