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)
问题原因
- 核心错误:Django视图的
update方法中,处理密码更新时直接将request.data['password'](字符串类型)传给序列化器,但序列化器要求接收字典格式的数据(如{'password': '新密码'}),导致报错Expected a dictionary, but got str.。 - 额外问题:
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
相关产品推荐
相关产品推荐

