Django Rest Framework 基于ID的分页

6 投票
3 回答
2312 浏览
提问于 2025-04-18 17:25

我用Django Rest Framework搭建了一个API,现在想改进分页功能,让用户体验更好。

问题:

客户端发起请求,想要获取所有的帖子。请求的格式是:

http://website.com/api/v1/posts/?page=1

这会返回第一页的帖子。但是,新的帖子总是在不断被创建。所以当用户请求:

http://website.com/api/v1/posts/?page=2

返回的帖子几乎和第一页的一样(因为新数据总是不断进来,而我们是按-created的顺序排列的)。

可能的解决方案?

我想到一个办法,就是在请求中发送一个对象的ID,这样在获取帖子的时候,就可以根据上一次的查询来获取。

http://website.com/api/v1/posts/?page=2&post_id=12345

然后在分页的时候,我们可以过滤出post_id < 12345的帖子。

但这只适用于我们的post_id是整数的情况。

目前我只在使用一个基本的ListAPIView

class PostList(generics.ListAPIView):
    """
    API endpoint that allows posts to be viewed
    """
    serializer_class = serializers.PostSerializer # just a basic serializer
    model = Post

有没有更好的分页方法?这样在用户的下一次请求中,不会因为新数据的创建而显示和第一次请求一样的内容。

3 个回答

0

你可以给每个对象加一个字段,比如“创建时间/更新时间”。这样的话,你就可以记录用户发出请求的时间,然后把之后的所有内容过滤掉。

我自己没试过,但我觉得这个方法可能适合你的情况。

5

你在找的是来自DRF文档的光标分页(CursorPagination)

光标分页的好处有:

  • 提供一致的分页视图。当正确使用光标分页时,客户端在翻页时永远不会看到同一个项目,即使在翻页过程中其他客户端正在插入新项目。
  • 支持处理非常大的数据集。对于超大的数据集,使用基于偏移量的分页方式可能会变得低效或无法使用。而光标分页则有固定的时间特性,随着数据集的增大也不会变慢。

你也可以像上面提到的那样使用-created作为排序字段。

3

你可以考虑缓存查询结果吗?这样下一个页面就可以直接使用之前的查询结果,而不是重新查询。你还可以用一个参数来获取新的查询结果,当你需要的时候。

大概是这样的:

from django.core.cache import cache

class PostList(generics.ListAPIView):

    def get_queryset(self):
        qs_key = str(self.request.user.id) + '_key'
        if 'refresh' in self.request.QUERY_PARAMS:
            # get a new query set
            qs = Post.objects.all()
            cache.set(qs_key, qs)
        return cache.get(qs_key)

基本上,只有当你的网址是这样的:

http://website.com/api/v1/posts/?refersh=whatever

请求才会返回新的数据。

更新

为了给每个用户提供自己的一组帖子,缓存的关键需要包含一个唯一的标识符(比如用户的ID):我也更新了代码。

这种方法的缺点是,如果用户数量非常多,且每个用户的帖子数量也很大,可能就不太好用了。

所以,这里是我的第二个想法

为帖子模型使用一个带时间戳的模型,并根据创建时间来过滤查询结果。

我对你的模型和具体构建方式了解不多,所以我想你需要选择哪个解决方案最适合你的应用 :)

撰写回答