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

Django URL含空格/特殊字符导致404错误求助

Django搜索栏含空格/特殊字符时404问题解决

问题表现

在Django站点开发搜索栏时,传递无空格名称一切正常,但用户输入含空格或!、?等特殊字符后,URL跳转出现404错误。尝试将URL转为UTF-8编码,Django自动将空格转为%20,但问题仍存在。

具体流程:访问doctors页面调用doctor视图,提交搜索内容后重定向到包含用户输入的URL,触发doctor_search视图,此时含空格/特殊字符的输入会报错。

原代码

urls.py

from django.urls import path
from dac import views
from django.conf.urls.static import static
from django.conf import settings
from django.urls import re_path
from django.contrib import admin

app_name = 'dac'
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index, name='index'),
    path('doctors/<slug:slug>', views.doctor, name='doctors'),
    path('doctors/search/<slug:slug>/<slug:slug2>', views.doctor_search, name='doctors_search'),
    re_path(r'^doctors_search/search/(?P<slug>.)/(?P<slug2>\d+)$', views.doctor_search, name='doctors_search'),
    path('<slug:slug>', views.doctor_detail, name='doctor_detail'),
    path('signup/', views.sign_up, name="sign_up"),
    path('login/', views.log_in, name='log_in'),
    path('logout/', views.log_out, name='log_out'),
    path('reports/', views.reports, name='reports'),
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

views.py

def doctor(request, slug):
    print("slug: ",slug)

    if request.method == 'POST':
        print("slug: ",slug)
        risposta=request.POST.get("textAnswer")
        risposta=risposta.upper()

        risposta_list=risposta.rsplit()
        print(risposta_list)

        actual_url=request.build_absolute_uri()
        url=re.sub("\d+$", "search/%s/1"%(risposta), actual_url)

        return HttpResponseRedirect(url.encode('utf-8'))
        
    slug=int(slug)
    max_slug=int(round(doctors.objects.count()/28,0))

    if slug==1:
        prew_slug=slug
    else:
        prew_slug=slug-1

    if slug==max_slug:
        nxt_slug=max_slug
    else:
        nxt_slug=slug+1

    doc_list=doctors.objects.all()[(slug-1)*28:slug*28]
    {"doc_list":doc_list}

    return render(request, 'dac/doctors.html', locals())

def doctor_search(request, slug, slug2):
    slug2=int(slug2)
    print("slug1: ",slug)
    print("slug2: ",slug2)

    if request.method=="POST":
        risposta=request.POST.get("textAnswer")
        risposta=risposta.upper()

        actual_url=request.build_absolute_uri()
        url=actual_url.replace(slug, risposta)

        return HttpResponseRedirect(url.encode('utf-8'))

    slug=urllib.parse.unquote(slug)
    doc_list=doctors.objects.filter(frst_nm=slug) | doctors.objects.filter(lst_nm=slug) | doctors.objects.filter(pri_spec=slug)

    if doc_list.count()==0:
        messages.error(request, "no result")

    max_slug=int(round(doc_list.count()/28,0))
    
    if slug2==1:
        prew_slug=slug2
    else:
        prew_slug=slug2-1

    if slug2==max_slug:
        nxt_slug=max_slug
    else:
        nxt_slug=slug2+1
    doc_list=doc_list.all()[(slug2-1)*28:slug2*28]

    {"doc_list":doc_list}

    return render(request, 'dac/doctors_search.html', locals())

问题原因

  1. Slug转换器限制:Django的<slug:slug>转换器仅允许字母、数字、下划线和连字符,空格、!、?等特殊字符(即使编码为%20)无法匹配该规则,直接导致URL路由不匹配,返回404。
  2. URL拼接方式错误:视图中通过re.sub和字符串replace手动拼接URL,容易出现路径格式错误,且无法正确处理特殊字符的编码逻辑。
  3. 正则路由规则错误:re_path中的(?P<slug>.)仅匹配单个字符,无法匹配包含多个字符的搜索内容,导致路由失效。

解决方案

1. 修改URL配置

将路由中的slug转换器替换为str(允许任意非空字符串),并修正正则路由的匹配规则:

from django.urls import path, re_path
from dac import views
from django.conf.urls.static import static
from django.conf import settings
from django.contrib import admin

app_name = 'dac'
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index, name='index'),
    # 医生列表页用int类型更语义化,原逻辑中该参数是页码
    path('doctors/<int:page>', views.doctor, name='doctors'),
    # 搜索页用str接收搜索关键词,int接收页码
    path('doctors/search/<str:search_query>/<int:page>', views.doctor_search, name='doctors_search'),
    # 修正正则路由的路径和匹配规则,.*匹配任意长度字符
    re_path(r'^doctors/search/(?P<search_query>.*)/(?P<page>\d+)$', views.doctor_search, name='doctors_search'),
    path('<slug:slug>', views.doctor_detail, name='doctor_detail'),
    path('signup/', views.sign_up, name="sign_up"),
    path('login/', views.log_in, name='log_in'),
    path('logout/', views.log_out, name='log_out'),
    path('reports/', views.reports, name='reports'),
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

2. 修正视图中的重定向逻辑

使用Django的reverse函数生成正确的URL,避免手动拼接,同时自动处理特殊字符编码:

from django.urls import reverse
from django.shortcuts import redirect, render
from django.contrib import messages

def doctor(request, page):
    print("page: ", page)

    if request.method == 'POST':
        risposta = request.POST.get("textAnswer").upper()
        # 用reverse生成搜索页URL,自动处理编码
        search_url = reverse('dac:doctors_search', args=[risposta, 1])
        return redirect(search_url)
        
    max_page = int(round(doctors.objects.count()/28, 0))
    prev_page = page if page == 1 else page - 1
    next_page = max_page if page == max_page else page + 1
    doc_list = doctors.objects.all()[(page-1)*28 : page*28]

    return render(request, 'dac/doctors.html', locals())

def doctor_search(request, search_query, page):
    print("search_query: ", search_query)
    print("page: ", page)

    if request.method == 'POST':
        risposta = request.POST.get("textAnswer").upper()
        # 同样用reverse生成新的搜索URL
        search_url = reverse('dac:doctors_search', args=[risposta, 1])
        return redirect(search_url)

    # str转换器已自动解码%20为空格,无需手动unquote
    doc_list = doctors.objects.filter(frst_nm=search_query) | doctors.objects.filter(lst_nm=search_query) | doctors.objects.filter(pri_spec=search_query)

    if not doc_list.exists():
        messages.error(request, "无匹配结果")

    max_page = int(round(doc_list.count()/28, 0))
    prev_page = page if page == 1 else page - 1
    next_page = max_page if page == max_page else page + 1
    doc_list = doc_list[(page-1)*28 : page*28]

    return render(request, 'dac/doctors_search.html', locals())

3. 额外优化点

  • 医生列表页的参数用int:page更语义化,原逻辑中该参数是页码,而非slug。
  • 使用doc_list.exists()替代doc_list.count()判断是否有结果,性能更优。
  • 移除不必要的risposta_list变量和无效的字典定义{"doc_list":doc_list}。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 14:54:19