Django REST Framework上传图片报错:提交数据非文件
问题原因
报错核心是3处代码错误+测试请求方式错误,Django admin走原生 Django 逻辑不经过DRF的序列化、解析器流程,所以不受配置错误影响可以正常上传:
- 视图层解析器配置属性名拼写错误:DRF视图指定解析器的属性为
parser_classes(复数),代码中写为parser_class(单数),导致配置的MultiPartParser文件解析器完全不生效,无法识别multipart格式的文件上传请求。 - 序列化器字段声明错误:显式声明
bloodbank_logo字段时直接赋值serializers.ImageField类本身,没有加括号实例化,字段校验逻辑异常。 - 测试请求逻辑完全错误:
- 用
json=data参数发请求时,请求编码为application/json,该格式不支持二进制文件传输; - 传入的
bloodbank_logo是本地文件路径字符串,不是真实的文件二进制内容,后端拿到的只是普通文本,自然判定不是合法文件。
- 用
- 额外隐藏bug:序列化器中
bloodbanks_user字段的source配置为user.username,但模型中外键字段名为bloodbank_user,不存在user属性,请求走到保存逻辑时会直接抛字段不存在的错误。
修复方案
按以下顺序修改对应代码即可:
1. 修改序列化器 serializers.py
修正字段实例化问题和source配置错误:
from rest_framework import serializers from bloodbank_api.models import Bloodbanks class BloodbankdataSerializer(serializers.ModelSerializer): # 修正source路径,匹配模型中的bloodbank_user外键字段 bloodbanks_user=serializers.CharField(source='bloodbank_user.username',read_only=True) url = serializers.HyperlinkedIdentityField( view_name='bloodbank-detail-id', lookup_field='pk' ) # 字段声明必须实例化,加();如果不需要自定义校验规则,这行可以直接删除,ModelSerializer会自动映射ImageField bloodbank_logo=serializers.ImageField() class Meta: model=Bloodbanks fields=['id','bloodbanks_user','fullbankname','apositive', 'anegative','bpositive','bnegative','abpositive', 'abnegative','opositive','onegative','latitude', 'longitude','state','district','location','website', 'bloodbank_logo','bloodbank_description','bloodbank_phone','url']
2. 修改视图层 views.py
修正解析器配置的属性名拼写:
from bloodbank_api.models import Bloodbanks from bloodbank_api.serializers import BloodbankdataSerializer from rest_framework import mixins,generics from rest_framework.parsers import FormParser, MultiPartParser,JSONParser class BloodbankDataDetail(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): queryset = Bloodbanks.objects.all() serializer_class = BloodbankdataSerializer # 修正属性名为parser_classes(复数),配置才会生效 parser_classes=(MultiPartParser, FormParser, JSONParser) lookup_field = 'pk' def get(self, request,*args, **kwargs): id=kwargs.get('pk') if id is not None: return self.retrieve(request,*args, **kwargs) return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) def perform_create(self, serializer): serializer.save(bloodbank_user=self.request.user)
3. 修改Python测试脚本
文件上传不能用JSON格式传参,需要拆分普通字段和文件字段,用data传普通参数、files传打开的文件对象,requests会自动设置正确的multipart/form-data请求头:
import requests endpoint='http://127.0.0.1:8000/bloodbank/' # 普通表单字段 data={ "fullbankname": "Dirgahyu", "apositive": 1, "anegative": 1, "bpositive": 1, "bnegative": 1, "abpositive": 1, "abnegative": 1, "opositive": 1, "onegative": 1, "latitude": "dhdhd", "longitude": "hdhdd", "state": "dhhd", "district": "dhdhgd", "location": "dhdhdhd", "website": "jdjdjd", "bloodbank_description": "hahaha", "bloodbank_phone": "1234567890" # 注意该字段max_length=10,不要传超过10位的内容 } # 二进制打开本地图片,作为文件参数传入 with open("/Users/aayushdipgiri/aayu's code/opencv/cat.jpeg", "rb") as img_file: files = {"bloodbank_logo": img_file} # 禁止用json=参数,用data和files传参 resp = requests.post(endpoint, data=data, files=files) print(resp.json())
4. Postman测试注意事项
不要选raw-JSON请求模式,选择form-data模式:
- 普通文本字段直接选
Text类型填值 bloodbank_logo字段类型选File,从本地选择要上传的图片即可
内容的提问来源于stack exchange,提问作者Aayush Dip Giri
相关产品推荐
相关产品推荐

