'LEAD and LAG window fucntions in Django orm, how to apply on single object?

Question is regarding Window functions usage in Django.

I have a following model:

class EntriesChangeLog(models.Model):
    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
    )
    object_id = models.PositiveIntegerField(
    )
    content_object = GenericForeignKey(
        'content_type',
        'object_id',
    )
    user = models.ForeignKey(
        get_user_model(),
        verbose_name='user',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='access_logs',
    )
    access_time = models.DateTimeField(
        verbose_name='access_time',
        auto_now_add=True,
    )
    as_who = models.CharField(
        verbose_name='Status of the accessed user.',
        choices=UserStatusChoices.choices,
        max_length=7,
    )
    operation_type = models.CharField(
        verbose_name='Type of the access operation.',
        choices=OperationTypeChoices.choices,
        max_length=6,
    )
    state = JSONField(
        verbose_name='Model state before save or delete.',
        encoder=CustomEncoder,
    )

My goal here is to annotate each object in queryset of this model with state field from previous and next object in queryset with same object_id and content_type_id. This is needed to get this annotation later in detail view and calculate difference between previous, current and future state.

My queryset in get_object:

def get_queryset(self):
    model = self.kwargs['model_name']
    instance_pk = self.kwargs['instance_pk']

    self.queryset = self.model.objects.filter(
        object_id=instance_pk,
        content_type__model=model.__name__.lower(),
        content_type__app_label=model._meta.app_label.lower(),
    ).select_related('user', )

    return super().get_queryset()


def get_object(self):
    queryset = self.filter_queryset(self.get_queryset())

    q = queryset.annotate(
        next_val=Window(
            expression=Lead('state'),
            order_by=F('id').asc()
        ),
        prev_val=Window(
                expression=Lag('state'),
                order_by=F('id').asc(),
        ),
    )
    obj = q.filter(pk=self.kwargs['pk']).first()

    self.check_object_permissions(self.request, obj)

    return obj

analogue in RAWSQL

SELECT
       id,
       state,
       LEAD("state") OVER(ORDER BY "id" ) AS "next_val",
       LAG("state") OVER(ORDER BY "id") AS "prev_val"
        
FROM "administration_entrieschangelog"
where object_id =158 and content_type_id=7

It works fine when there are more then one object in queryset, that is in ListView. But in detail view it seems like WHERE works before SELECT and both annotations return NULL. It seems like that WINDOW function in queryset with only one single object in it restricted only by this object and LEAD and LAG cant see outside it’s neighbors.

Question is – is it possible to have these 2 annotations for single object somehow?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source