Django Query Многие отношения "включает" - PullRequest
1 голос
/ 17 января 2020

У меня есть следующие модели:

class Contact(models.Model):
    email_list = models.ForeignKey(EmailList, related_name='contacts')
    customer = models.ForeignKey('Customer')

class EmailList(models.Model):
    customers = models.ManyToManyField('Customer',
        related_name='lists',
        through='Contact')

class Customer(models.Model):
    email = models.EmailField(max_length=255, unique=True)

Теперь я хочу запросить всех клиентов, которые принадлежат к определенному набору списков EmailList, по идентификатору EmailList. Я думал, что смогу запросить это с чем-то:

all_customers.filter(lists__id__contains=[email_list_1.pk, email_list_2.pk])

, что странно выглядит, я принимаю и возвращает пустой Queryset, но удивляет то, что он работает, когда запрашивает только 1 EmailList, как:

all_customers.filter(lists__id__contains=email_list_1.pk)

Итак, мой следующий шаг - запрос с помощью оператора AND и:

customers.filter(Q(lists__id__contains=el1.pk) & Q(lists__id__contains=el2.pk))

, но при этом возвращается и очищается QuerySet. Я хотел бы найти способ запросить lists__id__contains прохождение итерируемого идентификатора или, по крайней мере, найти способ объединить подзапросы

1 Ответ

3 голосов
/ 17 января 2020

Причина, по которой это не удается, заключается в том, что __contains lookup [Django -doc] работает для текста, и если правый операнд не является строкой, он будет преобразовать его в строку. Таким образом, вы в основном запрашиваете что-то, что имеет подстроку '[1, 4]'1 и 4 идентификаторами, они могут быть разными).

Клиент находится в любом списках

Вам необходимо использовать __in lookup [Django -doc] здесь:

all_customers.filter(<b>lists__id__in</b>=[email_list_1.pk, email_list_2.pk])

или вы можете использовать «логический или»:

customers.filter(Q(lists__id=el1.pk) | Q(lists__id=el2.pk))

Клиент находится в всех списках

Кроме того, мы можем запрашивать клиентов, которые есть во всех списках, путем аннотирования и фильтрации :

ids = [email_list_1.pk, email_list_2.pk]

all_customers.filter(<b>lists__id__in</b>=ids).annotate(
    <b>nlists=Count('lists')</b>
).filter(
    <b>nlists=len(set(ids))</b>
)
...