【问题标题】:DRF create object with nested serializers and foreign keysDRF 使用嵌套的序列化程序和外键创建对象
【发布时间】:2020-06-02 16:16:07
【问题描述】:

我正在使用 DRF,我正在尝试创建一个对象,该对象具有多个外键以及在此过程中需要创建的相关对象。

这是我的模型的简化版本:

class Race(models.Model):
    name = models.CharField(max_length=200)
    owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='races')
    type = models.ForeignKey(Type, on_delete=models.SET_NULL, related_name='races', null=True)
    region = models.ForeignKey(Region, on_delete=models.CASCADE, verbose_name=_('region'), related_name='races')
    country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name='races')
    timezone = models.ForeignKey(Timezone, on_delete=models.SET_NULL, null=True)

class Event(models.Model):
    name = models.CharField(max_length=200)
    race = models.ForeignKey(Race, on_delete=models.CASCADE, related_name='events')

然后这是我的 Race 序列化器:

class RaceSerializer(serializers.ModelSerializer):
    owner = UserSerializer(read_only=True)
    type = TypeSerializer(read_only=True)
    events = EventSerializer(many=True)
    country = CountrySerializer()
    region = RegionSerializer(read_only=True)
    timezone = TimezoneSerializer(read_only=True)

    def create(self, validated_data):
        with transaction.atomic():
            events = validated_data.pop('events', None)
            race = Race(**validated_data)
            race.save()
            for event in events:
                Event.objects.create(race=race, **event)
        return race

我的看法:

class AddRaceView(CreateAPIView):
    serializer_class = RaceSerializer
    permission_classes = (IsAuthenticated,)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

下面是我通过 POST 请求发送的一些测试数据:

{
    "name": "ABC Marathon",
    "country": {
        "pk": 1,
        "name": "United States"
    },
    "region": {
        "pk": 1,
        "code": "ME"
    },
    "timezone": {
        "pk": 1,
        "code": "EST"
    },
    "events": [
        {
            "name": "Marathon"
        },
        {
            "name": "Half Marathon"
        }
    ]
}

所以我遇到的问题是将有效数据传递给外键的序列化程序。我不想为Type、Region、Country、Timezone 创建新对象,只传递适当的数据,以便新创建的 Race 对象正确引用现有外键。

这是我尝试过的:

1) 未在外键序列化程序上设置 read_only=True。这试图创建我不想要的新对象。

2) 在外键序列化程序上设置read_only=True(如上面的代码所示)。这有助于不尝试创建新的Type、Region 等对象,但会在序列化程序创建方法中从validated_data 中删除相应的字段。所以我无法在创建时将现有对象添加到 Race 外键。

3) 使用 PrimaryKeyForeignField 而不是TypeSerializer、RegionSerializer 等。但是当我使用RaceSerializer 检索比赛数据时,我在type、region 等下只有一个pk,而我'我真的很想能够检索外键的所有字段。

能否请您告知正确的设置会寻找这样的东西吗?我觉得不应该这么难。

谢谢!

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    所以最后我通过使用RelatedField 解决了这个问题,而不是为每个外键使用单独的序列化程序,嵌套的EventSerializer 除外,它确实需要编写嵌套的Event 对象。

    这是RaceSerializer:

    class RaceSerializer(serializers.ModelSerializer):
        owner = UserSerializer(read_only=True)
        type = TypeField()
        country = CountryField()
        region = RegionField()
        timezone = TimezoneField()
        events = EventSerializer(many=True)
        race_cal_types = serializers.SerializerMethodField()
    
        def create(self, validated_data):
            with transaction.atomic():
                events = validated_data.pop('events', None)
                race = Race(**validated_data)
                race.save()
                for event in events:
                    Event.objects.create(race=race, **event)
            return race
    

    这是RelatedField 和ModelSerializer 的组合,我用于RaceSerializer 中的每个字段,例如对于region 外键:

    class RegionSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Region
            fields = ('pk', 'name', 'code')
    
    
    class RegionField(RelatedField):
    
        def get_queryset(self):
            return Region.objects.all()
    
        def to_internal_value(self, data):
            return self.get_queryset().get(**data)
    
        def to_representation(self, value):
            return RegionSerializer(value).data
    

    每个字段(type、region、country、timezone)都有自己的 to_internal_value 和 to_representation 方法来以我需要的方式序列化/反序列化数据。

    【讨论】:

      猜你喜欢
      • 2020-10-03
      • 1970-01-01
      • 2015-07-09
      • 1970-01-01
      • 2016-03-22
      • 1970-01-01
      • 2020-05-04
      相关资源
      最近更新 更多