DRF:操作序列化器字段布局

时间:2014-11-20 18:11:44

标签: django django-rest-framework

我有一个代表房子的模型:

class House(models.Model):
    name = models.CharField(...)
    long = models.FloatField(...)
    lat = models.FloatField(...)

和一个序列化程序,用于返回最基本的表示中的房屋列表:

class HouseSerializer(serializers.ModelSerializer):
    class Meta:
        model = House
        fields = ('id', 'name')    

和视图

class HouseList(generics.ListAPIView):
    queryset = House.objects.all()
    serializer_class = HouseSerializer

这很好用。我可以访问/api/house/并看到json的房屋清单:

{ 
    'id': 1,
    'name': 'Big House'
},
{
    'id': 1
    'name': 'Small House',
}...

现在,我想在/api/maps/markers/创建第二个视图/资源,将我的房屋作为Google-Map-Friendly标记列表返回:

{ 
    'id': 1,
    'long': ...,
    'lat': ...,
    'houseInfo': {
        'title': "Big House",
    }
} ...

我可以预见两种方法:

  • 将其作为单独的序列化程序执行(使用与之前相同的视图)并映射出替代字段布局。
  • 将此作为单独的视图执行(使用与之前相同的序列化程序),并在创建Response之前简单地布置字段

但是我没有采用任何方法,我明确了如何处理它,哪种方法更可取?

2 个答案:

答案 0 :(得分:5)

回答1

在我看来,你需要两种 - 不同的视图和序列化器。

仅仅因为视图端点不是第一个的子网址,所以它们不相关 - 不同的视图,即使它们使用相同的模型。

不同的序列化程序 - 因为你有不同的字段布局。

不确定你的情况有多复杂,但无论如何,任何代码重复都可能由mixin解决。

回答2

取决于用例:

  • 如果您还需要使用相同的结构编写数据,则需要定义自己的字段类并正确处理解析
  • 如果只是阅读数据,你应该没问题:

    class HouseGoogleSerializer(HouseSerializer):
        houseInfo = serializers.SerializerMethodField('get_house_info')
    
        class Meta:
            model = House
            fields = [...]
    
        def get_house_info(self, obj):
            return {'title': obj.name}
    

    其中HouseSerializer是您的基本序列化程序。

答案 1 :(得分:1)

此代码来自正在运行的项目,并提供更多您要求的代码 但如果你想删除一些功能,可以很容易地适应你的需要。 当前的实现允许您:

  • 只使用一个url一个序列化程序和一个视图
  • 使用查询字符串param(?serializer = std)
  • 选择输出

如何在您的代码中使用:

案例1 (一个能够通过查询字符串选择序列化程序的网址)

class HouseSerializer(HouseSerializer):
    houseInfo = serializers.SerializerMethodField('get_house_info')

    class Meta:
        model = House

    def get_house_info(self, obj):
        return {'title': obj.name}


class HouseList(DynamicSerializerMixin, generics.ListAPIView):
    queryset = House.objects.all()
    serializer_class = HouseSerializer
    serializers_fieldsets = {'std': ('id', 'name'),
                              'google' : ('id', 'long', 'lat', 'houseInfo')}

案例2 (不同观点)

class HouseList(DynamicSerializerMixin, generics.ListAPIView):
    queryset = House.objects.all()
    serializer_class = HouseSerializer
    serializers_fieldsets = {'std': ('id', 'name')}

class GoogleHouseList(DynamicSerializerMixin, generics.ListAPIView):
    queryset = House.objects.all()
    serializer_class = HouseSerializer
    serializers_fieldsets = {'std': ('id', 'long', 'lat', 'houseInfo')}

==============

def serializer_factory(model, base=BaseHyperlinkedModelSerializer,
                       fields=None, exclude=None):
    attrs = {'model': model}
    if fields is not None:
        attrs['fields'] = fields
    if exclude is not None:
        attrs['exclude'] = exclude

    parent = (object,)
    if hasattr(base, 'Meta'):
        parent = (base.Meta, object)
    Meta = type(str('Meta'), parent, attrs)
    if model:
        class_name = model.__name__ + 'Serializer'
    else:
        class_name = 'Serializer'
    return type(base)(class_name, (base,), {'Meta': Meta, })


class DynamicSerializerMixin(object):
    """
    Mixin that allow to limit the fields returned
    by the serializer.

    Es.
        class User(models.Model):
            country = models.ForeignKey(country)
            username = models.CharField(max_length=100)
            email = models.EmailField()

        class UserSerializer(BaseHyperlinkedModelSerializer):
            country = serializers.Field(source='country.name')


        class MyViewSet(DynamicSerializerViewSetMixin, BaseModelViewSet):
            model = User
            serializer_class = UserSerializer
            serializers_fieldsets = {'std': None,
                                      'brief' : ('username', 'email')
                                      }
    this allow calls like

        /api/v1/user/?serializer=brief

    """
    serializers_fieldsets = {'std': None}
    serializer_class = ModelSerializer

    def get_serializer_class(self):
        ser = self.request.QUERY_PARAMS.get('serializer', 'std')

        fields = self.serializers_fieldsets.get(ser, 'std')

        return serializer_factory(self.model,
                                  self.serializer_class,
                                  fields=fields)