Расширенная фильтрация для аннотаций "многие-ко-многим

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

class CloudObjects(models.Model):
    object_id = models.AutoField(primary_key=True)
    object_name = models.CharField(max_length=256)
    creation_time = models.DateTimeField()
    removed_date = models.DateTimeField(blank=True, null=True)
    item = models.ManyToManyField(BackupItems, db_table='cloud_object_items')

class BackupItems(models.Model):
    name = models.CharField(max_length=100)

Хотелось бы аннотировать для каждого BackupItem самое последнее поле creation_time из CloudObject для элементов, которые планируется удалить в removed_date в будущем. В качестве примера: CloudObject выглядит следующим образом.

object_id | object_name | creation_time | removed_date | item
1 | object_one_in_cloud | 2021-01-01 | 2021-10-01 | 1
2 | object_two_in_cloud | 2021-02-02 | 2099-12-31 | 1
3 | object_three_in_cloud | 2021-03-03 | 2099-12-31 | 1
4 | object_four_in_cloud | 2021-12-31 | 2022-01-01 | 1

Для приведенного выше примера я бы хотел аннотировать элемент 3, так как он имеет дату удаления_в будущем и это самый свежий элемент (элемент 2 также планируется удалить в будущем, но 3 - более свежий)

Теперь в моих Представлениях я хотел бы сделать аннотацию. Я пробовал разные способы, но теперь могу двигаться дальше. Вот последний, который я пробовал:

from django.db.models import Subquery, OuterRef

class BackupListView(ListView):
    template_name = 'somefile.html'

    def get_queryset(self):
        last_item = CloudObjects.objects.filter(item=OuterRef("pk")).filter(removed_date__gte=timezone.now()).last()
        all_items = BackupItems.objects.annotate(last_backup=Subquery(last_item.get('creation_time')))
        return all_items

Как заставить его работать?

Как описано в docs:

(Использование get() вместо среза будет неудачным, поскольку OuterRef не может быть разрешен, пока набор запросов не будет использован в подзапросе)

.

last ведет себя аналогично get, где он пытается разрешить кверисет, но OuterRef сначала должен быть в подзапросе. Вот почему это не сработает. Поэтому вместо этого следует использовать нарезку, например, так:

    def get_queryset(self):
        cloud_objects = CloudObjects.objects.filter(
            item=OuterRef("pk")
        ).filter(
            removed_date__gte=timezone.now()
        ).order_by(
            "-creation_time"
        )

        all_items = BackupItems.objects.annotate(
            last_backup_date=Subquery(
                cloud_objects.values("creation_time")[:1]
            )
        )
        return all_items
Вернуться на верх