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, 
 }
}

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

我已经调查并找到了几个选项

  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 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,或者稍微保留createupdate方法更短,但不清楚您给自己的额外间接访问是否值得。

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