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

如何将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 01:31:07