Вопрос касается использования Window
функций в Django.
У меня следующая модель:
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,
)
Моя цель здесь - аннотировать каждый объект в наборе запросов этой модели с полем state
из предыдущего и следующего объекта в наборе запросов с одинаковыми object_id
и content_type_id
. Это необходимо, чтобы позже получить эту аннотацию в подробном представлении и вычислить разницу между предыдущим, текущим и будущим состоянием.
Мой набор запросов в 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
аналог в RAW SQL
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
Он отлично работает, когда в наборе запросов есть более одного объекта, то есть в ListView. Но при детальном просмотре кажется, что WHERE
работает до SELECT
, и обе аннотации возвращают NULL
. Похоже, что функция WINDOW
в наборе запросов с одним единственным объектом в ней ограничена только этим объектом, а LEAD
и LAG
не могут видеть за пределами своих соседей.
Вопрос - возможно ли иметь эти 2 аннотации для одного объекта каким-то образом?