- 這篇文章會(huì)從由 rest framework 的 viewsets, router, ModelSerializer 構(gòu)建的一個(gè)基礎(chǔ)的 API 開始, 然后對(duì)其進(jìn)行自定義.
- 建議閱讀部分 rest framework 的教程
- 版本 :
- Django==2.0.1
- djangorestframework==3.7.7
- Github地址
其它:
- 用Django REST framework 編寫RESTful API(3.添加評(píng)論模塊)
- 用Django REST framework 編寫RESTful API(2.對(duì)viewsets和modelseriaizer自定義)
通過 viewsets, router, ModelSerializer 構(gòu)建一個(gè)關(guān)于博客的 API
構(gòu)建博客的 model
一篇文章需要有:
- 標(biāo)題
- 文章主體
- 發(fā)布時(shí)間
- 標(biāo)簽
- 作者
標(biāo)簽和文章的關(guān)系應(yīng)該是多對(duì)多(一篇文章可以有多個(gè)標(biāo)簽,一個(gè)標(biāo)簽可以關(guān)聯(lián)多篇文章)
作者和文章的關(guān)系應(yīng)該是一對(duì)多(一個(gè)作者可以有多篇文章,一篇文章只有一個(gè)作者)
需要的model有三個(gè): 文章(Post), 標(biāo)簽(Tag), 作者(User)
class Post(models.Model):
"""
title:標(biāo)題
body:主體
pub_time:發(fā)布時(shí)間
tag:標(biāo)簽,多對(duì)多
author:作者,一對(duì)多
"""
title = models.CharField(max_length=100, blank=True, default='')
body = models.TextField()
pub_time = models.DateTimeField(auto_now_add=True)
tags = models.ManyToManyField('Tag', related_name='posts', blank=True)
author = models.ForeignKey('auth.User', related_name='posts', on_delete=models.CASCADE)
class Meta:
ordering = ('-pub_time',)
class Tag(models.Model):
name = models.CharField(max_length=50)
作者model 使用 Django 的 User model, 便利驗(yàn)證和權(quán)限管理
構(gòu)建 Serializer
Serializer 是 rest framework 非常重要的一個(gè)部分
在 Django 中一般是 Model 和 View 之間交互,由 View 處理請(qǐng)求,把數(shù)據(jù)傳給Model, View 展現(xiàn)從 Model 得到的數(shù)據(jù)
而在 rest framework 中 Serializer 就相當(dāng)于 Model 和 View 的中間處理,將從 Model 中得到的數(shù)據(jù)序列化后交給 View, 將從 View 中得到的數(shù)據(jù)反序列化后交給 Model
新建 serializer.py 使用 HyperlinkedModelSerializer 構(gòu)建 Serializer:
from django.contrib.auth.models import User
from restAPI.models import Post, Tag
class PostSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Post
fields = ('url', 'id', 'title', 'pub_time', 'author', 'body', 'tags')
class TagSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Tag
fields = ('url', 'id', 'name', 'posts')
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'id', 'username', 'posts')
HyperlinkedModelSerializer 默認(rèn)會(huì)為關(guān)系數(shù)據(jù)生成 url, 所以不用在 Tag 和 User中聲明 fields 中的 'posts'
構(gòu)建 View
設(shè)置權(quán)限, 新建 permission.py:
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
"""
只允許作者修改但允許所有人讀的權(quán)限設(shè)置
"""
def has_object_permission(self, request, view, obj):
# 所有用戶都允許讀取,所以安全的http方法會(huì)直接放行
# SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
if request.method in permissions.SAFE_METHODS:
return True
# 寫入權(quán)限需要作者本人
return obj.author == request.user
用 viewsets 構(gòu)建 View:
from rest_framework import viewsets, permissions, mixins
from django.contrib.auth.models import User
from restAPI.models import Post, Tag
from restAPI.serializers import PostSerializer, TagSerializer, UserSerializer
from restAPI.permissions import IsAuthorOrReadOnly
class PostViewSet(viewsets.ModelViewSet):
"""
處理 /api/posts/ GET POST , 處理 /api/post/<pk>/ GET PUT PATCH DELETE
"""
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsAuthorOrReadOnly)
def perform_create(self, serializer):
"""
重寫 perform_create
user 信息不在 request.data 中, 在保存時(shí)加入 user 信息
"""
serializer.save(author=self.request.user)
class TagViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
"""
處理 /api/tags/ GET POST, 處理 /api/tags/<pk>/ GET
"""
queryset = Tag.objects.all()
serializer_class = TagSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
處理 /api/users/ GET, 處理 /api/users/<pk>/ GET
"""
queryset = User.objects.all()
serializer_class = UserSerializer
ModelViewSet 通過 create() 中 get_serializer(data=request.data) 將數(shù)據(jù)傳給 serializer, 但是 user 信息并不在 request.data 中,而是作為 request 的一個(gè)屬性,
所以通過重寫 perform_create() 在保存時(shí)添加 user
構(gòu)建 urls
因?yàn)槭褂玫氖?viewsets 所有通過 router 構(gòu)建 urls 非常簡單:
項(xiàng)目 urls:
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('restAPI.urls'))
]
app urls:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from restAPI.views import PostViewSet, TagViewSet, UserViewSet
router = DefaultRouter()
router.register(r'posts', PostViewSet)
router.register(r'tags', TagViewSet)
router.register(r'users', UserViewSet)
urlpatterns = [
path('', include(router.urls)),
path('api-auth/', include('rest_framework.urls')),
]
include(router.urls) 會(huì)幫我們直接生成好
- /api/posts/
- /api/posts/<pk>/
- /api/users/
- /api/users/<pk>/
- /api/tags/
- /api/tags/<pk>/
結(jié)尾
到此基礎(chǔ)的 API 就構(gòu)建完了, 但是還留有一些問題:
- GET /api/posts/ 得到的序列中的 'body' 是全部的信息, 我們?cè)讷@取 posts 列表的時(shí)候不需要那么完整的信息, 導(dǎo)致流量浪費(fèi), 在tag author中只有鏈接,信息太少
- GET /api/tags/ posts 只有鏈接, 信息太少
這些會(huì)在下一篇中解決
其它: