使用Django REST Framework创建/更新Account模型遇400错误排查
问题排查:DRF创建Account模型时400/500错误
问题描述
尝试通过Django REST Framework指定id和params字段创建或更新Account模型,持续收到400或500错误码,相关代码及日志如下:
客户端代码
data = dict(id=18, params=dict(foo='bar')) res = requests.post('http://container-django-1:8002/api/account/', data=data, headers={'Authorization': 'Bearer {0}'.format(instance.bookkeeper.jwt_token)} )
服务端配置
models.py
class Account(TimestampedModel): active = models.BooleanField(null=True, blank=True, default=False) params = models.JSONField(default=dict, blank=True)
views.py
class AccountViewSet(viewsets.ModelViewSet): serializer_class = AccountSerializer queryset = Account.objects.all() http_method_names = ['post']
serializer.py
class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = ('id', 'params',) def create(self, validated_data): return Account.objects.create(id=validated_data['id'], params=validated_data['params'])
urls.py
router = routers.DefaultRouter() router.register("account", AccountViewSet, basename="accounts-list") urlpatterns = [ path('', include(router.urls)), ]
输出日志
container-django-1 | 2022-09-03T18:14:22.785593661Z Starting development server at http://0.0.0.0:8002/ container-django-1 | 2022-09-03T18:14:22.785604944Z Quit the server with CONTROL-C. container-django-1 | 2022-09-03T18:17:09.252092179Z Bad Request: /api/account/ container-django-1 | 2022-09-03T18:17:09.253001155Z Bad Request: /api/account/ container-django-1 | 2022-09-03T18:17:09.261351802Z [03/Sep/2022 18:17:09] "POST /api/account/ HTTP/1.1" 400 40
问题原因及解决方案
1. 客户端请求格式错误
当前客户端用data=data发送表单格式数据,但params是嵌套字典的JSON字段,表单格式无法正确解析嵌套结构,导致DRF序列化器验证失败。
解决方法:
将data=data改为json=data,让requests发送JSON格式的请求体:
data = dict(id=18, params=dict(foo='bar')) res = requests.post('http://container-django-1:8002/api/account/', json=data, # 改为json参数 headers={'Authorization': 'Bearer {0}'.format(instance.bookkeeper.jwt_token)} ) # 打印响应详情,定位具体错误 print(res.json())
2. 模型id字段类型不支持手动指定
默认Django模型的id是AutoField(自增主键),数据库会自动生成值,不允许手动插入指定id,这会导致创建时数据库报错(可能触发500,或序列化器验证不通过触发400)。
解决方法:
修改Account模型,将id改为手动可控的主键类型:
class Account(TimestampedModel): id = models.IntegerField(primary_key=True) # 替换默认AutoField active = models.BooleanField(null=True, blank=True, default=False) params = models.JSONField(default=dict, blank=True)
注意:如果已有数据,需生成并执行数据库迁移,确保字段类型变更兼容现有数据。
3. 序列化器与模型逻辑不匹配
自定义的create方法尝试手动指定id,但如果模型id是AutoField,数据库会拒绝该操作。另外,ModelSerializer默认已支持处理validated_data中的字段,无需手动重写create(除非有额外逻辑)。
优化方案:
移除自定义的create方法,或改为支持“存在则更新,不存在则创建”的逻辑:
class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = ('id', 'params',) def create(self, validated_data): # 实现创建/更新逻辑 account, created = Account.objects.update_or_create( id=validated_data['id'], defaults={'params': validated_data['params']} ) return account
4. 缺少更新接口配置
当前ViewSet只允许post方法(对应创建操作),如果要更新已有Account,需使用put/patch方法访问详情接口(如/api/account/18/),或在create方法中实现上述update_or_create逻辑。
内容的提问来源于stack exchange,提问作者Florent
相关产品推荐
相关产品推荐

