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

React+Django应用用户密码更新失败问题求助

问题描述

后端采用Django框架,前端基于React.js开发,实现用户密码更新功能时,React端提交操作失败,Django终端报错如下:

{'password': 'welcome1313'}
username
{'detail': [ErrorDetail(string='Invalid data. Expected a dictionary, but got str.', code='invalid')]}
Bad Request: /users/12/
[15/Jan/2023 15:42:10] "PATCH /users/12/ HTTP/1.1" 400 13

相关代码

React.js代码

const MyPagePasswordUpdate = () => {
  const [my_password, setValue] = useState(null);
  const [my_ID,setMyID] = useState(null);
  const isLoggedIn= useSelector(state => state.user.isLoggedIn);

  const { register, handleSubmit, errors } = useForm();

  useEffect(() => {
    async function fetchData(){
      const result = await axios.get(
        apiURL+'mypage/',
        {
          headers: {
              'Content-Type': 'application/json',
              'Authorization': `JWT ${cookies.get('accesstoken')}`
            }
        })
        .then(result => {
          setValue(result.data.password);
          setMyID(result.data.id);
        })
        .catch(err => {
          console.log("err");
        });
    }

    fetchData();
  },[]);

const update = async (data) =>{
    console.log("Body sent to server", {
      password:data.password
    })
    await axios.patch(`${apiURL}users/`+my_ID+'/',
    {
      password:data.password,
    },
    {
      headers: {
      'Content-Type': 'application/json',
      'Authorization': `JWT ${cookies.get('accesstoken')}`
      }
    }
    ).then(message => {
      alert("Updated!")
    })
    .catch(err => {
        console.log("miss");
        console.log(err);
        alert("The characters are invalid");
    });
  };

  return (
    <div>
      {isLoggedIn ?
        <div class="update-block">
          <form onSubmit={handleSubmit(update)}>
          <label for="password">Password:</label>
          <input className='form-control' type="password" {...register('password')} />
          <input className='btn btn-secondary' type="submit" value="Update" />
          </form>
          <Link to="/mypage">Back to My Pgae</Link>
        </div> :
        <div>
              Logout
        </div>
      }

    </div>
  )
};

export default MyPagePasswordUpdate;

views.py代码

class UserViewSet(viewsets.ModelViewSet):
    permission_classes = (permissions.UpdateOwnProfile,)
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def update(self, request, pk, partial=True):
            user = User.objects.get(id=pk)
            if request.data['password']:
                data = request.data['password']
                serializer = self.serializer_class(user,data=data, partial=True)
                if user.is_anonymous:
                    return Response("unauthorized", status=status.HTTP_401_UNAUTHORIZED)
                if serializer.is_valid():
                    serializer.save()
                return Response("bad request", status=status.HTTP_400_BAD_REQUEST)

            else:
                data={'state': request.data['state'], 'city': request.data['city'], 'address': request.data['address'], 'zipcode': request.data['zipcode'],'phone_number': request.data['phone_number']}
                if request.data['username'] != user.username:
                    data['username'] = request.data['username']
                if request.data['email'] != user.email:
                    data['email'] = request.data['email']

            
            serializer = self.serializer_class(user,data=data, partial=True)
  
            if user.is_anonymous:
                return Response("unauthorized", status=status.HTTP_401_UNAUTHORIZED)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_200_OK)
            print(serializer.errors)
            return Response("bad request", status=status.HTTP_400_BAD_REQUEST)

models.py代码

class User(AbstractUser):
    email = models.EmailField(blank=True, max_length=254, unique=True, verbose_name='email address')
    state = models.ForeignKey(State,verbose_name='State', on_delete=models.CASCADE, blank=True, null=True)
    city = models.CharField(verbose_name='City', max_length=20, blank=True, null=True)
    address = models.CharField(verbose_name='Address', max_length=50, blank=True, null=True)
    zipcode = models.CharField(verbose_name='Zip Code',max_length=10, blank=True, null=True)
    phone_number = PhoneField(verbose_name='Phone Number ',blank=True, null=True)
    objects = CustomUserManager()

serializers.py代码

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=False)

    class Meta:
        model = User
        fields = ('id', 'username', 'email', 'password', 'state', 'city', 'address', 'zipcode', 'phone_number')
        
    def create(self,validated_data):
            user = User.objects.create_user(
                email=validated_data['email'],
                username=validated_data['username'],
                password=validated_data['password'],
            )
            print(validated_data)
            return user

    def update(self,instance,validated_data):
        if 'password' in validated_data:
            password=validated_data.pop('password')
            instance.set_password(password)       

        return super().update(instance,validated_data)
问题原因
  1. 核心错误:Django视图的update方法中,处理密码更新时直接将request.data['password'](字符串类型)传给序列化器,但序列化器要求接收字典格式的数据(如{'password': '新密码'}),导致报错Expected a dictionary, but got str.。
  2. 额外问题:
    • if request.data['password']:写法存在风险,若请求中无password字段会直接抛出KeyError,应改用request.data.get('password')判断。
    • 密码更新分支逻辑错误:无论序列化器验证是否通过,都返回bad request响应。
    • React组件中useEffect的异步处理写法冗余,同时使用await和.then/.catch,代码不够简洁。
解决方法

1. 修复Django视图的update方法

修改views.py中的update方法,确保传给序列化器的是字典格式数据,并修正逻辑错误:

class UserViewSet(viewsets.ModelViewSet):
    permission_classes = (permissions.UpdateOwnProfile,)
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def update(self, request, pk=None, partial=True):
        user = User.objects.get(id=pk)
        # 先判断用户权限
        if user.is_anonymous:
            return Response("unauthorized", status=status.HTTP_401_UNAUTHORIZED)

        # 处理密码更新
        password = request.data.get('password')
        if password:
            # 构造字典格式数据传给序列化器
            data = {'password': password}
            serializer = self.serializer_class(user, data=data, partial=True)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_200_OK)
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        
        # 处理其他字段更新
        data = {}
        fields_to_update = ['state', 'city', 'address', 'zipcode', 'phone_number', 'username', 'email']
        for field in fields_to_update:
            value = request.data.get(field)
            # 仅当字段存在且与原数据不同时才加入更新字典
            if value is not None and value != getattr(user, field, None):
                data[field] = value
        
        serializer = self.serializer_class(user, data=data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        print(serializer.errors)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

2. 优化React组件的异步处理(可选)

简化useEffect中的异步逻辑,统一使用async/await:

useEffect(() => {
  async function fetchData(){
    try {
      const result = await axios.get(
        `${apiURL}mypage/`,
        {
          headers: {
              'Content-Type': 'application/json',
              'Authorization': `JWT ${cookies.get('accesstoken')}`
            }
        }
      );
      setValue(result.data.password);
      setMyID(result.data.id);
    } catch (err) {
      console.log("err", err);
    }
  }
  fetchData();
},[]);

3. 确认前端请求格式

前端axios.patch发送的请求体已经是正确的字典格式{password: data.password},这部分无需修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 10:40:37