Django Rest框架 - 如何在序列化程序中嵌套几个字段? [英] Django Rest Framework - How to nest several fields in a serializer?
问题描述
我正在尝试使DRF在嵌套的位置字段中序列化位置相关的字段。例如,
{
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超(BaseSerializer,个体经营).create(validated_d ata)
def update(self,instance,validated_data):
location = LocationSerializer(data = validated_data.pop('location',{}),partial = True)
如果location.is_valid():attr的
,location.validated_data.iteritems()中的值:
setattr(instance,attr,value)
返回超级(BaseSerializer,self).update实例,validated_data)
class ChildSerializer(BaseSerializer):
location = LocationSerializer()
class meta:
model = ChildModel
fields =('name','location',)
我已经测试了有效/无效的帖子/补丁,它的工作完美。
谢谢。
我建议使用显式的串行化器类,并明确写入字段。这是一个更冗长的,但它是简单,明显和可维护。
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,valida ted_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 ()
返回实例
有一些方式你可以使用 ModelSerializer
或者保持创建
和更新
方法稍短一些,但是不清楚,你给自己的额外间接是值得的。
我们几乎总是为我们正在构建的API使用完全显式的序列化器类。
I have several a base model with several control fields. Among them a location fields compound from lat, lon, accuracy, provider and client time. Most of my writable models (and hence resources) are inheriting from this base model.
I'm trying to make DRF serialize the location related fields in a nested "location" field. For example,
{
"id": 1,
"name": "Some name",
"location": {
"lat": 35.234234,
"lon": 35.234234,
"provider": "network",
"accuracy": 9.4,
}
}
I'ts important to remember that these fields are regular (flat) fields on the base model.
I've investigated and found several options
Create a custom field and by overriding "get_attribute" create the nested representation. I don't like this solution because i lose some of the benefits of the model serializer such as validation.
Create a nested resource called Location. I guess i could make it work by adding a property by the same name on the model but again, no validations.
So my question is, What is the best way to nest ( or group ) several fields in a DRF serializer ?
DRF 3.0.0, Django 1.7
EDIT:
Building on top of @Tom Christie answer this is what i came up with (simplified)
# 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',)
I've tested with valid/invalid post/patch and it worked perfectly.
Thanks.
I'd suggest simply using explicit serializer classes, and writing the fields explicitly. It's a bit more verbose, but it's simple, obvious and maintainable.
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
There's a bunch of ways you could use a ModelSerializer
instead, or ways to keep the create
and update
methods a little shorter, but it's not clear that the extra indirection you'd be giving yourself is at all worth it.
We almost always use completely explicit serializer classes for APIs that we're building.
这篇关于Django Rest框架 - 如何在序列化程序中嵌套几个字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!