Django导航栏下拉菜单调用show_profile_page遇NoReverseMatch错误
Reverse for 'show_profile_page' with arguments '('',)' not found. 1 pattern(s) tried: ['subs/(?P[0-9]+)/profile/\Z']
我在导航栏添加「编辑设置/查看个人资料页」下拉选项时遇到上述错误,该功能之前在其他模板中调用正常,放到下拉菜单后就报错,求排查方向。
urls.py(subs应用)
from django.urls import path from .views import UserRegisterView, UserEditView, PasswordsChangeView, ShowProfilePageView, EditProfilePageView from . import views urlpatterns = [ path('register/', UserRegisterView.as_view(), name='register'), path('edit_profile/', UserEditView.as_view(), name='edit_profile'), path('password/', PasswordsChangeView.as_view(template_name='registration/change-password.html')), path('password_success', views.password_success, name="password_success"), path('<int:pk>/profile/', ShowProfilePageView.as_view(), name="show_profile_page"), path('<int:pk>/edit_profile_page/', EditProfilePageView.as_view(), name="edit_profile_page") ]
models.py
from django.db import models from django.conf import settings from django.contrib.auth.models import User from django.urls import reverse from datetime import datetime, date from ckeditor.fields import RichTextField class Post(models.Model): title = models.CharField(max_length=255) title_tag = models.CharField(max_length=255) header_image = models.ImageField(null=True, blank=True, upload_to="images/") description = models.CharField(max_length=50) author = models.ForeignKey(User, on_delete=models.CASCADE) body = RichTextField(blank=True, null=True) timestamp = models.DateField(auto_now_add=True) category = models.CharField(max_length=255, default='News') likes = models.ManyToManyField(User, related_name='post_user') def total_likes(self): return self.likes.count() def __str__(self): return self.title + ' | ' + str(self.author) def get_absolute_url(self): return reverse('home') class Category(models.Model): name = models.CharField(max_length=255) def __str__(self): return self.name def get_absolute_url(self): return reverse('home') class Profile(models.Model): user = models.OneToOneField(User, null=True, on_delete=models.CASCADE) bio = models.TextField() profile_pic = models.ImageField(null=True, blank=True, upload_to="images/profile") site_url = models.CharField(max_length=255, null=True, blank=True) facebook_url = models.CharField(max_length=255, null=True, blank=True) instagram_url = models.CharField(max_length=255, null=True, blank=True) def __str__(self): return str(self.user)
views.py(subs应用)
from django.shortcuts import render, get_object_or_404 from django.views import generic from django.views.generic import DetailView from django.contrib.auth.forms import UserCreationForm, UserChangeForm, PasswordChangeForm from django.urls import reverse_lazy from .forms import SignUpForms, EditProfileForm, PasswordUpdateForm from django.contrib.auth.views import PasswordChangeView from nooz2.models import Profile class UserRegisterView(generic.CreateView): form_class = SignUpForms template_name = 'registration/register.html' success_url = reverse_lazy('login') class UserEditView(generic.UpdateView): form_class = EditProfileForm template_name = 'registration/edit_profile.html' success_url = reverse_lazy('home') def get_object(self): return self.request.user class PasswordsChangeView(PasswordChangeView): form_class = PasswordUpdateForm success_url = reverse_lazy('password_success') def password_success(request): return render(request, 'registration/password_success.html', {}) class ShowProfilePageView(DetailView): model = Profile template_name = 'registration/user_profile.html' def get_context_data(self, *args, **kwargs): context = super(ShowProfilePageView, self).get_context_data(*args, **kwargs) page_user = get_object_or_404(Profile, id=self.kwargs['pk']) context["page_user"] = page_user return context class EditProfilePageView(generic.UpdateView): model = Profile template_name = 'registration/edit_profile_page.html' fields = ['bio', 'profile_pic', 'site_url', 'facebook_url', 'instagram_url'] success_url = reverse_lazy('home')
forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, PasswordChangeForm from django.contrib.auth.models import User from django import forms class SignUpForms(UserCreationForm): email = forms.EmailField(widget=forms.EmailInput(attrs={'class': 'form-control'})) first_name = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'})) last_name = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'})) class Meta: model = User fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2') def __init__(self, *args, **kwargs): super(SignUpForms, self).__init__(*args, **kwargs) self.fields['username'].widget.attrs['class'] = 'form-control' self.fields['password1'].widget.attrs['class'] = 'form-control' self.fields['password2'].widget.attrs['class'] = 'form-control' class EditProfileForm(UserChangeForm): email = forms.EmailField(widget=forms.EmailInput(attrs={'class': 'form-control'})) first_name = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'})) last_name = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'})) username = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'})) last_login = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'})) is_superuser = forms.CharField(max_length=100, widget=forms.CheckboxInput(attrs={'class': 'form-check'})) is_staff = forms.CharField(max_length=100, widget=forms.CheckboxInput(attrs={'class': 'form-check'})) is_active = forms.CharField(max_length=100, widget=forms.CheckboxInput(attrs={'class': 'form-check'})) date_joined = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'form-control'})) class Meta: model = User fields = ('username', 'first_name', 'last_name', 'email', 'password', 'is_superuser', 'is_staff', 'is_active', 'date_joined') class PasswordUpdateForm(PasswordChangeForm): old_password = forms.CharField(max_length=100, widget=forms.PasswordInput(attrs={'class': 'form-control', 'type':'password'})) new_password1 = forms.CharField(max_length=100, widget=forms.PasswordInput(attrs={'class': 'form-control', 'type':'password'})) new_password2 = forms.CharField(max_length=100, widget=forms.PasswordInput(attrs={'class': 'form-control', 'type':'password'})) class Meta: model = User fields = ('old_password', 'new_password1', 'new_password2')
navbar.html
<nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <div class="container-fluid"> <a class="navbar-brand" href="{% url 'home' %}">Nooz</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNavDropdown"> <ul class="navbar-nav"> <!-- Dropdown code for categories --> {% if category_menu %} <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"> Categories </a> <ul class="dropdown-menu"> <!-- generates list of categories --> {% for item in category_menu %} <li><a class="dropdown-item" href="{% url 'category' item|slugify|title %}">{{ item }}</a></li> {% endfor %} </ul> </li> {% endif %} {% if user.is_authenticated %} <li class="nav-item"> <a class="nav-link" href="{% url 'new_post' %}">Add Post</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'add_category' %}">Add Category</a> </li> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"> Profile </a> <ul class="dropdown-menu dropdown-menu-dark"> <li><a class="nav-link dropdown-item" href="{% url 'edit_profile' %}">Settings</a></li> <li><a class="nav-link dropdown-item" href="{% url 'show_profile_page' user.profile.id %}">View Profile Page</a></li> <li><a class="nav-link dropdown-item" href="{% url 'edit_profile_page' user.profile.id %}">Edit Profile Page</a></li> <li><a class="nav-link dropdown-item" href="{% url 'logout' %}">Logout</a></li> </ul> </li> {% else %} <li class="nav-item"> <a class="nav-link" href="{% url 'register' %}">Register</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'login' %}">Login</a> </li> {% endif %} </ul> </div> </div> </nav>
已尝试操作
- 检查拼写错误
- 修改urls.py添加后缀/
- 移除user.profile.id参数测试
排查与解决建议
核心原因
错误提示arguments '('',)'说明传递给show_profile_page的参数是空字符串,问题出在user.profile.id:你在Profile模型中设置了user = models.OneToOneField(User, null=True, ...),意味着部分用户可能没有关联的Profile实例,此时user.profile会被模板静默处理为None,导致user.profile.id变成空值,无法匹配URL中要求的<int:pk>整数参数。
解决步骤
自动为新用户创建Profile实例
在subs应用中新建signals.py,添加信号监听用户创建事件,自动生成对应Profile:from django.db.models.signals import post_save from django.contrib.auth.models import User from django.dispatch import receiver from .models import Profile @receiver(post_save, sender=User) def create_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(user=instance) @receiver(post_save, sender=User) def save_profile(sender, instance, **kwargs): instance.profile.save()然后在
subs/apps.py的ready()方法中注册信号:from django.apps import AppConfig class SubsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'subs' def ready(self): import subs.signals这样新注册的用户会自动生成Profile,避免
user.profile不存在的情况。处理已有无Profile的用户
进入Django shell,为没有Profile的用户批量创建:from django.contrib.auth.models import User from subs.models import Profile for user in User.objects.all(): Profile.objects.get_or_create(user=user)模板中增加存在性判断(可选)
为了更健壮,在模板中先判断Profile是否存在再渲染链接:<ul class="dropdown-menu dropdown-menu-dark"> <li><a class="nav-link dropdown-item" href="{% url 'edit_profile' %}">Settings</a></li> {% if user.profile %} <li><a class="nav-link dropdown-item" href="{% url 'show_profile_page' user.profile.id %}">View Profile Page</a></li> <li><a class="nav-link dropdown-item" href="{% url 'edit_profile_page' user.profile.id %}">Edit Profile Page</a></li> {% endif %} <li><a class="nav-link dropdown-item" href="{% url 'logout' %}">Logout</a></li> </ul>优化URL参数(可选)
可以把URL中的pk改为用户ID,这样即使Profile不存在也能通过用户ID获取(但需要修改视图逻辑):- 修改urls.py:
path('<int:user_pk>/profile/', ShowProfilePageView.as_view(), name="show_profile_page"), - 修改视图:
class ShowProfilePageView(DetailView): model = Profile template_name = 'registration/user_profile.html' def get_object(self): return get_object_or_404(Profile, user_id=self.kwargs['user_pk']) - 模板中改为:
{% url 'show_profile_page' user.id %}
- 修改urls.py:
内容的提问来源于stack exchange,提问作者mpysys

