版本 :
- Django==2.0.1
- djangorestframework==3.7.7
- Github地址
其它:
增加 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
其它: