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

DRF批量保存300个对象报错:'ListSerializer' object has no attribute 'error'

DRF批量保存Card对象报错修复方案

问题场景

通过DRF API向/cards端点发送POST请求时,自动生成300组每组16个元素的组合,将每个组合与请求中的game值配对后批量保存为Card对象,当前遇到两个问题:

  1. 序列化验证失败时抛出AttributeError: 'ListSerializer' object has no attribute 'error'
  2. 不确定生成的组合数据是否正确传递给序列化器

报错原因分析

  1. 序列化器错误属性误用:DRF序列化器的错误信息属性是复数形式的errors,而非单数error,即使使用many=True生成ListSerializer,也需要调用serializer.errors获取错误。
  2. 数据格式不匹配:generate_combinations函数返回的是[[组合元组, game值], ...]的嵌套列表结构,但CardSerializer需要的是包含game和image_numbers键的字典列表,当前格式无法被序列化器正确解析。

修复步骤

1. 修正错误属性调用

将POST方法中返回错误的代码从serializer.error改为serializer.errors。

2. 调整组合生成函数的数据格式

修改generate_combinations,返回字典列表,每个字典对应Card模型的字段:

  • image_numbers:存储生成的组合列表
  • game:传入的game关联值

3. 正确提取请求中的game参数

从request.data中提取game字段的值,而非直接传递整个request.data给生成函数。

完整修正代码

模型定义(无修改)

class Card(models.Model): 
    game = models.ForeignKey(Game, on_delete=models.CASCADE)
    image_numbers = jsonfield.JSONField()

序列化器(无修改)

class CardSerializer(serializers.ModelSerializer): 
    class Meta: 
        model = Card
        fields = (
            'game', 
            'image_numbers'
        )

视图类(修正后)

import math
import random
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from .models import Card
from .serializers import CardSerializer

class CardsView(APIView): 
    permission_classes = (IsAuthenticated, )

    # 组合生成工具方法
    def nth_combination(self, iterable, r, index):
        pool = tuple(iterable)
        n = len(pool)
        c = math.comb(n, r)
        if index < 0:
            index += c
        if index < 0 or index >= c:
            raise IndexError
        result = []
        while r:
            c, n, r = c*r//n, n-1, r-1
            while index >= c:
                index -= c
                c, n = c*(n-r)//n, n-1
            result.append(pool[-1-n])
        return tuple(result)

    # 生成符合序列化器格式的组合数据
    def generate_combinations(self, game_id):
        n = 64
        r = 16
        iterable = range(1, n)
        n_combos = math.comb(len(iterable), r)
        indices = random.sample(range(n_combos), k=300) 
        # 返回字典列表,匹配序列化器字段
        combos = [
            {
                "image_numbers": list(self.nth_combination(iterable, r, i)),
                "game": game_id
            } 
            for i in indices
        ]
        return combos

    def get(self, request, *args, **kwargs):
        qs = Card.objects.all()
        serializer = CardSerializer(qs, many=True)
        return Response(serializer.data)

    def post(self, request, *args, **kwargs):
        # 提取请求中的game字段值
        game_id = request.data.get('game')
        if not game_id:
            return Response({"error": "game参数必填"}, status=400)
        
        # 生成符合格式的批量数据
        batch_data = self.generate_combinations(game_id)
        serializer = CardSerializer(data=batch_data, many=True)
        
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        # 使用正确的errors属性返回错误
        return Response(serializer.errors, status=400)

验证说明

修复后发送POST请求时,只需传入game字段(如{"game": 1}),视图会自动生成300条符合格式的Card数据并批量保存,序列化验证失败时会返回正确的错误信息列表。

内容的提问来源于stack exchange,提问作者Felix A. Covarrubias Perez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 17:25:07