Django Rest框架 - 如何在序列化程序中嵌套几个字段? [英] Django Rest Framework - How to nest several fields in a serializer?

查看:769
本文介绍了Django Rest框架 - 如何在序列化程序中嵌套几个字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有几个具有几个控制字段的基本模型。其中一个位置字段由lat,lon,准确性,提供者和客户端时间组成。我的大部分可写模型(因此资源)都是从这个基本模型继承而来的。



我正在尝试使DRF在嵌套的位置字段中序列化位置相关的字段。例如,

  {
id:1,
name:Some name ,
location:{
lat:35.234234,
lon:35.234234,
provider:network,
accuracy 9.4,
}
}

重要的是要记住这些字段是基础模型上的常规(平面)字段。



我已经调查并发现了几个选项


  1. 创建一个自定义字段和覆盖get_attribute创建嵌套表示。我不喜欢这个解决方案,因为我失去了模型序列化程序的一些好处,如验证。


  2. 创建一个名为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

  1. 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.

  2. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆