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

React-Select向Django DRF提交Form Data时数组传递失败求助

问题:React+Django多对多字段通过Form Data传值失败

我正在开发React+Django应用,使用Django REST Framework(DRF)和Form Data将React表单数据提交至Django模型。因需向Django多对多字段传值,使用了支持多选的React-Select组件,但提交时遇到字段类型不匹配的问题。


后端代码

models.py

class classification(models.Model):
    theclass = models.CharField(max_length=200) 
    def __str__ (self):
        return self.theclass
    @property
    def classification_dictionary(self):
        return {'label':self.theclass , 'value': self.id}

class book(models.Model):
    name = models.CharField(max_length=499)
    bookClassification = models.ManyToManyField('classification', blank=True)
    def __str__ (self):
        return self.name

serializers.py

class BookSerializer(serializers.ModelSerializer):
    bookClassification = serializers.SlugRelatedField(
        many=True,
        read_only = True,
        slug_field='classification_dictionary'
    )
    class Meta:
        model = book
        fields = '__all__'

views.py(接口)

class BookFormViewSet(APIView):
    permission_classes = [AllowAny]
    authentication_classes = [TokenAuthentication]
    parser_classes = [MultiPartParser, FormParser, JSONParser]
    @csrf_exempt
    def post(self, request, format=None):
        serializer = BookFormSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data,status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

前端React代码

import React from "react";
import Select from 'react-select';
import axios from "axios";
import { useForm, Controller } from "react-hook-form";
import { TextField, Checkbox } from "@material-ui/core";

export default function BookForm(){
    const [classificationOptions, setClassificationOptions] = React.useState([])
    const [entryClass, setEntryClass] =React.useState([])
   
    React.useEffect (function(){
        fetch('/classification')
        .then(res => res.json())
        .then(data => {
            data.map(theclass => {
                let newClass = {value: `${theclass.id}`, label: `${theclass.theclass}`}  
                setClassificationOptions(oldArray => [...oldArray, newClass]) 
            })
        })
    }, [])

    const onSubmit= (data) => {
        console.log(data)
        let classArray = []
        data.bookClassification.map(option => {
          classArray.push(option.value)
        })
        let form_data = new FormData();
        form_data.append('name', data.name);
        form_data.append('bookClassification', classArray);
        console.log(form_data)
        axios.post('/postbook/', form_data, {
            headers: {
                'Content-Type': 'multipart/form-data'
            }
        }).then(res => {
            console.log(res.data);
        }).catch(err => {
            console.log(err.response.data);
            console.log('working')
        })
    }
        
    return(
        <div>
            <form style={styles.book}onSubmit={handleSubmit(onSubmit)}>
                <div>
                    <label className="inputLabel">
                        bookName:
                        <input className="inputForm" type="text" {...register("name", { required: true })}/>
                    </label>
                    {errors.name && <span>This field is required</span>}
                </div>

                <div>
                    <h4>book classification:</h4>
                    <Controller
                        name="bookClassification"
                        control={control}
                        rules={{ required: true }}
                        render={({ field }) => 
                            <Select
                                isMulti
                                {...field} 
                                options={classificationOptions}
                            />
                        }
                    />
                    {errors.bookClassification && <span>This field is required</span>}
                </div>
                
                <div>
                    <input type="submit" />
                </div>
            </form>
        </div>
    )
}

问题现象

前端Form Data未以数组形式传递图书分类值,而是转为字符串。提交时console.log显示:

FormData { name → "name", bookClassification → "1,3" }

后端返回错误:

bookClassification: Array [ "Incorrect type. Expected pk value, received str." ]

尝试过用JSON.stringify和数组处理bookClassification的值,但均无效,请问如何通过Form Data正确传递数组?


解决方案

1. 前端:多次append同一个字段名传递数组

FormData不直接支持数组,但可以通过重复调用append方法、使用同一个字段名来传递多个值。修改onSubmit函数:

const onSubmit= (data) => {
    console.log(data)
    let form_data = new FormData();
    form_data.append('name', data.name);
    // 循环分类选项,逐个append到同一个字段
    data.bookClassification.forEach(option => {
        form_data.append('bookClassification', option.value);
    });
    console.log(form_data)
    axios.post('/postbook/', form_data, {
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    }).then(res => {
        console.log(res.data);
    }).catch(err => {
        console.log(err.response.data);
    })
}

这样DRF的MultiPartParser会自动将同名字段的多个值解析为数组。

2. 后端:修正Serializer配置

当前BookSerializer将bookClassification设为read_only=True,这会导致DRF忽略提交的字段值,无法保存多对多关系。同时需要调整为支持写入的字段类型:

class BookSerializer(serializers.ModelSerializer):
    # 改为PrimaryKeyRelatedField,支持接收PK值数组
    bookClassification = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=classification.objects.all()
    )
    class Meta:
        model = book
        fields = '__all__'

如果需要返回数据时保持字典格式,可以新增一个只读字段:

class BookSerializer(serializers.ModelSerializer):
    # 用于接收提交数据的字段
    bookClassification = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=classification.objects.all()
    )
    # 用于返回响应的只读字段
    bookClassification_dict = serializers.SerializerMethodField()

    class Meta:
        model = book
        fields = ['name', 'bookClassification', 'bookClassification_dict']

    def get_bookClassification_dict(self, obj):
        return [item.classification_dictionary for item in obj.bookClassification.all()]

3. 修正视图中的Serializer名称

视图中使用的BookFormSerializer未在代码中定义,需替换为实际的BookSerializer:

# views.py中修改Serializer实例化代码
serializer = BookSerializer(data=request.data)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 14:24:55