当前位置:首页 > 技术 > 正文内容

Django 中实现数据分页的完整方案

访客 技术 2026年7月9日 1

导入核心分页模块

在 Django 中使用分页功能,需引入以下类:

from django.core.paginator import Paginator, EmptyPage
  • Paginator:用于对查询集进行分页处理。
  • EmptyPage:当请求的页码超出范围时抛出的异常,可用于异常捕获和默认页跳转。

内置 Paginator 的基本用法

假设有一个模型查询结果 books,每页显示 7 条记录:

paginator = Paginator(books, 7)

常用属性与方法:

paginator.count 总数据条数
paginator.num_pages 总页数(根据每页数量计算)
paginator.page_range 页码范围生成器,如 range(1, 15)

获取指定页面内容

page = paginator.page(1)  # 获取第一页对象
for item in page:
    print(item.name)  # 遍历当前页所有对象

# 获取当前页数据列表
data_list = page.object_list

页面导航控制方法

  • page.has_previous():判断是否存在上一页
  • page.has_next():判断是否存在下一页
  • page.has_other_pages():判断是否有其他页(上或下)
  • page.previous_page_number():返回上一页页码
  • page.next_page_number():返回下一页页码

模拟数据填充示例

为测试分页效果,可批量创建测试数据:

from myapp.models import Book

book_instances = []
for i in range(1, 100):
    book_instances.append(Book(title=f'Python 入门 {i}', price=i))

try:
    Book.objects.bulk_create(book_instances)
except Exception as e:
    pass  # 忽略主键冲突等异常

视图中集成分页逻辑

在视图函数中结合 Paginator 实现分页响应:

def book_list(request):
    book_queryset = Book.objects.all()
    paginator = Paginator(book_queryset, 10)
    page_number = request.GET.get('page', 1)

    try:
        page_obj = paginator.page(page_number)
    except EmptyPage:
        page_obj = paginator.page(paginator.num_pages)

    context = {
        'page_obj': page_obj
    }
    return render(request, 'books/list.html', context)

前端模板中的分页展示

在 HTML 模板中遍历当前页数据:

{% for book in page_obj %}
    <div>{{ book.title }} - ¥{{ book.price }}</div>
{% endfor %}

基础分页控件渲染

<ul class="pagination">
  {% if page_obj.has_previous %}
    <li><a href="?page={{ page_obj.previous_page_number }}">上一页</a></li>
  {% endif %}

  {% for num in page_obj.paginator.page_range %}
    <li {% if num == page_obj.number %}class="active"{% endif %}>
      <a href="?page={{ num }}">{{ num }}</a>
    </li>
  {% endfor %}

  {% if page_obj.has_next %}
    <li><a href="?page={{ page_obj.next_page_number }}">下一页</a></li>
  {% endif %}
</ul>

优化页码显示:仅展示部分页码

当总页数较多时,避免显示全部页码。例如只显示当前页前后各 5 页:

# 在视图或分页类中计算显示范围
def get_display_range(page, total_pages, max_display=11):
    if total_pages <= max_display:
        return range(1, total_pages + 1)

    half = max_display // 2
    start = page - half
    end = page + half

    if start < 1:
        start = 1
        end = max_display
    if end > total_pages:
        end = total_pages
        start = max(end - max_display + 1, 1)

    return range(start, end + 1)

自定义高级分页组件

为了增强功能和复用性,可封装一个支持 URL 参数保留、首页/尾页跳转等功能的分页器:

import copy
from django.utils.safestring import mark_safe

class PageBar:
    def __init__(self, request, queryset, page_param='page', per_page=10):
        self.query_params = copy.deepcopy(request.GET)
        self.query_params._mutable = True
        self.page_param = page_param
        self.per_page = per_page

        total = queryset.count()
        total_pages, remainder = divmod(total, per_page)
        if remainder:
            total_pages += 1

        current_page = self.parse_page(request.GET.get(page_param), total_pages)

        self.total_count = total
        self.total_pages = total_pages
        self.current_page = current_page
        self.start_idx = (current_page - 1) * per_page
        self.end_idx = current_page * per_page
        self.queryset = queryset[self.start_idx:self.end_idx]

    def parse_page(self, val, max_page):
        if not val or not val.isdigit():
            return 1
        num = int(val)
        if num < 1:
            return 1
        elif num > max_page:
            return max_page
        return num

    def render(self):
        if self.total_pages == 0:
            return mark_safe("")

        page_html = []

        # 首页
        self.query_params.setlist(self.page_param, [1])
        page_html.append(f'<li><a href="?{self.query_params.urlencode()}">首页</a></li>')

        # 上一页
        if self.current_page > 1:
            self.query_params.setlist(self.page_param, [self.current_page - 1])
            page_html.append(f'<li><a href="?{self.query_params.urlencode()}">上一页</a></li>')

        # 动态页码范围
        if self.total_pages <= 11:
            page_range = range(1, self.total_pages + 1)
        elif self.current_page <= 6:
            page_range = range(1, 12)
        elif self.current_page + 5 > self.total_pages:
            page_range = range(self.total_pages - 10, self.total_pages + 1)
        else:
            page_range = range(self.current_page - 5, self.current_page + 6)

        for i in page_range:
            self.query_params.setlist(self.page_param, [i])
            active = ' class="active"' if i == self.current_page else ''
            page_html.append(f'<li{active}><a href="?{self.query_params.urlencode()}">{i}</a></li>')

        # 下一页
        if self.current_page < self.total_pages:
            self.query_params.setlist(self.page_param, [self.current_page + 1])
            page_html.append(f'<li><a href="?{self.query_params.urlencode()}">下一页</a></li>')

        # 尾页
        self.query_params.setlist(self.page_param, [self.total_pages])
        page_html.append(f'<li><a href="?{self.query_params.urlencode()}">尾页</a></li>')

        # 统计信息
        page_html.append(f'<li class="disabled"><a>共 {self.total_count} 条 | {self.total_pages} 页</a></li>')

        return mark_safe("".join(page_html))

在视图中使用自定义分页器

def product_list(request):
    products = Product.objects.all()
    pager = PageBar(request, products, per_page=15)
    
    context = {
        'products': pager.queryset,
        'pager_html': pager.render()
    }
    return render(request, 'product/list.html', context)

前端调用方式

{% for p in products %}
    <div>{{ p.name }}</div>
{% endfor %}

<ul class="pagination">
    {{ pager_html }}
</ul>
标签: django

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。