Как я могу фильтровать по нескольким условиям NOT в ORM Django?
В Django 3.2 у меня есть следующие модели:
class Person(models.Model):
name = models.CharField(max_length=100)
class Employment(models.Model):
owner = models.ForeignKey('Person', on_delete=models.CASCADE)
title = models.CharField(max_length=100)
full_time = models.BooleanField(default=True)
class Car(models.Model):
owner = models.ForeignKey('Person', on_delete=models.CASCADE)
color = models.CharField(max_length=100)
Цель: Give me all people that drive a red car (unless they're a full-time firefighter)
Вот то, что, как я думал, будет работать:
Person.objects.filter(
~Q(employment__title='firefighter', employment__full_time=True),
car__color='red'
)
Однако, это порождает следующий SQL:
SELECT *
FROM "person"
INNER JOIN "car" ON ("person"."id" = "car"."owner_id")
WHERE "car"."color" = red
AND (
NOT (
EXISTS(
SELECT (1) AS "a"
FROM "employment" U1
WHERE U1."full_time"
AND U1."owner_id" = "person"."id"
)
AND EXISTS(
SELECT (1) AS "a"
FROM "employment" U1
WHERE U1."title" = firefighter
AND U1."owner_id" = "person"."id"
)
)
Это фактически возвращает всех людей, которые водят красный автомобиль (если они не являются пожарными ИЛИ имеют постоянную работу). Как и во всех других ключах фильтра, я ожидал, что эти два условия на внешнем отношении будут ИДЕЙНЫМИ.
Почему такое неожиданное поведение? Как правильно написать это, чтобы sql выглядел следующим образом:?
SELECT *
FROM "person"
INNER JOIN "car" ON ("person"."id" = "car"."owner_id")
WHERE "car"."color" = red
AND (
NOT (
EXISTS(
SELECT (1) AS "a"
FROM "employment" U1
WHERE U1."full_time",
AND U1."title" = firefighter
AND U1."owner_id" = "person"."id"
)
)
Можно не смешивать Q()
и **kwargs
в filter()
:
Person.objects.filter(
~Q(employment__title='firefighter', employment__full_time=True) &
Q(car__color='red')
)