Как мне получить 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)

Это может быть медленнее, хотя в зависимости от вашей базы данных и количества строк для каждой модели.

Другие вопросы по тегам