DRF批量保存300个对象报错:'ListSerializer' object has no attribute 'error'
DRF批量保存Card对象报错修复方案
问题场景
通过DRF API向/cards端点发送POST请求时,自动生成300组每组16个元素的组合,将每个组合与请求中的game值配对后批量保存为Card对象,当前遇到两个问题:
- 序列化验证失败时抛出
AttributeError: 'ListSerializer' object has no attribute 'error' - 不确定生成的组合数据是否正确传递给序列化器
报错原因分析
- 序列化器错误属性误用:DRF序列化器的错误信息属性是复数形式的
errors,而非单数error,即使使用many=True生成ListSerializer,也需要调用serializer.errors获取错误。 - 数据格式不匹配:
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
相关产品推荐
相关产品推荐

