You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django REST Framework中many=True字段表单验证失败问题排查

问题:DRF POST请求中items字段验证失败(提示必填)

我在将Django REST Framework作为Astro前端的API使用时,发起POST请求遇到验证错误:{'items': [ErrorDetail(string='This field is required.', code='required')]}。

明明修改后的request.data里已经包含了items字段且格式看似正确,却还是报错,想知道原因以及如何确定多嵌套字段的正确格式。


相关代码

views.py

from django.shortcuts import render

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Ticket, Item
from .serializers import TicketSerializer, ItemSerializer
import json
# Create your views here.

class TicketView(APIView):
    def post(self, request, format=None):

        items = request.data.pop('items')[0]
        # Items are given as a string with the json format. We need to parse it.
        items = json.loads(items)[0]
        # Add the items to the request data
        data = request.data
        data['items'] = items
        
        data.pop('itemName0')
        data.pop('itemPrice0')
        data.pop('itemCount0')
        print(data)
        serializer = TicketSerializer(data=data)
        
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        print(serializer.errors)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Serializers.py

from .models import Ticket, Item
from rest_framework import serializers

class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = ('name', 'price', 'count')
class TicketSerializer(serializers.ModelSerializer):
    items = ItemSerializer(many=True)

    class Meta:
        model = Ticket
        fields = ('img', 'subtotal', 'service', 'tax', 'etc', 'total', 'items')

    def create(self, validated_data):
        items_data = validated_data.pop('items')
        ticket = Ticket.objects.create(**validated_data)
        for item_data in items_data:
            Item.objects.create(ticket=ticket, **item_data)
        return ticket

修改后request.data输出

<QueryDict: {'subtotal': ['12'], 'service': ['12'], 'tax': ['12'], 'etc': ['12'], 'total': ['120'], 'img': [<InMemoryUploadedFile: photo_2024-01-17 23.39.53.jpeg (image/jpeg)>, <InMemoryUploadedFile: photo_2024-01-17 23.39.53.jpeg (image/jpeg)>], 'items': [{'name': 'mei', 'count': '012', 'price': '012'}]}>

问题原因

  1. 字段格式不匹配:你的TicketSerializer中items字段设置了many=True,意味着它必须接收列表(数组)格式的Item数据。但你在代码中执行了items = json.loads(items)[0],将解析后的Item列表取了第一个元素,变成了单个字典,后续即使将其放入列表,QueryDict的特性可能导致Serializer无法正确识别格式。
  2. QueryDict的不可变性:request.data是DRF封装的QueryDict,默认不可直接修改,直接赋值data['items']可能无法被Serializer正确读取。

解决步骤

1. 修正items格式处理

确保最终传入Serializer的items是列表类型,哪怕只有一个Item:

# 取出前端传的json字符串,不要直接取列表第一个元素
items_str = request.data.pop('items')[0]
# 解析为列表格式
items = json.loads(items_str)
# 兜底处理:如果解析后不是列表,转为列表
if not isinstance(items, list):
    items = [items]

2. 转换QueryDict为普通字典

将不可变的QueryDict转为普通字典,确保修改能生效:

data = dict(request.data)
# 移除冗余字段
for key in ['itemName0', 'itemPrice0', 'itemCount0']:
    data.pop(key, None)
# 处理文件字段:QueryDict中文件是列表,取第一个(前端只传一个文件的情况)
if 'img' in data:
    data['img'] = data['img'][0]
# 赋值items
data['items'] = items

修正后的完整视图代码

class TicketView(APIView):
    def post(self, request, format=None):
        # 处理items字段
        items_str = request.data.pop('items')[0]
        items = json.loads(items_str)
        if not isinstance(items, list):
            items = [items]
        
        # 转换为普通字典
        data = dict(request.data)
        # 移除冗余字段
        for key in ['itemName0', 'itemPrice0', 'itemCount0']:
            data.pop(key, None)
        
        # 处理文件字段
        if 'img' in data:
            data['img'] = data['img'][0]
        
        data['items'] = items
        print(data)
        
        serializer = TicketSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        print(serializer.errors)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

如何确定字段的正确格式

  • 对于many=True的嵌套字段,DRF要求传入JSON数组(Python列表),每个元素是符合子Serializer结构的字典。
  • 可以通过构造测试数据,打印Serializer的validated_data属性来查看预期格式;也可以直接参考DRF嵌套序列化器的官方示例。
  • 测试时用Postman直接发送标准JSON请求,验证Serializer是否正常工作,排除前端数据格式问题。

内容的提问来源于stack exchange,提问作者David Florez Mazuera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 13:16:20