概述 Django的分页器系统是一个精心设计的组件,用于处理大量数据的分页显示。它由两个核心类组成:
Paginator:分页器主类,负责分页逻辑和元数据计算
Page:单个页面类,表示具体某一页的数据和导航信息
核心设计原理 1. 分页器初始化 1 2 3 4 5 6 class Paginator (object ): def __init__ (self, object_list, per_page, orphans=0 , allow_empty_first_page=True ): self .object_list = object_list self .per_page = int (per_page) self .orphans = int (orphans) self .allow_empty_first_page = allow_empty_first_page
orphans参数的巧妙设计 :
如果最后一页的项目数少于或等于orphans,这些项目会被添加到前一页
避免出现最后一页只有1-2条数据的尴尬情况
2. 核心分页算法 总页数计算算法 1 2 3 4 5 6 7 8 @cached_property def num_pages (self ): if self .count == 0 and not self .allow_empty_first_page: return 0 hits = max (1 , self .count - self .orphans) return int (ceil(hits / float (self .per_page)))
算法精髓 :
hits = max(1, self.count - self.orphans) 确保至少有1页
通过ceil向上取整,保证所有数据都能被包含
orphans参数会减少总的有效记录数,从而可能减少页数
页面数据获取算法 1 2 3 4 5 6 7 8 9 10 def page (self, number ): number = self .validate_number(number) bottom = (number - 1 ) * self .per_page top = bottom + self .per_page if top + self .orphans >= self .count: top = self .count return self ._get_page(self .object_list[bottom:top], number, self )
关键逻辑 :
使用切片操作 object_list[bottom:top] 获取页面数据
最后一页特殊处理:将剩余的orphan数据包含进来
数据总数获取策略 1 2 3 4 5 6 7 8 @cached_property def count (self ): try : return self .object_list.count() except (AttributeError, TypeError): return len (self .object_list)
兼容性设计 :
QuerySet.count() → 执行 SELECT COUNT(*) SQL查询
list/tuple → 使用Python内置的 len() 函数
@cached_property装饰器深度解析 实现原理 1 2 3 4 5 6 7 8 9 10 11 12 class cached_property (object ): def __init__ (self, func, name=None ): self .func = func self .__doc__ = getattr (func, '__doc__' ) self .name = name or func.__name__ def __get__ (self, instance, cls=None ): if instance is None : return self res = instance.__dict__[self .name] = self .func(instance) return res
工作机制
描述符协议 :实现了__get__方法,控制属性访问
缓存策略 :将计算结果存储在instance.__dict__中
访问优化 :利用Python属性查找顺序,后续访问直接从实例字典获取
链式赋值的巧妙运用 1 res = instance.__dict__[self .name] = self .func(instance)
这行代码体现了Python链式赋值的优雅:
只调用一次函数 :self.func(instance)只执行一次
同时完成缓存和返回 :一步到位
代码简洁 :避免临时变量
Page类的设计亮点 1. 实现Sequence协议 1 2 3 4 5 6 7 8 9 class Page (collections.Sequence ): def __len__ (self ): return len (self .object_list) def __getitem__ (self, index ): if not isinstance (self .object_list, list ): self .object_list = list (self .object_list) return self .object_list[index]
2. 导航方法 1 2 3 4 5 6 7 8 9 def start_index (self ): return (self .paginator.per_page * (self .number - 1 )) + 1 def end_index (self ): if self .number == self .paginator.num_pages: return self .paginator.count return self .number * self .paginator.per_page
性能优化策略 1. 缓存层次 1 2 3 4 5 6 7 paginator.count paginator.count paginator.num_pages page.object_list
2. QuerySet优化 1 2 3 4 5 6 def __getitem__ (self, index ): if not isinstance (self .object_list, list ): self .object_list = list (self .object_list) return self .object_list[index]
3. 数据库查询优化 1 2 3 4 5 def count (self ): if self ._result_cache is not None : return len (self ._result_cache) return self .query.get_count(using=self .db)
性能与数据一致性的权衡 核心问题 1 2 3 4 5 6 7 8 9 paginator = Paginator(Article.objects.all (), 10 )print (paginator.count) Article.objects.filter (id__lte=10 ).delete()print (paginator.count)
Django的解决策略:请求级缓存 1. 短生命周期设计 1 2 3 4 5 6 7 8 9 10 11 def article_list (request ): paginator = Paginator(Article.objects.all (), 10 ) count1 = paginator.count count2 = paginator.count num_pages = paginator.num_pages return render(request, 'list.html' , {'paginator' : paginator})
2. 实例级隔离 1 2 3 4 5 request_1_paginator = Paginator(queryset, 10 ) request_2_paginator = Paginator(queryset, 10 )
权衡分析
方案
性能
一致性
复杂度
适用场景
无缓存
差
强一致
低
数据变化频繁
请求级缓存
好
请求内一致
低
Web应用(Django选择)
全局缓存
最好
弱一致
高
读多写少
TTL缓存
较好
最终一致
中
可接受延迟
Django选择的优势
简单可靠 :不需要复杂的缓存失效策略
内存安全 :随请求结束自动清理,无内存泄漏
并发安全 :每个请求独立,无竞态条件
适合Web场景 :符合HTTP请求的无状态特性
实际应用场景 1. 基础分页 1 2 3 4 5 6 7 8 9 10 11 def article_list (request ): articles = Article.objects.all ().order_by('-created_at' ) paginator = Paginator(articles, 10 ) page_number = request.GET.get('page' , 1 ) page = paginator.page(page_number) return render(request, 'list.html' , { 'page' : page, 'paginator' : paginator })
2. 模板中的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <div class ="pagination-info" > 共 {{ paginator.count }} 条记录,分为 {{ paginator.num_pages }} 页 当前第 {{ page.number }} 页</div > <div class ="pagination" > {% if page.has_previous %} <a href ="?page={{ page.previous_page_number }}" > 上一页</a > {% endif %} {% for num in paginator.page_range %} {% if num == page.number %} <strong > {{ num }}</strong > {% else %} <a href ="?page={{ num }}" > {{ num }}</a > {% endif %} {% endfor %} {% if page.has_next %} <a href ="?page={{ page.next_page_number }}" > 下一页</a > {% endif %}</div >
3. orphans参数的应用 1 2 3 4 5 6 paginator = Paginator(articles, per_page=10 , orphans=3 )
高级扩展 1. 自定义分页器 1 2 3 4 5 6 7 8 9 10 11 class SmartPaginator (Paginator ): def refresh_count (self ): """手动刷新计数缓存""" if 'count' in self .__dict__: del self .__dict__['count' ] if 'num_pages' in self .__dict__: del self .__dict__['num_pages' ] def get_fresh_count (self ): """获取最新计数,绕过缓存""" return self .object_list.count()
2. TTL缓存实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class TTLPaginator (Paginator ): def __init__ (self, *args, cache_ttl=60 , **kwargs ): super ().__init__(*args, **kwargs) self .cache_ttl = cache_ttl self ._count_cache_time = 0 self ._count_cache_value = None @property def count (self ): import time current_time = time.time() if (self ._count_cache_value is None or current_time - self ._count_cache_time > self .cache_ttl): self ._count_cache_value = self .object_list.count() self ._count_cache_time = current_time return self ._count_cache_value
最佳实践 1. 性能优化 1 2 3 4 5 6 7 articles = Article.objects.all ().order_by('-id' ) paginator = Paginator(articles, 10 ) articles = Article.objects.all () paginator = Paginator(articles, 10 )
2. 数据库查询优化 1 2 3 4 5 6 7 articles = Article.objects.select_related('author' ).order_by('-created_at' ) paginator = Paginator(articles, 10 ) articles = Article.objects.prefetch_related('tags' ).order_by('-created_at' ) paginator = Paginator(articles, 10 )
3. 错误处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from django.core.paginator import EmptyPage, PageNotAnIntegerdef article_list (request ): paginator = Paginator(articles, 10 ) page_number = request.GET.get('page' , 1 ) try : page = paginator.page(page_number) except PageNotAnInteger: page = paginator.page(1 ) except EmptyPage: page = paginator.page(paginator.num_pages) return render(request, 'list.html' , {'page' : page})
总结 Django分页器的设计体现了以下几个核心原则:
实用主义 :选择适合Web应用场景的缓存策略
性能优化 :通过@cached_property避免重复计算
简单可靠 :请求级缓存,避免复杂的失效机制
向后兼容 :支持QuerySet和普通列表
用户友好 :orphans参数优化用户体验
这个设计在性能、一致性和复杂度之间找到了最佳平衡点,是Web框架设计的典型范例。