Django Rest Framework - 如何在序列化程序中嵌套多个字段?
Django Rest Framework - How to nest several fields in a serializer?
我有几个带有多个控制字段的基础模型。其中一个位置字段由 lat、lon、accuracy、provider 和 client time 组成。我的大部分可写模型(以及资源)都继承自该基础模型。
我试图让 DRF 序列化嵌套 "location" 字段中的位置相关字段。例如,
{
"id": 1,
"name": "Some name",
"location": {
"lat": 35.234234,
"lon": 35.234234,
"provider": "network",
"accuracy": 9.4,
}
}
重要的是要记住这些字段是基本模型上的常规(平面)字段。
我已经调查并找到了几个选项
创建自定义字段并通过覆盖 "get_attribute" 创建嵌套表示。我不喜欢这个解决方案,因为我失去了模型序列化程序的一些好处,例如验证。
创建名为 Location 的嵌套资源。我想我可以通过在模型上添加同名的 属性 来使其工作,但同样没有验证。
所以我的问题是,在 DRF 序列化程序中嵌套(或分组)多个字段的最佳方法是什么?
DRF 3.0.0,Django 1.7
编辑:
基于@Tom Christie 的回答,这就是我想出的(简化)
# models.py
class BaseModel(models.Model):
id = models.AutoField(primary_key=True)
lat = models.FloatField(blank=True, null=True)
lon = models.FloatField(blank=True, null=True)
location_time = models.DateTimeField(blank=True, null=True)
location_accuracy = models.FloatField(blank=True, null=True)
location_provider = models.CharField(max_length=50, blank=True, null=True)
@property
def location(self):
return {
'lat': self.lat,
'lon': self.lon,
'location_time': self.location_time,
'location_accuracy': self.location_accuracy,
'location_provider': self.location_provider
}
class ChildModel(BaseModel):
name = models.CharField(max_lengtg=10)
# serializers.py
class LocationSerializer(serializers.Serializer):
lat = serializers.FloatField(allow_null=True, required=False)
lon = serializers.FloatField(allow_null=True, required=False)
location_time = serializers.DateTimeField(allow_null=True, required=False)
location_accuracy = serializers.FloatField(allow_null=True, required=False)
location_provider = serializers.CharField(max_length=50,allow_null=True, required=False)
class BaseSerializer(serializers.ModelSerializer):
def create(self,validated_data):
validated_data.update(validated_data.pop('location',{}))
return super(BaseSerializer,self).create(validated_data)
def update(self, instance, validated_data):
location = LocationSerializer(data=validated_data.pop('location',{}), partial=True)
if location.is_valid():
for attr,value in location.validated_data.iteritems():
setattr(instance,attr,value)
return super(BaseSerializer,self).update(instance, validated_data)
class ChildSerializer(BaseSerializer):
location = LocationSerializer()
class meta:
model = ChildModel
fields = ('name','location',)
我用 valid/invalid post/patch 测试过,效果很好。
谢谢。
我建议只使用显式序列化器 类,并显式写入字段。它有点冗长,但它简单、明显且可维护。
class LocationSerializer(serializers.Serializer):
lat = serializers.FloatField()
lon = serializers.FloatField()
provider = serializers.CharField(max_length=100)
accuracy = serializers.DecimalField(max_digits=3, decimal_places=1)
class FeatureSerializer(serializers.Serializer):
name = serializers.CharField(max_length=100)
location = LocationSerializer()
def create(self, validated_data):
return Feature.objects.create(
name=validated_data['name'],
lat=validated_data['location']['lat'],
lon=validated_data['location']['lat'],
provider=validated_data['location']['provider'],
accuracy=validated_data['location']['accuracy']
)
def update(self, instance, validated_data):
instance.name = validated_data['name']
instance.lat = validated_data['location']['lat']
instance.lon = validated_data['location']['lat']
instance.provider = validated_data['location']['provider']
instance.accuracy = validated_data['location']['accuracy']
instance.save()
return instance
有很多方法可以让您改用ModelSerializer
,或者稍微保留create
和update
方法更短,但不清楚您给自己的额外间接访问是否值得。
我们几乎总是为我们正在构建的 API 使用完全显式的序列化器类。
我有几个带有多个控制字段的基础模型。其中一个位置字段由 lat、lon、accuracy、provider 和 client time 组成。我的大部分可写模型(以及资源)都继承自该基础模型。
我试图让 DRF 序列化嵌套 "location" 字段中的位置相关字段。例如,
{
"id": 1,
"name": "Some name",
"location": {
"lat": 35.234234,
"lon": 35.234234,
"provider": "network",
"accuracy": 9.4,
}
}
重要的是要记住这些字段是基本模型上的常规(平面)字段。
我已经调查并找到了几个选项
创建自定义字段并通过覆盖 "get_attribute" 创建嵌套表示。我不喜欢这个解决方案,因为我失去了模型序列化程序的一些好处,例如验证。
创建名为 Location 的嵌套资源。我想我可以通过在模型上添加同名的 属性 来使其工作,但同样没有验证。
所以我的问题是,在 DRF 序列化程序中嵌套(或分组)多个字段的最佳方法是什么?
DRF 3.0.0,Django 1.7
编辑:
基于@Tom Christie 的回答,这就是我想出的(简化)
# models.py
class BaseModel(models.Model):
id = models.AutoField(primary_key=True)
lat = models.FloatField(blank=True, null=True)
lon = models.FloatField(blank=True, null=True)
location_time = models.DateTimeField(blank=True, null=True)
location_accuracy = models.FloatField(blank=True, null=True)
location_provider = models.CharField(max_length=50, blank=True, null=True)
@property
def location(self):
return {
'lat': self.lat,
'lon': self.lon,
'location_time': self.location_time,
'location_accuracy': self.location_accuracy,
'location_provider': self.location_provider
}
class ChildModel(BaseModel):
name = models.CharField(max_lengtg=10)
# serializers.py
class LocationSerializer(serializers.Serializer):
lat = serializers.FloatField(allow_null=True, required=False)
lon = serializers.FloatField(allow_null=True, required=False)
location_time = serializers.DateTimeField(allow_null=True, required=False)
location_accuracy = serializers.FloatField(allow_null=True, required=False)
location_provider = serializers.CharField(max_length=50,allow_null=True, required=False)
class BaseSerializer(serializers.ModelSerializer):
def create(self,validated_data):
validated_data.update(validated_data.pop('location',{}))
return super(BaseSerializer,self).create(validated_data)
def update(self, instance, validated_data):
location = LocationSerializer(data=validated_data.pop('location',{}), partial=True)
if location.is_valid():
for attr,value in location.validated_data.iteritems():
setattr(instance,attr,value)
return super(BaseSerializer,self).update(instance, validated_data)
class ChildSerializer(BaseSerializer):
location = LocationSerializer()
class meta:
model = ChildModel
fields = ('name','location',)
我用 valid/invalid post/patch 测试过,效果很好。
谢谢。
我建议只使用显式序列化器 类,并显式写入字段。它有点冗长,但它简单、明显且可维护。
class LocationSerializer(serializers.Serializer):
lat = serializers.FloatField()
lon = serializers.FloatField()
provider = serializers.CharField(max_length=100)
accuracy = serializers.DecimalField(max_digits=3, decimal_places=1)
class FeatureSerializer(serializers.Serializer):
name = serializers.CharField(max_length=100)
location = LocationSerializer()
def create(self, validated_data):
return Feature.objects.create(
name=validated_data['name'],
lat=validated_data['location']['lat'],
lon=validated_data['location']['lat'],
provider=validated_data['location']['provider'],
accuracy=validated_data['location']['accuracy']
)
def update(self, instance, validated_data):
instance.name = validated_data['name']
instance.lat = validated_data['location']['lat']
instance.lon = validated_data['location']['lat']
instance.provider = validated_data['location']['provider']
instance.accuracy = validated_data['location']['accuracy']
instance.save()
return instance
有很多方法可以让您改用ModelSerializer
,或者稍微保留create
和update
方法更短,但不清楚您给自己的额外间接访问是否值得。
我们几乎总是为我们正在构建的 API 使用完全显式的序列化器类。