Аннотируйте кверисет django на основе атрибутов связанных полей
Предположим, что у вас есть такая структура моделей в проекте django
from django.db import models
class Object(models.Model):
name = models.CharField(max_length=100)
class ObjectEvent(models.Model):
class EventTypes(models.IntegerChoices):
CREATED = 1, "Created"
SCHEDULED = 2, "Scheduled"
COMPLETED = 3, "Completed"
CANCELED = 4, "Canceled"
event_type = models.IntegerField(choices=EventTypes.choices)
object = models.ForeignKey(Object, on_delete=models.CASCADE, related_name="events")
Теперь я хочу получить доступ к производному свойству ended
, которое определяется как каждый объект, имеющий событие типа COMPLETED или CANCELED. Я уже сделал это с помощью декоратора @property, но я хочу иметь возможность фильтровать с помощью атрибута ended
. Поэтому я пытаюсь реализовать это через метод annotate
queryset.
class ObjectManager(models.Manager):
def get_queryset(self):
qs = super().get_queryset()
qs = qs.annotate(
ended=models.Case(
models.When(
events__event_type__in=(
ObjectEvent.EventTypes.COMPLETED,
ObjectEvent.EventTypes.CANCELED,
),
then=models.Value(True),
),
default=models.Value(False),
output_field=models.BooleanField(
verbose_name="Object ended",
),
)
)
return qs
class Object(models.Model):
objects = ObjectManager()
name = models.CharField(max_length=100)
fake_data.json
[
{ "model": "main.object", "pk": 1, "fields": { "name": "task1" } },
{ "model": "main.object", "pk": 2, "fields": { "name": "task2" } },
{ "model": "main.object", "pk": 3, "fields": { "name": "task3" } },
{ "model": "main.object", "pk": 4, "fields": { "name": "task4" } },
{
"model": "main.objectevent",
"pk": 1,
"fields": { "event_type": 1, "object": 1 }
},
{
"model": "main.objectevent",
"pk": 2,
"fields": { "event_type": 2, "object": 1 }
},
{
"model": "main.objectevent",
"pk": 3,
"fields": { "event_type": 4, "object": 1 }
},
{
"model": "main.objectevent",
"pk": 4,
"fields": { "event_type": 1, "object": 2 }
},
{
"model": "main.objectevent",
"pk": 5,
"fields": { "event_type": 1, "object": 3 }
},
{
"model": "main.objectevent",
"pk": 6,
"fields": { "event_type": 2, "object": 3 }
},
{
"model": "main.objectevent",
"pk": 7,
"fields": { "event_type": 3, "object": 3 }
},
{
"model": "main.objectevent",
"pk": 8,
"fields": { "event_type": 1, "object": 4 }
},
{
"model": "main.objectevent",
"pk": 9,
"fields": { "event_type": 2, "object": 4 }
}
]
Теперь, попробовав с этими фальшивыми данными, я получил странный результат в оболочке manage.py
>>> ended_objects = Object.objects.filter(ended=True)
>>> ended_objects.count()
2 # this is fine
>>> not_ended_objects = Object.objects.filter(ended=False)
>>> not_ended_objects.count()
7 # why?
>>> not_ended_objects.distinct().count()
4 # Event using distinct doesn't resolve the problem
Что я упускаю?