我正在使用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
等对象,但是会在序列化器create方法中删除validated_data
中的各个字段。因此,我无法在创建时将现有对象添加到Race外键中。
3)使用PrimaryKeyForeignField代替TypeSerializer
,RegionSerializer
等。但是当我使用RaceSerializer
检索比赛数据时,我在{{1 }},pk
等,我真的很希望能够检索外键的所有字段。
您能建议类似这样的正确设置是什么吗?我觉得这不应该那么难。
谢谢!
答案 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
中用于每个字段的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
方法来序列化/反序列化数据我需要的方式