'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 |
---|