DRF使用嵌套的序列化程序和外键创建对象

时间:2020-06-02 16:16:07

标签: django django-rest-framework

我正在使用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"
        }
    ]
}

所以我遇到的问题是将有效数据传递给外键的序列化器。我不想为TypeRegionCountryTimezone创建新对象,仅传递适当的数据,因此新创建的Race对象正确地引用了现有外键

这是我尝试过的:

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

2)在外键序列化器上设置read_only=True(如上面的代码所示)。这有助于避免尝试创建新的TypeRegion等对象,但是会在序列化器create方法中删除validated_data中的各个字段。因此,我无法在创建时将现有对象添加到Race外键中。

3)使用PrimaryKeyForeignField代替TypeSerializerRegionSerializer等。但是当我使用RaceSerializer检索比赛数据时,我在{{1 }},pk等,我真的很希望能够检索外键的所有字段。

您能建议类似这样的正确设置是什么吗?我觉得这不应该那么难。

谢谢!

1 个答案:

答案 0 :(得分:2)

因此最后,我通过使用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中用于每个字段的ModelSerializerRaceSerializer的组合,例如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

每个字段(typeregioncountrytimezone)都有自己的to_internal_valueto_representation方法来序列化/反序列化数据我需要的方式