Фильтрация набора запросов по нескольким атрибутам FK - PullRequest
1 голос
/ 17 июня 2020

Моя ситуация:

У меня django модель HOST и модель PACKAGE (с именем и версией), у которой есть FK на host модель. Теперь мне нужно отфильтровать все хосты , у которых есть пакеты с определенным именем и определенной версией .. поэтому что-то вроде Host.objects.filter(name="best_package", version__in=['1.0','2.0']) Это все красиво и легко, но мне нужно повторить это действие для нескольких пакетов, чтобы я получил хост, у которого есть каждый из желаемых пакетов в одной из версий ..

Я попробовал два подхода, но оба не удалось, сначала применялся filter выше в for l oop, это было некрасиво, но работало, и как бы я ни был счастлив, я обнаружил, что это нестабильное решение, и некоторые из моих запросов, которые я применяю далее ИНОГДА не удается .. да иногда! Как я сказал себе, я не собираюсь углубляться в эту дыру Django ORM magi c Я попытался построить запрос с помощью Q. Я получил следующий код

pckgs_query = reduce(
    operator.and_,
    (
        Q(packages__name=name, packages__version__in=versions)
        for name, versions in pckgs_dict.items()
    )
 )
hosts = Host.objects.filter(pckgs_query)

, но, к сожалению это не работает должным образом, так как я проверил генерируемый им запрос SQL, я уверен, что он ищет единственный объект PACKAGE со всеми этими параметрами, который, конечно, не существует ... Есть ли кто-то достаточно опытный, чтобы дать мне рука? Я действительно потерялся здесь и хотел бы, чтобы моего приложения не было в django прямо сейчас tbh

Спасибо!

1 Ответ

0 голосов
/ 17 июня 2020

Причина, по которой это не работает, заключается в том, что вы каждый раз ограничиваете один и тот же пакет. Если ключи, таким образом, 'best_package' и 'other_package', вы говорите, что должен быть связанный пакет, который имеет как name и 'best_package', и 'other_package', но поскольку пакет имеет только одно имя, это, конечно,

Мы можем решить эту проблему другим способом: сначала мы фильтруем пакеты, чтобы получить все пакеты , которые соответствуют записи в словаре, а затем подсчитываем эти , и, таким образом, проверьте, совпадает ли количество связанных пакетов с количеством элементов в словаре:

from django.db.models import Count, Q

pckgs_query = Q(
    *[Q(package_set__name=name, package_set__version__in=versions)
      for name, versions in pckgs_dict.items()],
    _connector=Q.OR
)

hosts = Host.objects.filter(pckgs_query).annotate(
    <b>num_packages=Count('package_set')</b>
).filter(
    <b>num_packages=len(pckgs_dict)</b>
)

Таким образом, будут возвращены только Host с все пакеты установлены, так как если пакет отсутствует, то num_packages будет меньше, чем len(pckgs_dict).

...