Django DeleteView如何按来源页面配置正确的success_url
Django删除视图跳转优化:按来源页面返回对应列表页(含分页)
问题背景
现有URL配置
app_name = "posts" urlpatterns = [ path('global/', PostListView.as_view(), name='global-list'), path('personal/', PersonalPostListView.as_view(), name='personal-list'), path('<int:pk>/', PostDetailView.as_view(), name='detail'), path('<int:pk>/delete/', PostDeleteView.as_view(), name='delete'), ]
模板删除链接
三个视图的模板中均使用同一删除链接:
<a href="{% url 'posts:delete' object.id %}">Delete</a>
核心需求
- 从
global/(PostListView)删除后,返回原分页的全局列表页 - 从
personal/(PersonalPostListView)删除后,返回原分页的个人列表页 - 从详情页(PostDetailView)删除后,默认返回全局列表页
当前实现的问题
使用HTTP_REFERER作为跳转目标时,删除后会回到删除操作页(如posts/15/delete/),无法满足预期跳转需求,且无法保留分页参数。
解决方案
方案1:传递next参数(推荐,可靠性最高)
通过在删除链接中携带目标跳转URL的next参数,明确指定删除后的跳转地址,同时保留分页参数。
步骤1:修改各模板的删除链接
- PostListView模板:
<a href="{% url 'posts:delete' object.id %}?next={{ request.get_full_path|urlencode }}">Delete</a>
- PersonalPostListView模板:
<a href="{% url 'posts:delete' object.id %}?next={{ request.get_full_path|urlencode }}">Delete</a>
- PostDetailView模板:
<a href="{% url 'posts:delete' object.id %}?next={% url 'posts:global-list'|urlencode %}">Delete</a>
注:
request.get_full_path会包含当前页面的完整路径(含分页等查询参数),urlencode用于处理URL中的特殊字符,避免编码错误。
步骤2:修改PostDeleteView的get_success_url方法
from django.urls import reverse class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView): model = Post def get_success_url(self): # 获取携带的next参数 next_url = self.request.GET.get('next') # 校验next参数为本站内URL,防止恶意跳转 if next_url and next_url.startswith('/'): return next_url # 兜底:默认返回全局列表页 return reverse('posts:global-list')
方案2:解析HTTP_REFERER(无需修改模板)
若不想改动模板,可通过解析请求的HTTP_REFERER判断来源页面,自动生成跳转地址。此方案依赖浏览器发送referer,可靠性略低。
from django.urls import resolve, reverse from urllib.parse import urlparse class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView): model = Post def get_success_url(self): referer = self.request.META.get('HTTP_REFERER') if not referer: return reverse('posts:global-list') # 解析referer的路径部分 parsed_referer = urlparse(referer) referer_path = parsed_referer.path try: # 匹配来源页面的视图名称 match = resolve(referer_path) view_name = match.view_name # 提取分页参数(从referer的查询参数中) page_param = urlparse(referer).query.split('page=')[-1] if 'page=' in referer else None if view_name == 'posts:global-list': base_url = reverse('posts:global-list') return f"{base_url}?page={page_param}" if page_param else base_url elif view_name == 'posts:personal-list': base_url = reverse('posts:personal-list') return f"{base_url}?page={page_param}" if page_param else base_url elif view_name == 'posts:detail': return reverse('posts:global-list') except: # 解析失败时返回全局列表页 return reverse('posts:global-list') return reverse('posts:global-list')
方案对比
- 方案1:逻辑清晰,跳转目标明确,自动保留分页参数,不受浏览器referer限制,是最优选择。
- 方案2:无需修改模板,但依赖浏览器的referer机制,存在丢失或篡改风险,仅作为备选方案。
内容的提问来源于stack exchange,提问作者Jobo Fernandez
相关产品推荐
相关产品推荐

