Django添加菜单项时URL出现双斜杠//引发404错误求助
Django URL双斜杠导致404错误的修复方案
问题描述
在Django项目中添加新菜单项时,生成的URL出现双斜杠//,触发404页面未找到错误。请求URL为http://127.0.0.1:8000/restaurant/menu/desserts-menu//add_menuItem/,无法匹配项目中已定义的URLconf规则。
项目相关代码
Urls.py
from django.urls import path from restaurant import views app_name = 'restaurant' urlpatterns = [ path('', views.index, name='index'), path('about/', views.about, name = 'about'), path('menu/<slug:menu_name_slug>/add_menu_item/', views.add_menu_item, name='add_menu_item'), path('menu/<slug:menu_name_slug>/', views.show_menu, name='show_menu'), path('menu/<slug:menu_name_slug>/<slug:menuItem_name_slug>/', views.show_menu_item, name='show_menu_item'), #Template shows form but without any functionality path('add_menu/', views.add_menu, name="add_menu"), path('menu/<slug:menu_name_slug>/<slug:menuItem_name_slug>/add_review/', views.add_review, name="add_review"), path('restricted/', views.restricted, name='restricted'), ]
Views.py
def show_menu(request,menu_name_slug): context_dict = {} try: menu = Menu.objects.get(slug=menu_name_slug) menuItems = MenuItem.objects.filter(menu=menu) context_dict['menuItems'] = menuItems context_dict['menu'] = menu except Menu.DoesNotExist: context_dict['menu'] = None context_dict['menuItems'] = None return render(request, 'restaurant/menu.html', context=context_dict) def show_menu_item(request, menu_name_slug, menuItem_name_slug): context_dict = {} try: menu = Menu.objects.get(slug=menu_name_slug) menuItem = MenuItem.objects.get(slug=menuItem_name_slug) reviews = Review.objects.filter(menuItem=menuItem) context_dict['reviews'] = reviews context_dict['menuItem'] = menuItem context_dict['menu'] = menu except Menu.DoesNotExist: context_dict['menu'] = None context_dict['menuItem'] = None context_dict['reviews'] = None return render(request, 'restaurant/menu_item.html', context=context_dict) @login_required def add_menu_item(request, menu_name_slug): try: menu = Menu.objects.get(slug=menu_name_slug) except Menu.DoesNotExist: menu = None if menu is None: return redirect('/restaurant/') form = MenuItemForm() if request.method == 'POST': form = MenuItemForm(request.POST) if form.is_valid(): if menu: menuItem = form.save(commit=False) menuItem.menu = menu menuItem.save() return redirect(reverse('restaurant:show_menu', kwargs={'menu_name_slug': menu_name_slug})) else: print(form.errors) context_dict = {'form': form, 'menu': menu} return render(request, 'restaurant/add_menuItem.html', context=context_dict) @login_required def add_review(request, menu_name_slug, menuItem_name_slug): try: menuItem = MenuItem.objects.get(slug=menuItem_name_slug) except MenuItem.DoesNotExist: menuItem = None if menuItem is None: return redirect('/restaurant/') form = ReviewForm() if request.method == 'POST': form = ReviewForm(request.POST) if form.is_valid(): if menuItem: review = form.save(commit=False) review.menuItem = menuItem review.save() return redirect(reverse('restaurant:show_menu_item', kwargs={'menuItem_name_slug': menuItem_name_slug})) else: print(form.errors) context_dict = {'form': form, 'menuItem': menuItem} return render(request, 'restaurant/add_review.html', context=context_dict)
Models.py
class Menu(models.Model): MENU_ID_MAX_LENGTH=128 menuID = models.TextField(max_length=MENU_ID_MAX_LENGTH,unique=True) # primary key slug = models.SlugField(unique=True) def save(self, *args, **kwards): self.slug = slugify(self.menuID) super(Menu, self).save(*args, **kwards) def __str__(self): return self.menuID class MenuItem(models.Model): MENUITEM_ID_MAX_LENGTH=128 DESCRIPTION_MAX_LENGTH=200 menuItemID = models.CharField(max_length=MENUITEM_ID_MAX_LENGTH, unique=True) # primary key menu = models.ForeignKey(Menu, on_delete=models.CASCADE) # foreign key: one to many - many menu items for one menu description = models.CharField(max_length=DESCRIPTION_MAX_LENGTH, default="None") price = models.FloatField(default =0.0) photo = models.ImageField(upload_to='menu_item_images', blank=True) slug = models.SlugField(unique=True) def save(self, *args, **kwards): self.slug = slugify(self.menuItemID) super(MenuItem, self).save(*args, **kwards) class Meta: verbose_name_plural = 'MenuItems' def __str__(self): return self.menuItemID class Review(models.Model): REVIEW_ID_MAX_LENGTH=128 COMMENT_MAX_LENGTH=1000 reviewID = models.CharField(max_length=REVIEW_ID_MAX_LENGTH, unique=True) # primary key user = models.ForeignKey(User, on_delete=models.CASCADE) # foreign key: one to many - many reviews for one user menuItem = models.ForeignKey(MenuItem, on_delete=models.CASCADE) # foreign key: one to many - many reviews for one menu item comment = models.CharField(max_length=COMMENT_MAX_LENGTH) ratings = models.IntegerField(default =0) photo = models.ImageField(upload_to='review_images', blank=True) def __str__(self): return self.reviewID
问题分析与修复
核心问题
- 大小写不匹配:请求URL中的
add_menuItem/(大写I)与URLconf定义的add_menu_item/(下划线+小写i)不一致,Django URL匹配大小写敏感,直接导致路径无法匹配。 - 双斜杠来源:手动拼接URL时,前半路径末尾带斜杠,后半路径开头又带斜杠,比如模板中写
{{ menu.get_absolute_url }}/add_menuItem/,而get_absolute_url返回的路径本身带末尾斜杠,拼接后出现双斜杠。
修复步骤
1. 用Django官方方式生成URL(彻底避免双斜杠和拼写错误)
放弃手动拼接URL,改用url模板标签或reverse函数生成,确保路径完全匹配URLconf:
- 模板中使用
url标签:
<a href="{% url 'restaurant:add_menu_item' menu_name_slug=menu.slug %}">添加菜单项</a>
- 视图中使用
reverse函数:
from django.urls import reverse # 生成添加菜单项的URL示例 url = reverse('restaurant:add_menu_item', kwargs={'menu_name_slug': menu.slug})
2. 修正路径大小写
如果之前手动写了URL路径,将add_menuItem改为add_menu_item,与URLconf中的定义保持一致。
3. 可选:为Menu模型添加规范的URL生成方法
在Menu模型中添加get_absolute_url,统一菜单详情页的URL生成:
class Menu(models.Model): # ... 现有代码 ... def get_absolute_url(self): return reverse('restaurant:show_menu', kwargs={'menu_name_slug': self.slug})
验证修复
修改后生成的正确URL应为http://127.0.0.1:8000/restaurant/menu/desserts-menu/add_menu_item/,无斜杠重复且路径匹配,可正常访问添加菜单项页面。
内容的提问来源于stack exchange,提问作者An K
相关产品推荐
相关产品推荐

