Как аннотировать поле M2M модели Django и получить список отдельных экземпляров?
У меня есть две модели Django Profile
и Device
с отношениями ManyToMany друг с другом следующим образом:
class Profile(models.Model):
devices = models.ManyToManyField(Device, related_name='profiles')
Я пытаюсь использовать annotate()
и Count()
для запроса всех профилей, имеющих 1 или более устройств, следующим образом:
profiles = Profile.objects.annotate(dev_count=Count('devices')).filter(dev_count__gt=1)
Это здорово, это дает мне QuerySet
со всеми профилями (4500+) с одним или несколькими устройствами, как и ожидалось.
Далее, из-за связи M2M, я хотел бы получить список всех отдельных устройств среди всех профилей из предыдущего набора запросов.
Все мои неудачные попытки ниже возвращают пустой набор запросов. Я прочитал документацию по values
, values_list
и annotate
, но все еще не могу понять, как сделать правильный запрос здесь.
devices = profiles.values('devices').distinct()
devices = profiles.values_list('devices', flat=True).distinct()
Я также пытался сделать это за один раз:
devices = (
Profile.objects.values_list('devices', flat=True)
.annotate(dev_count=Count('devices'))
.filter(dev_count__gt=1)
.distinct()
)
Вы не можете работать с .values()
, так как этот элемент появляется и в пункте SELECT
, и в пункте GROUP BY
, поэтому тогда вы начинаете упоминать поле, и, следовательно, COUNT(devices)
вернет 1
для каждой группы.
Вы можете отфильтровать Device
, которые связаны хотя бы с одним из этих Profile
с помощью:
profiles = Profile.objects.annotate(
dev_count=Count('devices')
).filter(dev_count__gt=1)
devices = Device.objects.filter(profile__in=profiles).distinct()
Для некоторых диалектов SQL, обычно MySQL, лучше сначала материализовать список profile
и не работать с подзапросом, поэтому:
profiles = Profile.objects.annotate(
dev_count=Count('devices')
).filter(dev_count__gt=1)
profiles_list = list(profiles)
devices = Device.objects.filter(profile__in=profiles_list).distinct()