#django #django-orm
#django #django-orm
Вопрос:
Я пытаюсь получить объекты на основе выбранных / добавленных элементов в поле множества.
Мои модели:
class Location(models.Model):
name = models.CharField(max_length=45)
class BenefitLocation(models.Model):
benefit = models.ForeignKey(Benefit)
location = models.ForeignKey(Location)
class Benefit(models.Model):
locations = models.ManyToManyField(Location, through='BenefitLocation')
Вот что я пытаюсь сделать в orm:
selected_locations = Location.objects.filter(id__in[1,2]) #Getting IDs from api request
matching_benefits = Benefit.objects.filter(locations=selected_locations)
В matching_benefits мне нужны только те, у которых есть именно эти выбранные местоположения. Когда я пробую свой код, я получаю эту ошибку: django.db.utils.OperationalError: (1242, 'Subquery returns more than 1 row')
Как я могу получить matching_benefits?
Редактировать:
Используя код @Willem Van Onsem, подобный этому:
location_ids = [1]
location_ids = set(location_ids)
matching_benefits = Benefit.objects.annotate(
nloc_count=Count('locations'),
nloc_filter=Count('locations', filter=Q(locations__in=location_ids))
).filter(
nloc_count=len(location_ids),
nloc_filter=len(location_ids)
)
for item in matching_benefits:
print(item.locations.values('id'))
#output
#<QuerySet [{'id': 1}]>
#<QuerySet [{'id': 2}]>
Но запрос по-прежнему дает два преимущества (с разными местоположениями)
Ответ №1:
Вы должны использовать __in
:
selected_locations = Location.objects.filter(id__in=[1,2])
matching_benefits = Benefit.objects.filter(locations__in=selected_locations)
но здесь чище использовать список идентификаторов напрямую:
matching_benefits = Benefit.objects.filter(locations__in=[1,2])
Если вы ищете Benefit
s, которые имеют по крайней мере все эти местоположения, вы фильтруете с помощью:
from django.db.models import Sum
location_ids = [1, 2]
location_ids = set(location_ids)
matching_benefits = Benefit.objects.filter(
locations__in=location_ids
).annotate(nloc=Count('locations')).filter(nloc=len(location_ids))
Для Benefit
s, которые имеют точно все эти местоположения, вы фильтруете с помощью:
from django.db.models import Sum, Q
location_ids = [1,2]
location_ids = set(location_ids)
matching_benefits = Benefit.objects.annotate(
nloc=Count('locations'),
nloc_filter=Count('locations', filter=Q(locations__in=location_ids))
).filter(
nloc=len(location_ids),
nloc_filter=len(location_ids)
)
Комментарии:
1.
__in
Похоже, что использование фильтра не дает особых преимуществ с точным location_id в locations. Фильтр получает преимущества только с одним из location_id в списке.2. @TomasJacobsen: Я не совсем понимаю, что вы говорите. Для извлечения объектов будет использоваться
IN ...
предложение. В случае, если вы используетеselected_locations
, он будет использовать подзапрос. Для баз данных MySQL это часто является проблемой, поскольку MySQL не анализирует подзапрос, чтобы увидеть, что он » статический «, и, таким образом, запускает подзапрос для каждого элемента.3. Допустим, есть два местоположения
New York
иLondon
, и есть три преимущества. Один с justNew York
, один сLondon
и третий с обоими местоположениями. Если я__in
проверю, какое преимущество имеет местоположение »London
amp;amp;New York
«, я получу все три преимущества, но я хотел получить только то, где присутствовали оба местоположения.4. @TomasJacobsen: если есть преимущество с
New York
,London
иParis
, это тоже должно быть включено?5. Нет, если фильтр запрашивает
New York
иLondon
, он должен извлекать только те преимущества, которые имеют точно выбранные.