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
相关产品推荐
相关产品推荐

