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

React客户端向DRF服务器发送PUT请求更新数据失败求助

问题描述

React客户端向DRF服务器发起PUT请求更新数据时失败,返回序列化数据不合法的错误。创建功能正常,排查后发现问题出在文件(图片、音频)字段:GET请求获取的文件数据是字符串路径,而非File对象,导致更新时校验不通过。需要实现两个目标:

  1. 用户仅更新标题、描述等字段时,无需重新上传文件
  2. 了解如何将字符串路径转换为File对象,用于表单字段预填充

GET请求代码

const getSongGenre=async()=>{
    const token=localStorage.getItem('token')
    const res =await fetch(`http://127.0.0.1:8000/update/${param.id}`,{
        method:'GET',
        headers:{'Authorization':`Token ${token}`},
    })
    const data= await res.json()
    console.log(data)

    setSongGenre(data.genre)
    
    setSongDetail({
        artist_name:data.music.artist_name,
        title:data.music.title,
        audioFile:data.music.audio,
        imageFile:data.music.image,
        description:data.music.description,
        genre:data.music.genre,
    })

    formik.setFieldValue('artist_name',data.music.artist_name)
    formik.setFieldValue('title',data.music.title)
    formik.setFieldValue('audio',data.music.audio)
    formik.setFieldValue('image',data.music.image)
    formik.setFieldValue('description',data.music.description)
    formik.setFieldValue('genre',data.music.genre)
}

PUT请求代码

const formik=useFormik({
    initialValues:{
        artist_name:'',
        title:"",
        description:"",
        genre:"",
    },
    
    validationSchema:Yup.object({
        artist_name:Yup.string().required(),
        title:Yup.string().required(),
    }),
    
    onSubmit:((values)=>{
        const token=localStorage.getItem('token')

        const data=new FormData()
        data.append('artist_name',values.artist_name)
        data.append('title',values.title)
        data.append('audio',values.audio)
        data.append('image',values.image)
        data.append('description',values.description)
        data.append('genre',values.genre)

        console.log(data)
        console.log(formik.values)
        fetch(`http://127.0.0.1:8000/update/${param.id}`,{
            method:"PUT",
            body:data,
            headers:{
                'Authorization':`Token ${token}`,
            },
        }).then(res=>res.json())
        .then(data=>{
            try{
                console.log(data)
                formik.setFieldValue('artist_name','')
                formik.setFieldValue('title','')
                formik.setFieldValue('audio','')
                formik.setFieldValue('image','')
                formik.setFieldValue('description','')
                formik.setFieldValue('genre','')

                if(data.status === false){
                    document.getElementById('upload_msg').style.display='block'
                    document.getElementById('upload_msg').innerHTML=`<li style=color:red>${data.message}</li>`

                    setTimeout(()=>{
                        if(document.getElementById('upload_msg').style.display=='block'){
                            document.getElementById('upload_msg').style.display='none'
                        }
                    },10000)
                }
            }catch(error){
                console.error(error)
            }
        })
    }),
})
解决方案

一、实现无需重新上传文件即可更新其他字段

1. 修改DRF序列化器

将文件字段的required设为False,允许更新时不传文件:

from rest_framework import serializers
from .models import Music

class MusicSerializer(serializers.ModelSerializer):
    class Meta:
        model = Music
        fields = ['artist_name', 'title', 'audio', 'image', 'description', 'genre']
        extra_kwargs = {
            'audio': {'required': False},
            'image': {'required': False}
        }

2. 前端PUT请求时只传有效文件字段

在FormData中仅添加用户新上传的File对象,字符串路径直接跳过:

// 修改onSubmit中的FormData构建逻辑
const data=new FormData()
data.append('artist_name',values.artist_name)
data.append('title',values.title)
data.append('description',values.description)
data.append('genre',values.genre)

// 仅当audio是File对象时才添加,字符串路径则跳过
if (values.audio instanceof File) {
    data.append('audio', values.audio)
}
// 图片字段同理处理
if (values.image instanceof File) {
    data.append('image', values.image)
}

后端收到请求后,会只更新传入的字段,原有文件保持不变。

二、将字符串路径转换为File对象用于表单预填充

浏览器无法直接将本地路径转为File对象,可通过请求后端获取文件Blob再转换:

1. 封装路径转File的工具函数

async function urlToFile(url, filename, mimeType) {
    const response = await fetch(url)
    const blob = await response.blob()
    return new File([blob], filename, { type: mimeType })
}

2. 在GET请求中转换并赋值

修改getSongGenre函数,将获取到的文件路径转为File对象后再设置到Formik:

const getSongGenre=async()=>{
    const token=localStorage.getItem('token')
    const res =await fetch(`http://127.0.0.1:8000/update/${param.id}`,{
        method:'GET',
        headers:{'Authorization':`Token ${token}`},
    })
    const data= await res.json()
    console.log(data)

    setSongGenre(data.genre)

    // 提取文件名并转换音频路径为File对象
    const audioFilename = data.music.audio.split('/').pop()
    const audioFile = await urlToFile(data.music.audio, audioFilename, 'audio/mpeg') // 根据实际音频类型调整MIME
    
    // 转换图片路径为File对象
    const imageFilename = data.music.image.split('/').pop()
    const imageFile = await urlToFile(data.music.image, imageFilename, 'image/jpeg') // 根据实际图片类型调整MIME

    setSongDetail({
        artist_name:data.music.artist_name,
        title:data.music.title,
        audioFile: audioFile,
        imageFile: imageFile,
        description:data.music.description,
        genre:data.music.genre,
    })

    formik.setFieldValue('artist_name',data.music.artist_name)
    formik.setFieldValue('title',data.music.title)
    formik.setFieldValue('audio', audioFile)
    formik.setFieldValue('image', imageFile)
    formik.setFieldValue('description',data.music.description)
    formik.setFieldValue('genre',data.music.genre)
}

注意:需根据实际文件类型设置正确的MIME类型,大文件转换会消耗带宽,需考虑性能影响。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 07:05:00