在每个类别的查询集中过滤 X 个最近的条目

Filtering X most recent entries in each category of queryset

问题是关于在每个查询集类别中过滤 X 个最近的条目。

目标是这样的:

我有一个基于以下模型的传入查询集。

class UserStatusChoices(models.TextChoices):
    CREATOR = 'CREATOR'
    SLAVE = 'SLAVE'
    MASTER = 'MASTER'
    FRIEND = 'FRIEND'
    ADMIN = 'ADMIN'
    LEGACY = 'LEGACY'


class OperationTypeChoices(models.TextChoices):
    CREATE = 'CREATE'
    UPDATE = 'UPDATE'
    DELETE = 'DELETE'


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,
    )
    

我需要以这种方式过滤这个传入的查询集,以便在每个类别中只保留 4 个最近的对象(由 access_time 字段定义)。类别由“content_type_id”字段定义,有 3 个可能的选项。 让我们称之为“option1”、“option2”和“option3

此传入查询集可能包含不同数量的 1,2 类或所有 3 类对象。这是事先无法预测的。 DISTINCT 无法使用,因为过滤操作后可能会对该查询集进行排序。

我通过以下方式设法获得了 1 个最新的对象:

# get one most recent operation in each category

last_operation_time = Subquery(
   EntriesChangeLog.objects.filter(user=OuterRef('user')).values('content_type_id').
    annotate(last_access_time=Max(‘access_time’)).values_list('last_access_time', flat=True)
)

queryset.filter(access_time__in=last_operation_time) 

但我很难弄清楚如何获取最后 4 个最近的对象而不是最后一个。 这是 Django-Filter 所必需的,并且需要在一个查询中完成。

DB-Postgres 12

你知道如何进行这种过滤吗?

谢谢...

pk_to_rank = queryset.annotate(rank=Window(
            expression=DenseRank(),
            partition_by=('content_type_id',),
            order_by=F('access_time').desc(),
        )).values_list('pk', 'rank', named=True)

        pks_list = sorted(log.pk for log in pk_to_rank if log.rank <= value)

        return queryset.filter(pk__in=pks_list)

通过将查询集分成两部分来设法做到这一点。带有 3 个联合的选项也是可能的,但是如果我们有 800 个选项而不是 3 - 使 800 个联合()怎么办???不...