如何在Django分页器中通过首尾对象字段增强页面导航?
在Django分页导航中显示FilmRoll的from_year范围
问题描述
我正在构建一个管理模拟胶片数字化流程的Django应用,希望在模板的分页导航组件中添加FilmRoll模型的film_roll.from_year字段,让用户能通过查看每个页面的from_year范围快速定位页面。
我的视图代码如下:
def index(request): film_roll_list = FilmRoll.objects.order_by( "from_year", "from_month", "title" ).annotate( scan_count=Count("myScanned_VS_NLP", distinct=True), ) paginator = Paginator(film_roll_list, 20) page_number = request.GET.get("page") try: page_obj = paginator.get_page(page_number) except PageNotAnInteger: # 如果页码不是整数,显示第一页 page_obj = paginator.get_page(1) except EmptyPage: # 如果页码超出范围,显示最后一页 page_obj = paginator.page(paginator.num_pages) film_roll_count = paginator.count context = { "film_roll_list": film_roll_list, "page_obj": page_obj, "film_roll_count": film_roll_count, } return render(request, "filmrolls/index.html", context)
模板中的分页导航代码:
{# Page navigator: start #} {% if page_obj.has_other_pages %} <div class="row"> <div class="btn-group" role="group" aria-label="Item pagination"> {% if page_obj.has_previous %} <a href="?page={{ page_obj.previous_page_number }}" class="btn btn-outline-primary">«</a> {% endif %} {% for page_number in page_obj.paginator.page_range %} {% if page_obj.number == page_number %} <button class="btn btn-outline-primary active"> <span>{{ page_number }} <span class="sr-only"></span></span> # TODO - 添加当前页面的from_year范围 </button> {% else %} {% if page_number == paginator.ELLIPSIS %} <button class="btn"> <span>{{ paginator.ELLIPSIS }} <span class="sr-only"></span></span> </button> {% else %} <a href="?page={{ page_number }}" class="btn btn-outline-primary"> {{ page_number }} # TODO - 添加其他页面的from_year范围 </a> {% endif %} {% endif %} {% endfor %} {% if page_obj.has_next %} <a href="?page={{ page_obj.next_page_number }}" class="btn btn-outline-primary">»</a> {% endif %} </div> </div> {% endif %} {# Page navigator: end #}
由于胶片集合已按from_year排序,我可以从分页列表的首尾对象获取from_year。请问该功能能否在Django模板中实现,还是必须在视图中计算这些属性?
解决方案
1. 当前页面的年份范围:可直接在模板实现
page_obj.object_list包含当前页面的所有FilmRoll对象,且数据已按from_year排序,直接取首尾对象的from_year即可:
修改模板中当前页面的按钮代码:
<button class="btn btn-outline-primary active"> <span>{{ page_number }} <span class="sr-only"></span></span> <small>({{ page_obj.object_list.0.from_year }} - {{ page_obj.object_list.last.from_year }})</small> </button>
2. 其他页面的年份范围:必须在视图中预计算
模板无法直接获取非当前页的对象列表(分页器的page_range仅提供页码,无对应数据),需要在视图中提前计算每个页面的首尾from_year,再传递给模板。
修改后的视图代码
def index(request): film_roll_list = FilmRoll.objects.order_by( "from_year", "from_month", "title" ).annotate( scan_count=Count("myScanned_VS_NLP", distinct=True), ) paginator = Paginator(film_roll_list, 20) page_number = request.GET.get("page") try: page_obj = paginator.get_page(page_number) except PageNotAnInteger: page_obj = paginator.get_page(1) except EmptyPage: page_obj = paginator.page(paginator.num_pages) # 预计算每个页面的年份范围 page_year_ranges = {} for num in paginator.page_range: page = paginator.page(num) if page.object_list: start_year = page.object_list[0].from_year end_year = page.object_list.last.from_year page_year_ranges[num] = f"{start_year} - {end_year}" else: page_year_ranges[num] = "" film_roll_count = paginator.count context = { "film_roll_list": film_roll_list, "page_obj": page_obj, "film_roll_count": film_roll_count, "page_year_ranges": page_year_ranges, # 传递年份范围数据 } return render(request, "filmrolls/index.html", context)
修改模板中其他页面的链接代码
Django模板默认不支持直接通过键获取字典值,需先添加自定义过滤器:
在app的templatetags目录下创建custom_filters.py:
from django import template register = template.Library() @register.filter def get_item(dictionary, key): return dictionary.get(key)
然后在模板顶部加载过滤器:
{% load custom_filters %}
最后修改分页链接代码:
<a href="?page={{ page_number }}" class="btn btn-outline-primary"> {{ page_number }} {% if page_year_ranges|get_item:page_number %} <small>({{ page_year_ranges|get_item:page_number }})</small> {% endif %} </a>
性能优化提示
如果数据量较大,遍历所有页面会触发多次数据库查询,建议:
- 缓存
page_year_ranges的计算结果,避免重复查询 - 只预计算分页导航中显示的页码(如当前页前后3页),而非所有页码
内容的提问来源于stack exchange,提问作者ShutterFreak
相关产品推荐
相关产品推荐

