Django自定义反序列化

时间:2016-02-12 16:32:30

标签: python json django django-rest-framework

我有以下django模型:

class Person(models.Model):
    name = models.CharField()
    location = models.PointField()

我想为这个模型创建一个序列化器/反序列化器。但是我收到的JSON对象是:

{
   "userList":[
      {
         "username": "Foo",
         "lat":40.875736,
         "lon":8.94382834,
      },
      {
      "username": "Bar",
      "lat":40.875736,
      "lon":8.94382834,
      }, 
   ]
}

串行器

class PersonListSerializer(serializers.PersonSerializer):
    username = serializers.CharField()
    lat = serializers.FloatField()
    lon = serializers.FloatField()


class PersonSerializer(serializers.ModelSerializer):
    personList = PersonListSerializer

    class Meta:
        model = Person

是否可以创建自定义序列化器/反序列化器来处理此结构而无需创建其他模型(PersonList)?

提前致谢。

1 个答案:

答案 0 :(得分:3)

嗯,这需要一段时间,这绝对是一个很好的学习经历。

您的问题可以分为两个单独的问题:

  1. 您需要一个从两个单独的值中获取其值的字段 表示字典中的字段,也输出两个单独的值。我对前者感到厌倦并做了自定义to_internal_value()
  2. 您需要一个不接受并返回列表的ListSerializer,而是一个包含单个嵌套字段的字典,其中包含实际列表。
  3. 你可以在不触及模型的情况下完成。

    class ListDictSerializer(serializers.ListSerializer):
        def get_field_name(self):
            if not hasattr(self.Meta, 'field_name'):
                raise ValueError('ListDictSerializer requires defining Meta.field_name overriding get_field_name()')
            return self.Meta.field_name
    
        def to_internal_value(self, data):
            field_name = self.get_field_name()
            return super(ListDictSerializer, self).to_internal_value(data[field_name])
    
        def to_representation(self, data):
            field_name = self.get_field_name()
            return ReturnDict({
                    field_name: super(ListDictSerializer, self).to_representation(data)
                }, serializer=self
            )
    
        @property
        def data(self):
           # skip over the ListSerializer to get the real data without the
            # ReturnList
            ret = super(serializers.ListSerializer, self).data
            return ReturnDict(ret, serializer=self)
    
    
    class PersonListSerializer(ListDictSerializer):
        class Meta:
            field_name = 'userList'
    
    
    class PersonSerializer(serializers.ModelSerializer):
        class Meta:
            list_serializer_class = PersonListSerializer
            model = Person
            fields = ('username', 'lat', 'lon')
    
        username = serializers.CharField(source='name')
        lat = serializers.SerializerMethodField(method_name='get_latitude')
        lon = serializers.SerializerMethodField(method_name='get_longitude')
    
        def get_latitude(self, instance):
            return instance.location.coords[1]
    
        def get_longitude(self, instance):
            return instance.location.coords[0]
    
        def to_internal_value(self, data):
            return ReturnDict({
                'name': data.get('username'),
                'location': Point(data['lat'], data['lon']),
            }, serializer=self)
    

    请注意,DRF允许您导出任意模型属性(不仅是字段)读/写。也就是说,我们可以通过适当的getter和setter在模型上定义@property latlon。但是,Point之类的地理对象在创建后是不可变的,因此您无法在现有对象上有选择地设置单个坐标。