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

如何在Django通用类视图中优雅处理无效分页

Django 类视图中捕获 InvalidPage 异常并实现重定向的优雅方案

问题痛点

处理分页请求时,若用户访问无效页码(如超出范围、非数字),默认会抛出Http404(本质由InvalidPage异常衍生)。虽然Paginator.get_page()能自动返回有效页,但在get_context_data中尝试重定向会直接失败——该方法仅允许返回上下文字典,无法返回HttpResponseRedirect对象。现有可行方案虽能渲染正确页面,但地址栏仍保留原错误请求的URL,不够优雅。

失败的尝试代码

试图在get_context_data中捕获异常后重定向,因方法返回值限制直接失败:

def get_context_data(self, **kwargs):
    paginator = self.get_paginator(self.queryset,self.paginate_by,self.paginate_orphans)
    try:
        context = super().get_context_data(**kwargs)
    except Http404 as err:
        self.kwargs['page'] = paginator.get_page(self.request.GET['page']).number
        return redirect('my_list_view', kwargs=self.kwargs)

    context['page_list'] = context['paginator'].get_elided_page_range(context['page_obj'].number,on_each_side=2,on_ends=1)

    return context

可行但非最优的代码

能渲染正确页面,但地址栏仍显示原错误URL,用户体验不佳:

def get_context_data(self, **kwargs):
    paginator = self.get_paginator(self.queryset,self.paginate_by,self.paginate_orphans)
    try:
        context = super().get_context_data(**kwargs)
    except Http404 as err:
        self.kwargs['page'] = paginator.get_page(self.request.GET['page']).number
        context = super().get_context_data(**kwargs)

    context['page_list'] = context['paginator'].get_elided_page_range(context['page_obj'].number,on_each_side=2,on_ends=1)

    return context

优雅解决方案:重写get()方法

类视图的get()是处理GET请求的入口,在此处处理分页异常并执行重定向更符合视图生命周期逻辑,且允许返回HttpResponse类对象。

实现代码

from django.http import HttpResponseRedirect
from django.core.paginator import InvalidPage
from django.views.generic import ListView

class YourTargetListView(ListView):
    paginate_by = 10
    paginate_orphans = 0
    template_name = "your_template.html"
    model = YourModel

    def get(self, request, *args, **kwargs):
        try:
            return super().get(request, *args, **kwargs)
        except Http404 as e:
            # 仅处理分页导致的InvalidPage异常
            if isinstance(e.__cause__, InvalidPage):
                paginator = self.get_paginator(self.get_queryset(), self.paginate_by, self.paginate_orphans)
                valid_page_num = paginator.get_page(request.GET.get("page")).number
                # 构造带正确页码的重定向URL
                query_params = request.GET.copy()
                query_params["page"] = valid_page_num
                return HttpResponseRedirect(f"{request.path}?{query_params.urlencode()}")
            # 非分页原因的404,正常抛出
            raise

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["page_list"] = context["paginator"].get_elided_page_range(
            context["page_obj"].number,
            on_each_side=2,
            on_ends=1
        )
        return context

方案优势

  1. 符合视图职责划分:在请求入口get()中处理重定向逻辑,与负责上下文构造的get_context_data分离,代码逻辑更清晰。
  2. 同步更新地址栏:重定向后地址栏显示正确页码的URL,用户体验一致。
  3. 兼容默认行为:仅拦截分页导致的404,其他场景的404仍按原有逻辑抛出,不破坏视图原有功能。

内容的提问来源于stack exchange,提问作者Karl Wooster

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 23:24:56