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

Django自定义用户模型更新密码URL参数报错问题咨询

问题解答

这是Django的预期行为,原因如下:

  1. 你定义的UserViewSet继承自ModelViewSet,它的update方法是为单个对象更新设计的,默认依赖视图的lookup_field(这里你设置为email)从URL的关键字参数中获取要更新的对象标识。
  2. 当你把put: update绑定到根路径''时,这个URL规则里没有<str:email>参数,Django无法获取到lookup_field所需的email关键字参数,因此抛出该错误。

解决方案

如果想实现“通过请求体中的email来更新用户”的需求,不能直接复用ModelViewSet的默认update方法,需要自定义视图逻辑:

方法一:自定义action(推荐,符合REST规范)

在UserViewSet中添加一个自定义动作,专门处理这种通过请求体标识用户的更新:

from rest_framework.decorators import action
from rest_framework.response import Response

class UserViewSet(viewsets.ModelViewSet):
    lookup_field = 'email'
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(detail=False, methods=['put'])
    def update_by_email(self, request):
        email = request.data.get('email')
        password = request.data.get('password')
        if not email or not password:
            return Response({'error': 'email和password为必填项'}, status=400)
        try:
            user = User.objects.get(email=email)
            user.set_password(password)
            user.save()
            serializer = self.get_serializer(user)
            return Response(serializer.data)
        except User.DoesNotExist:
            return Response({'error': '用户不存在'}, status=404)

然后在accounts应用的urls.py中配置该action的路由:

urlpatterns = [
    path('', views.UserViewSet.as_view({'get': 'list', 'post': 'create'}), name='user-list'),
    path('update-by-email/', views.UserViewSet.as_view({'put': 'update_by_email'}), name='user-update-by-email'),
    path('<str:email>/', views.UserViewSet.as_view({'get': 'retrieve'}), name='user-detail'),
]

方法二:重写update方法(不推荐,破坏REST规范)

如果坚持要在根路径用PUT请求,可重写update方法,手动从请求体获取email定位用户,但这种方式不符合RESTful API设计规范(单个对象更新应使用带标识的URL):

class UserViewSet(viewsets.ModelViewSet):
    lookup_field = 'email'
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def update(self, request, *args, **kwargs):
        # 忽略URL中的lookup参数,从请求体获取email
        email = request.data.get('email')
        if not email:
            return Response({'error': 'email为必填项'}, status=400)
        try:
            self.object = User.objects.get(email=email)
        except User.DoesNotExist:
            return Response({'error': '用户不存在'}, status=404)
        # 复用父类的更新逻辑
        return super().update(request, *args, **kwargs)

补充说明

RESTful API的最佳实践中,单个资源的更新应通过带资源标识的URL(如/accounts/<email>/)发起PUT请求,请求体无需重复传递email。你的初始URL配置其实更符合规范,若觉得请求体带email冗余,可修改UserSerializer将email设为只读:

class UserSerializer(ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'email', 'password']
        extra_kwargs = {
            'password': {'write_only': True},
            'email': {'read_only': True}  # 设置为只读
        }

    def create(self, validated_data):
        user = User.objects.create_user(**validated_data)
        return user

这样更新时只需在URL中传email,请求体仅传新密码即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 04:35:30