Как мне получить ORM Django, чтобы проверить, имеет ли модель HAS или ВСЕ ВСЕ определенное отношение?
Я читал, что плохо избегать больших предложений IN, потому что они медленные (особенно с PostgreSQL).
Скажем, у меня есть класс под названием "Холодильник" и классы "Овощи и приправы".
Оба они имеют отношения ManyToMany между собой и Fridge.
Так что-то вроде:
class Fridge(models.Model):
condiments = models.ManyToManyField(Condiments)
vegetables = models.ManyToManyField(Vegetables)
И здесь у нас есть QuerySet, который представляет наши белые холодильники:
qs = Fridges.objects.filter(color='white')
Первый вопрос:
"Учитывая список идентификаторов приправ, принесите мне все холодильники, в которых есть ЛЮБОЙ из этих приправ (изменяя исходный QuerySet)".
Второй запрос:
"Учитывая список идентификаторов овощей, принесите мне все холодильники, в которых есть ВСЕ эти овощи (модифицируя исходный QuerySet)".
Как же я могу это сделать, не создавая список идентификаторов холодильников и не добавляя предложение IN в мой набор запросов?
Вот решения, которые делают это с предложениями IN (названия измененных версий моих существующих решений):
Первый запрос:
condiment_ids = [...] # list of condiment IDs
condiments = Condiment.objects.filter(
id__in=condiment_ids).all()
condiment_fridges = None
for condiment in condiments:
qs = condiment.fridge_set.all()
if not condiment_fridges:
condiment_fridges = qs
else:
condiment_fridges = condiment_fridges | qs
qs = qs.filter(id__in=[l.id for l in condiment_fridges])
Второй запрос:
vegetable_ids = [...] # list of vegetable IDs
vegetables = vegetable.objects.filter(id__in=vegetable_ids).all()
vegetable_fridges = None
for vegetable in vegetables:
qs = vegetable.location_set.all()
if not vegetable_fridges:
vegetable_fridges = qs
else:
vegetable_fridges = vegetable_fridges & qs
qs = qs.filter(id__in=[l.id for l in vegetable_fridges])
Эти решения кажутся ужасными и хакерскими, и мне было интересно, есть ли лучший способ сделать это с ORM Джанго.
1 ответ
Если я не понимаю вопрос, то все, что вам нужно, это:
Fridge.objects.filter(condiments__in=[1,2,3,4,5])
Там может быть более эффективный способ найти, если холодильник имеет все приправы. Не проверено, но что-то вроде:
max_conds = Condiment.objects.all().count()
result = Fridge.objects.annotate(conds=Count('condiments')).filter(conds=max_conds)
Это может быть медленнее, хотя в зависимости от вашей базы данных и количества строк для каждой модели.