如何将OrderedDict转换为Django模型实例?附报错与代码示例
Django嵌套序列化器创建关联模型的错误修复
现有Django模型
class City(models.Model): name = models.CharField(max_length=50, default='') class Street(models.Model): name = models.CharField(max_length=50) city = models.ForeignKey(City, on_delete=models.CASCADE) class Shop(models.Model): name = models.CharField(max_length=50) city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True) street = models.ForeignKey(Street, on_delete=models.SET_NULL, null=True) house = models.SmallIntegerField() opening_time = models.TimeField() closing_time = models.TimeField()
对应序列化器代码
from rest_framework import serializers class CitySerializer(serializers.ModelSerializer): class Meta: model = City fields = ['id', 'name'] class StreetSerializer(serializers.ModelSerializer): city = CitySerializer() class Meta: model = Street fields = ['id', 'name', 'city'] # 原方法无法工作,问题核心在此 def create(self, validated_data): city_data = validated_data.pop('city') city = City.objects.create(**city_data) street = Street.objects.create(**validated_data) class ShopSerializer(serializers.ModelSerializer): city = CitySerializer() street = StreetSerializer() class Meta: model = Shop fields = ['id', 'name', 'city', 'street', 'house', 'opening_time', 'closing_time'] def create(self, validated_data): city_data = validated_data.pop('city') city = City.objects.create(name=city_data['name']) street_data = validated_data.pop('street') street = Street.objects.create(name = street_data['name'], city = city) shop = Shop.objects.create(**validated_data) # 此处报错:NoneType,因为ForeignKey不能用add() shop.city.add(city) # 赋值后未保存 shop.street = street return shop
运行时错误信息
ValueError: Cannot assign "OrderedDict([('name', 'Ryazan')])": "Street.city" must be a "City" instance.
问题分析与修复方案
1. 修复StreetSerializer的create方法
错误根源是StreetSerializer嵌套了CitySerializer,导致validated_data['city']是OrderedDict而非City实例,必须先将字典转换为模型实例再关联:
class StreetSerializer(serializers.ModelSerializer): city = CitySerializer() class Meta: model = Street fields = ['id', 'name', 'city'] def create(self, validated_data): # 取出城市的字典数据 city_data = validated_data.pop('city') # 用get_or_create避免重复创建同名城市 city, _ = City.objects.get_or_create(**city_data) # 将City实例传入Street创建逻辑 street = Street.objects.create(city=city, **validated_data) return street
2. 修复ShopSerializer的create方法
原代码存在两个问题:
shop.city.add(city)错误:ForeignKey是一对一关联,没有add()方法,直接赋值即可- 赋值
shop.street = street后未保存实例
优化后的代码:
class ShopSerializer(serializers.ModelSerializer): city = CitySerializer() street = StreetSerializer() class Meta: model = Shop fields = ['id', 'name', 'city', 'street', 'house', 'opening_time', 'closing_time'] def create(self, validated_data): # 处理城市数据,避免重复创建 city_data = validated_data.pop('city') city, _ = City.objects.get_or_create(name=city_data['name']) # 处理街道数据,关联已创建的城市 street_data = validated_data.pop('street') street, _ = Street.objects.get_or_create(name=street_data['name'], city=city) # 创建Shop时直接传入关联实例,一步到位 shop = Shop.objects.create(city=city, street=street, **validated_data) return shop
简化方案(可选)
如果不需要每次创建新的City/Street,而是关联已有数据,建议用SlugRelatedField替代嵌套序列化器,直接通过名称关联实例,无需手动转换字典:
class StreetSerializer(serializers.ModelSerializer): city = serializers.SlugRelatedField(slug_field='name', queryset=City.objects.all()) class Meta: model = Street fields = ['id', 'name', 'city']
此时传入{"name": "某街道", "city": "Ryazan"}即可自动关联到对应的City实例,省去字典转模型的步骤。
内容的提问来源于stack exchange,提问作者mezigar
相关产品推荐
相关产品推荐

