【问题标题】:Django Rest Framework - How to nest several fields in a serializer?Django Rest Framework - 如何在序列化程序中嵌套多个字段?
【发布时间】:2015-03-27 01:42:15
【问题描述】:

我有几个带有多个控制字段的基本模型。其中一个位置字段由 lat、lon、accuracy、provider 和 client time 组合而成。我的大多数可写模型(以及资源​​)都继承自这个基本模型。

我正在尝试让 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 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',)

我已经使用有效/无效的帖子/补丁进行了测试,并且效果很好。

谢谢。

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

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

    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 使用完全显式的序列化程序类。

    【讨论】:

    • 同意这是处理它的好方法。如果您以易于理解的方式命名序列化程序,也会有所帮助。例如,如果OrderSerializer 具有嵌套属性,那么您应该考虑将其他序列化程序类命名为OrderCustomerSerializerOrderLineItemSerializer
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-03
    • 2018-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-17
    相关资源
    最近更新 更多