Django REST Framework JS调用PUT报Must be Device instance错误
报错核心是DeviceConfigSerializer中device字段的配置不支持反序列化逻辑:你当前配置device = serializers.UUIDField(source='device.id')仅能满足序列化输出(把关联Device实例的id返回给前端),但处理PUT请求的反序列化逻辑时,DRF不知道要把前端传入的UUID字符串转换成对应的Device实例,会直接尝试把值赋值给DeviceConfig.device外键字段,因此触发「必须是Device实例」的错误。
你之前不传device字段返回400,是因为序列化器默认device字段为必填项,反序列化时会校验该字段是否存在。
方案1:保留body传device参数的逻辑
将序列化器中的device字段替换为SlugRelatedField,该字段会自动根据传入的slug值(这里就是Device的id)从指定查询集里匹配对应的Device实例,完美适配外键字段的读写需求:
class DeviceConfigSerializer(serializers.HyperlinkedModelSerializer): # 替换原来的UUIDField配置 device = serializers.SlugRelatedField( slug_field='id', queryset=Device.objects.all() ) class Meta: model = DeviceConfig fields = ('device','bpm', 'duty','ledState','pressureMax')
方案2:无需body传device参数(更推荐)
你的PUT请求URLhttp://localhost:81/configs/"+DEVICE_UUID已经携带了device主键作为路由参数,DRF的ModelViewSet会自动根据路由参数定位到要更新的实例,完全不需要在请求body里重复传device值。
直接将序列化器的device字段设为只读即可,既不会触发必填校验,也不会影响返回结果:
class DeviceConfigSerializer(serializers.HyperlinkedModelSerializer): device = serializers.UUIDField(source='device.id', read_only=True) class Meta: model = DeviceConfig fields = ('device','bpm', 'duty','ledState','pressureMax')
修改后前端提交请求时可以去掉object["device"] = DEVICE_UUID;这行逻辑,也不会再报400错误。
你当前视图get_queryset方法中的过滤写法不规范:
# 原写法,无意义构造了未保存的Device实例 return DeviceConfig.objects.filter(device=Device(device=device))[0:]
可以直接用外键id过滤,效率更高也更符合Django ORM规范:
return DeviceConfig.objects.filter(device_id=device)
内容的提问来源于stack exchange,提问作者voidtype

