Django Rest Framework - 如何在序列化器中嵌套多个字段?

时间:2015-01-28 08:14:11

标签: django django-rest-framework

我有几个带有几个控制字段的基本模型。其中,位置字段由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的嵌套资源。我想我可以通过在模型上添加同名的属性来使其工作,但同样没有验证。

  3. 所以我的问题是,在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',)
    

    我已经使用有效/无效的帖子/补丁进行了测试,但效果非常好。

    感谢。

1 个答案:

答案 0 :(得分:9)

我建议只使用显式的序列化程序类,并明确地编写字段。它有点冗长,但它简单,明显且易于维护。

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,或者让createupdate方法更短一些,但不是明确你自己给予的额外间接是完全值得的。

我们几乎总是为我们正在构建的API使用完全显式的序列化程序类。