用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閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容