用Django REST framework 编写RESTful API(2.对viewsets和modelseriaizer自定义)

版本 :

其它:

增加 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
其它:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容