как рассчитать частоту пар в наборе запросов - PullRequest
1 голос
/ 20 июня 2020

У меня две модели в Django:

class Pair(models.Model):
   pass

class Person(models.Model):
    pair = models.ForeignKey(to=Pair, related_name='mates')
    city = models.ForeignKey(to=City)

Поэтому мне нужно рассчитать частоту пар из разных городов:

city_a<->city_b: 100
city_a<->city_a: 80
city_b<->city_c: 200
...

для каждого человека, которого я могу получить город другого парня через: person.pair.mates.exclude(id=person.id).first() или что-то в этом роде, так что теоретически я могу просто l oop через все экземпляры Person, а затем вычислить частоты, но, по-видимому, это будет очень неэффективно.

но я могу Не понимаю, как получить эту информацию с помощью стандартного запроса (если есть способ). Любые подсказки приветствуются

1 Ответ

3 голосов
/ 20 июня 2020

Вы можете аннотировать пары, например:

from django.db.models import Count, F, Q

Person.objects.filter(
    Q(pair__mates__lt=F('pk')) | Q(pair__mates__gt=F('pk'))
).values(
    city1=F('city__name'),
    city2=F('pair__mates__city__name')
).annotate(
    <b>number=Count('pk')</b>
).order_by('city1', 'city2')

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

Запрос работает следующим образом: Q(pair__mates__lt=F('pk')) | Q(pair__mates__gt=F('pk')) обычно должен исключать «товарищей», которые относятся к тому же Person. Затем мы используем .values(..), чтобы получить name (или другое поле) из города и из pair__mates__city__names. Теперь, когда у нас есть эти два значения, мы получаем Count(..) количество записей в группе из city1 и city2. .order_by(..) необходим, чтобы избежать этого индексации, например, qs[1] вернет одну запись из исходного запроса Person.

Таким образом, запрос выглядит так:

SELECT app_name_city.name AS city1,
       T5.name AS city2,
       COUNT(app_name_person.id) AS number
FROM app_name_person
INNER JOIN app_name_pair ON app_name_person.pair_id = app_name_pair.id
INNER JOIN app_name_person T3 ON app_name_pair.id = T3.pair_id
INNER JOIN app_name_city ON app_name_person.city_id = app_name_city.id
INNER JOIN app_name_city T5 ON T3.city_id = T5.id
WHERE T3.id < app_name_person.id OR T3.id > app_name_person.id
GROUP BY app_name_city.name, T5.name
ORDER BY city1 ASC, city2 ASC

Это вернет QuerySet словарей:

<QuerySet [
    {'city1': 'city_a', 'city2': 'city_a', 'number': 80},
    {'city1': 'city_a', 'city2': 'city_b', 'number': 100},
    {'city1': 'city_b', 'city2': 'city_c', 'number': 200}
]>
...