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

Django类视图表单提交返回上一页 模板传参404问题咨询

问题根因

现有代码存在三个核心错误:

  • urls.py中as_view(previous_url="previous_url")的写法只会给视图类传入固定值为"previous_url"的类属性,完全无法读取URL?后拼接的查询参数。
  • 查询参数属于URL的附加参数,不需要在路由path中单独定义,你触发404的原因是请求将previous_url拼接成了路径段,最终请求地址变成/entry/5131/edit/previous_url,没有匹配到任何路由规则。
  • 编辑表单为POST提交,你仅在进入编辑页的GET链接中携带了上一页参数,表单中没有存储该值,提交时参数会直接丢失。
正确实现步骤

1. 修正路由配置

删除as_view()中多余的固定参数,路由不需要单独处理查询参数,Django会自动将查询参数封装到request对象中:

path('entry/<int:pk>/edit/', EntryUpdateView.as_view(), name='entry_update'),

2. 重写类视图逻辑

废弃固定类属性传值的写法,在GET请求渲染表单时将查询参数中的上一页地址传入模板上下文,在POST提交表单时从请求体中取出上一页地址,增加安全校验避免开放重定向漏洞后完成跳转:

from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.http import url_has_allowed_host_and_scheme

class EntryUpdateView(LoginRequiredMixin, UpdateView):
    model = Entry
    template_name = 'entry_update.html'
    fields = ('source', 'target', 'glossary', 'notes')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 从GET查询参数中提取上一页地址,传给模板
        context['previous_url'] = self.request.GET.get('previous_url', '')
        return context

    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.updated_by = self.request.user
        obj.save()
        
        # 从POST表单数据中提取上一页地址
        redirect_url = self.request.POST.get('previous_url', '')
        # 安全校验:仅允许跳转至本站域名下的地址,防止恶意跳转
        if not url_has_allowed_host_and_scheme(
            url=redirect_url,
            allowed_hosts={self.request.get_host()},
            require_https=self.request.is_secure()
        ):
            # 校验不通过时跳转至默认兜底页面,替换为你自己的默认路由即可
            redirect_url = reverse('entry_list')
        
        return HttpResponseRedirect(redirect_url)

3. 修正编辑表单模板

在表单中新增隐藏字段存储上一页地址,确保POST提交时参数能正常传递到后端:

<form method="POST" novalidate>
    {% csrf_token %}
    <!-- 新增隐藏字段存储上一页地址 -->
    <input type="hidden" name="previous_url" value="{{ previous_url }}">

    <div class="mb-3">
        {{ form.source|as_crispy_field }}
    </div>

    <div class="mb-3">
        {{ form.target|as_crispy_field }}
    </div>

    <div class="mb-3">
        {{ form.glossary|as_crispy_field }}
    </div>

    <div class="mb-3">
        {{ form.notes|as_crispy_field }}
    </div>

    <div class="text-center mt-3">
        <button type="submit" class="btn btn-outline-primary mx-2">Update</button>
    </div>
</form>

4. 列表页跳转链接无需修改

你原有的编辑跳转链接写法正确,只要确保最终生成的链接中previous_url是跟在?后的查询参数即可,不要把参数直接拼接到路径段中。

备用简化方案

如果不需要兼容极端的参数传递场景,可以直接从请求头中读取HTTP_REFERER字段获取来源页地址,不需要额外传参,缺点是部分浏览器、隐私插件会拦截Referer头的发送,稳定性稍差:

def form_valid(self, form):
    obj = form.save(commit=False)
    obj.updated_by = self.request.user
    obj.save()
    redirect_url = self.request.META.get('HTTP_REFERER', reverse('entry_list'))
    return HttpResponseRedirect(redirect_url)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:54:08