返回

Django原生sql灵活高效分页方案

后端

原生SQL分页的妙招:打破Django限制

在Django的浩瀚世界中,Paginator 扮演着分页大师的角色,轻松驾驭 ORM 查询集和列表。然而,当原生 SQL 登场时,Paginator 的魔法就失效了,因为原生 SQL 返回的是一个神秘的游标对象,而 Paginator 只认得 ORM 查询集。

别担心,我们有办法打破这个限制,让原生 SQL 也能享受分页的乐趣。

方式一:巧用ORM查询集

这个方法可谓化腐朽为神奇,它将原生 SQL 查询的结果巧妙地转换成 Django ORM 查询集,让 Paginator 欢天喜地地接受。代码如下:

from django.db import connection

cursor = connection.cursor()
cursor.execute("SELECT * FROM my_table")

# 将游标对象变身 ORM 查询集
queryset = Model.objects.from_queryset(cursor)

# 交给 Paginator 分页
paginator = Paginator(queryset, 10)  # 每页 10 条数据

# 拿到当前页数据
page = paginator.page(1)  # 第一页

# 渲染分页模板
return render(request, 'my_template.html', {'page': page})

方式二:自定义分页器

这个方法更具灵活性,我们可以按自己的喜好定制分页行为。代码如下:

from django.core.paginator import Paginator
import math

class CustomPaginator(Paginator):
    def __init__(self, cursor, per_page):
        self.cursor = cursor
        self.per_page = per_page
        self.num_pages = math.ceil(self.cursor.rowcount / self.per_page)

    def page(self, number):
        if number > self.num_pages:
            raise Http404("非法页码")

        # 获取当前页数据
        self.cursor.execute("SELECT * FROM my_table LIMIT %s OFFSET %s", [self.per_page, (number - 1) * self.per_page])
        data = self.cursor.fetchall()

        # 创建一个 Page 对象
        page = Page(data, number, self)

        return page

# 使用自定义分页器分页原生 SQL 结果
paginator = CustomPaginator(cursor, 10)  # 每页 10 条数据

# 获取当前页数据
page = paginator.page(1)  # 第一页

# 渲染分页模板
return render(request, 'my_template.html', {'page': page})

选择哪种方式?

两种方法各有千秋:

  • 方式一 更简单,但原生 SQL 结果需转换为 ORM 查询集,可能影响查询效率。
  • 方式二 更灵活,可定制分页行为,但需要编写自定义分页器,代码复杂度更高。

根据实际情况选择最适合你的方法吧!

常见问题解答

1. 如何在分页模板中显示页码链接?

使用 Django 的 {% paginate %}{% page_numbers %} 标签。

2. 如何自定义分页样式?

修改 django.contrib.admin.templatetags.admin_list.html 模板文件。

3. 如何分页嵌套查询的结果?

使用递归或分步查询。

4. 如何处理大数据集分页?

考虑使用数据库分页或异步分页。

5. 是否有其他分页库可以与 Django 集成?

是的,例如 Django Extensions 和 Django REST Framework。