使用Fetch发送POST请求时DRF无法接收数据的问题排查
我正在开发一个SoundCloud克隆项目,前端使用静态文件,后端基于Django REST Framework(DRF),文件存储在AWS上。当用Fetch发送包含文件的POST请求时,后端返回错误:
django.db.utils.IntegrityError: null value in column "name" of relation "cloudsound_song" violates not-null constraint
前端通过console.log确认数据并非空值,但后端无法接收表单数据,此前还出现过500错误。
尝试给模型字段添加null=true后,请求能成功创建数据,但所有字段均为null,示例如下:
{ "id": 31, "image": null, "name": null, "audio_file":null, "created_on": "2023-01-07T03:20:55.104324Z" }
后端代码
app/views.py
class SongViewSet(viewsets.ModelViewSet): queryset = Song.objects.all() content_type ='multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' serializer_class = SongSerializer parser_classes = [parsers.MultiPartParser, parsers.FormParser, parsers.JSONParser, parsers.FileUploadParser] http_method_names = ['get', 'post', 'patch', 'delete'] def perform_create(self, serializer): serializer.save(owner=self.request.user)
app/serializers.py
class SongSerializer(serializers.HyperlinkedModelSerializer): content_type ='multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' name = serializers.CharField(required=False ) image = serializers.FileField(required=False) audio_file = serializers.FileField(required=False) class Meta : model = Song fields = ('id', 'image', 'name', 'audio_file', 'created_on') def create(self, validated_data): song = Song.objects.create( name=validated_data.get('name'), image=validated_data.get('image'), audio_file=validated_data.get('audio_file'), ) song.save() return song
app/models.py
class Song(models.Model): name = models.CharField(max_length=100, ) image = models.FileField(upload_to='./media/', default='./media/default-cover-art_tVe9r28.png' ) audio_file = models.FileField(upload_to='./media/') created_on = models.DateTimeField(auto_now_add=True) def __str__(self): return self.name
移除硬编码的content_type
你在ViewSet和Serializer中硬编码了content_type,会覆盖DRF自动处理的请求头,导致表单数据解析失败。直接删除这两行:- ViewSet中的
content_type ='multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' - Serializer中的
content_type ='multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
- ViewSet中的
修正Serializer的字段必填设置
模型中name和audio_file是必填字段(未设置null或blank),但Serializer里将它们设为required=False,会导致即使前端传值,Serializer也可能不校验,进而validated_data中无对应字段。修改如下:class SongSerializer(serializers.HyperlinkedModelSerializer): name = serializers.CharField() # 移除required=False image = serializers.FileField(required=False) audio_file = serializers.FileField() # 移除required=False class Meta : model = Song fields = ('id', 'image', 'name', 'audio_file', 'created_on', 'owner') # 新增owner字段规范前端Fetch请求格式
确保前端使用FormData传递数据,且不要手动设置Content-Type头(浏览器会自动处理multipart/form-data的boundary)。示例:const formData = new FormData(); formData.append('name', '歌曲名称'); formData.append('image', imageFile); formData.append('audio_file', audioFile); fetch('/api/songs/', { method: 'POST', body: formData, headers: { 'Authorization': 'Bearer ' + token // 仅添加必要的认证头 } });优化Serializer的create方法
Song.objects.create()已自动完成数据保存,无需额外调用song.save();同时要兼容perform_create传入的owner字段:def create(self, validated_data): return Song.objects.create(**validated_data)模型字段路径优化
模型中image的upload_to建议使用相对路径(如'covers/'),而非./media/,Django会自动结合MEDIA_ROOT处理存储路径。
内容的提问来源于stack exchange,提问作者GermBurg

