You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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">&laquo;</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">&raquo;</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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 22:35:54