Django中通过User UUID更新Profile时出现404错误的解决咨询
问题描述
我希望通过传递User UUID作为关键字参数来更新用户Profile,配置的URL如下:
path("profile/update/<uuid:pk>", UpdateProfile.as_view(), name="update_profile"),
但尝试更新时出现404错误,View代码如下:
class UpdateProfile(LoginRequiredMixin, UpdateView): model = Profile user_type_fields = { "Buyer": ["photo", "first_name", "last_name", "city"], "Celler": ["photo", "name", "city", "address"], } def get(self, request, *args, **kwargs): print(kwargs) self.fields = self.user_type_fields[get_user_model().objects.get(pk=kwargs["pk"]).type] return super().get(request, *args, **kwargs)
错误信息:
Page not found (404)
No profile found matching the query
我清楚这是因为Django默认用URL中的UUID去查找Profile,但找不到对应记录。如果把View的model改成User,又无法访问Profile模型的字段。目前只能用Profile ID作为参数,但出于安全考虑不想这么做。请问如何通过User UUID作为关键字参数来更新Profile?
补充:User和Profile模型代码
class CustomUser(AbstractBaseUser, PermissionsMixin): class UserTypeChoices(models.TextChoices): SINGLE_VOLUNTEER = "Single Volunteer", _("Single Volunteer") VOLUNTEERS_ORGANISATION = "Volunteers Organisation", _("Volunteers Organisation") CIVIL_PERSON = "Civil Person", _("Civil Person") MILITARY_PERSON = "Military Person", _("Military Person") type = models.CharField( max_length=23, choices=UserTypeChoices.choices, ) uuid = models.UUIDField( primary_key=True, default=uuid4, unique=True, db_index=True, editable=False, ) email = models.EmailField( _("email address"), null=True, blank=True, ) phone = PhoneNumberField( _("phone"), null=True, blank=True, ) is_staff = models.BooleanField( _("staff status"), default=False, help_text=_("Designates whether the user can log into this admin site."), ) is_active = models.BooleanField( _("active"), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."), ) def __str__(self): if self.email: return str(self.email) else: return str(self.phone) USERNAME_FIELD = "email" objects = CustomUserManager()
class Profile(models.Model): user = models.OneToOneField(to="accounts.CustomUser", on_delete=models.CASCADE, blank=True, null=True) photo = models.ImageField(upload_to="profile/", blank=True, null=True, default="profile/profile_default.png") name = models.CharField(_("name"), max_length=150, blank=True, null=True, default=None) first_name = models.CharField(_("first name"), max_length=150, blank=True, null=True, default=None) last_name = models.CharField(_("last name"), max_length=150, blank=True, null=True, default=None) city = models.CharField(_("city"), max_length=150, blank=True, null=True, default=None) address = PlainLocationField() def __str__(self): if self.user.email: return str(self.user.email) else: return str(self.user.phone)
解决方案
核心思路
重写UpdateView的get_object方法,让视图通过URL中的User UUID找到关联的Profile,而非默认用pk查找Profile本身。
方法一:重写get_object(推荐)
修改UpdateProfile视图,添加get_object方法:
class UpdateProfile(LoginRequiredMixin, UpdateView): model = Profile # 注意:用户类型要和CustomUser的UserTypeChoices完全匹配,原代码中的Buyer/Celler是笔误 user_type_fields = { "Single Volunteer": ["photo", "first_name", "last_name", "city"], "Volunteers Organisation": ["photo", "name", "city", "address"], "Civil Person": ["photo", "first_name", "last_name", "city", "address"], "Military Person": ["photo", "first_name", "last_name", "city", "address"], } def get_object(self, queryset=None): # 通过URL中的pk(User UUID)获取用户 user = get_user_model().objects.get(pk=self.kwargs["pk"]) # 自动创建Profile(如果用户还没有的话),避免404 profile, created = Profile.objects.get_or_create(user=user) # 可选:限制只能修改自己的Profile,增强安全性 if user != self.request.user: raise PermissionDenied() return profile def get(self, request, *args, **kwargs): user = get_user_model().objects.get(pk=self.kwargs["pk"]) self.fields = self.user_type_fields[user.type] return super().get(request, *args, **kwargs)
方法二:通过关联字段查找
如果不想修改get_object,可以利用Django的关联查询字段配置:
- 修改URL参数名(可选,让语义更清晰):
path("profile/update/<uuid:user_uuid>", UpdateProfile.as_view(), name="update_profile"),
- 视图中指定通过Profile的
user__uuid字段匹配URL参数:
class UpdateProfile(LoginRequiredMixin, UpdateView): model = Profile slug_field = "user__uuid" # 关联到User的uuid字段 slug_url_kwarg = "user_uuid" # URL中对应的参数名 user_type_fields = { # 匹配UserTypeChoices的字段配置 "Single Volunteer": ["photo", "first_name", "last_name", "city"], "Volunteers Organisation": ["photo", "name", "city", "address"], } def get(self, request, *args, **kwargs): user = get_user_model().objects.get(pk=self.kwargs["user_uuid"]) self.fields = self.user_type_fields[user.type] return super().get(request, *args, **kwargs)
关键注意点
- 确保
user_type_fields的键和CustomUser.UserTypeChoices的选项完全一致,否则会抛出KeyError - 用
get_or_create处理Profile不存在的情况,避免出现404 - 加上权限验证(比如只能修改自己的Profile),防止越权操作
内容的提问来源于stack exchange,提问作者Алексей Василенко
相关产品推荐
相关产品推荐

