美文网首页Django
用Django REST framework 编写RESTful

用Django REST framework 编写RESTful

作者: Jimzjy | 来源:发表于2018-01-26 14:56 被阅读58次

    版本 :

    其它:

    增加 GET /api/posts/ /api/posts/<pk> 中的 user 和 tag 信息

    目前 author 和 tag 中只有链接,没有其它信息,我想得到一个有链接以及名称的嵌套序列
    那么就需要改掉 PostSerializer 的 fields 中默认的 'author' 'tag'
    新建 tag 和 user 的 seriaizer:

    class UserSerializerLite(serializers.HyperlinkedModelSerializer):
        """
        用于在Post的序列化器中的, 只包含 'url' 'username' 的User序列化器
        """
        class Meta:
            model = User
            fields = ('url', 'username')
    
    
    class TagSerializerLite(serializers.HyperlinkedModelSerializer):
        """
        用于在Post的序列化器中, 只包含 'url' 'name' 的Tag序列化器
        """
        class Meta:
            model = Tag
            fields = ('url', 'name')
    

    在 PostSerializer 中 class Meta 前添加:

    author = UserSerializerLite(read_only=True)
    tag = TagSerializerLite(many=True)
    

    现在 tag 和 author 就会变成嵌套序列,有 url 和 name/username

    但是又有问题来了, 你会发现 GET /api/tags 变的越来越多了, 原因是每次创建新的 post 都会创建新的 Tag 再引用, 而不是直接引用已有的, 更新 post 也会更新 Tag

    对于这个问题只要重写 PostSeriaizer 中的 create() 和 update() 就行, 在 PostSeriaizer 中添加:

    @staticmethod
    def addtag(tags, post):
        """
        为传入的 post 添加 tag ,如果 tag 已经存在,添加的关系是库中已经存在的 tag,
        如果 tag 不存在,则将 tag 添加到 Tag,添加的关系是新入库的 tag
        :param tags: validated_data 中的 tag
        :param post: Post类实例
        """
        for tag in tags:
            try:
                t = Tag.objects.get(name=tag['name'])
                post.tag.add(t)
                flag = True
            except ObjectDoesNotExist:
                flag = False
            if not flag:
                t = Tag.objects.create(name=tag['name'])
                post.tag.add(t)
    
    def create(self, validated_data):
        """
        重写 create ,从 validated_data 中取出 tag ,
        添加 Post 后调用 addtag() 添加 tag 关系
        """
        tags = validated_data.pop('tags')
        post = Post.objects.create(**validated_data)
        if tags is not None:
            self.addtag(tags, post)
        return post
    
    def update(self, instance, validated_data):
        """
        重写 update ,实例中的 'title' 'body' 更新后删除所有 tag 关系
        再调用 addtag() 添加 tag 关系
        """
        instance.title = validated_data.get('title', instance.title)
        instance.body = validated_data.get('body', instance.body)
        instance.save()
        instance.tag.clear()
        tags = validated_data.get('tag')
        if tags is not None:
            self.addtag(tags, post)
        return instance
    

    增加 GET tags users 的信息

    目前 tags users 中的 posts 只有链接, 我想得到 'url' 'title' 'author' 'body'
    所以新建 seriaizer:

    class PostSerializerLite(serializers.HyperlinkedModelSerializer):
        """
        用于 Tag, User 序列化器中, 只包含 'url' 'title' 'author' 'body' 的序列化器
        """
        author = UserSerializerLite(read_only=True)
    
        def to_representation(self, instance):
            """
            重写 to_representation 改变生成的序列, 将 'body' 中大于 50 长度的截断
            """
            ret = super(PostSerializerLite, self).to_representation(instance)
            excerpt = ret['body']
            if str(excerpt).__len__() > 50:
                excerpt = excerpt[:50] + '...'
            ret['body'] = excerpt
            return ret
    
        class Meta:
            model = Post
            fields = ('url', 'title', 'author', 'body')
    

    因为 'body' 中含有完整信息, 而在获取列表时并不需要那么完整的信息, 所以重写 to_representation() 截断 'body' 中的信息

    在 TagSerializer 和 UserSerializer 中添加:

    posts = PostSerializerLite(many=True, read_only=True)
    
    另一种将 'body' 信息截断的方法

    在 models.py 中的 Post 中添加 方法 :

    def excerpt(self):
        excerpt = str(self.body)
        if excerpt.__len__() > 50:
            excerpt = excerpt[:50]+'...'
        return excerpt
    

    之后将 PostSeriaizerLite 的 fields 修改为:

    fields = ('url', 'title', 'author', 'excerpt')
    

    减少 GET /api/posts/ 'body' 的信息

    我们只想改变在 GET /api/posts/ 时的 'body' 信息,所以不能直接修改 serializer_class
    当我们发送 GET 请求到 /api/posts/ 的时候, router 会把请求交给 PostViewSet 处理,
    PostViewSet 会调用 ListModelMixin 中的 list() 方法
    那么我们只要重写 list 方法就行
    原 list():

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
    
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
    
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
    

    可以看到 serializer 的实例是通过 self.get_serializer(page, many=True)或者self.get_serializer(queryset, many=True)得到的
    我的想法是替换掉其中的 get_seriaizer ,改成 PostSeriaizerLite

    def list(self, request, *args, **kwargs):
        """
        重写 list
        GET /api/posts/ 时调用的序列化器默认是 PostSerializer, 改成 PostSerializerLite 去除多余信息
        """
        queryset = self.filter_queryset(self.get_queryset())
    
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = PostSerializerLite(page, many=True, context={'request': self.request})
            return self.get_paginated_response(serializer.data)
    
        serializer = PostSerializerLite(queryset, many=True, context={'request': self.request})
        return Response(serializer.data)
    

    因为 HyperlinkedModelSerializer 需要 request 我们添加 context={'request': self.request}

    结尾

    rest framework 中 还有很多很有用的组件可以非常方便的构建 API
    其它:

    相关文章

      网友评论

        本文标题:用Django REST framework 编写RESTful

        本文链接:https://www.haomeiwen.com/subject/nbfwaxtx.html