初衷
在2018年5月份接觸并搭建過基于Django的后端平臺,由于當時時間緊任務單一,很多細節也沒去深究。但是我清除地記著我是有去找過Django REST相關的解讀的,但是無果。最近有些時間,開始搭建一個較完整的基于Django的平臺,結果發現“毒”還在,看來只能自制“解藥”了。因為離上一次寫類似博客的東西已經很久了,所以語言組織已經退化,不求寫清楚了,但求和我一樣的入門者有一個可聊的話題。
創建工程,添加一個應用
- 基于virtualenv創建工作環境,遠離python版本管理的煩惱
virtualenv -p /usr/local/bin/python3 py3env
source py3env/bin/activate
pip install django
pip install djangorestframework
- 創建工程和應用
django-admin.py startproject tutorial .
cd tutorial
django-admin.py startapp quickstart
- 默認使用sql數據庫初始化數據庫設置
python manage.py migrate
- 創建管理員賬號
python manage.py createsuperuser
關于Quickstart
這是一個官方的demo:http://www.django-rest-framework.org/tutorial/quickstart/, 目的是為了向開發者展示該組件的易用性,但是立足點似乎有點高,作為剛剛開始接觸Django并想要了解REST組件的同學來說,這個例子的文檔比較晦澀,而且絕大部分資料也只是對官方文檔的翻譯。該demo的功能,是實現一套簡單的api(含視圖),用于管理員查看和編輯Django的用戶和組信息。
了解流程
由于Django本身是MVC(MVT)結構,便于開發,但不便于入門理解,簡單地說,就是不便于在入門時理解Demo代碼中的關聯性。
-
Django的MTV模塊
- M:負責業務和數據庫的關系映射
- T:負責如何把html頁面展示給用戶
- V:負責業務邏輯,調用M和T(MVC中C的角色)
-
Django對請求的處理流程并不是僅僅MTV三個模塊
Django處理流程
其實從WSGI以后,都算作Django的工作范圍,WSGI通過調用Django中的Django.core.wsgi的入口方法(wgsi.py文件)以后,就將請求的數據結構傳遞給Django的各種中間件,具體會流經哪些中間件?可見setting.py中的MIDDLEWARE配置(跨域過濾之類的一般會設置在中間件中) -
Django app內部的流程會相對清晰一些:
- URLconf是指urls.py選擇一個視圖來處理請求
- 被選擇的那個視圖通常要做下面所列出的一件或者更多件事情:
- 通過model與數據庫對話
- 使用模板渲染HTML或者任何格式化過的響應
- 返回一個純文本響應(不被顯示的)(API)
- 返回response
額外的重點:序列化
序列化和反序列化的目的就是對象和傳輸數據(或是存儲數據)之間的轉換。Django本身和Django REST 組件都提供了序列化的輔助類,目的是為了簡化代碼(可以想象一下JAVA里的序列化操作,雖然有工具可以生成代碼),特別是ModelForm、ModelSerializer這些類,可以通過直接連接model模塊,完成序列化和反序列化的工作。序列化功能的實現,往往是實現功能的第一步。我們可以理解成數據流驅動了編碼順序。
- 序列化:對象 -> 字節序列(數據庫或者response)
- 反序列化:字節序列 -> 對象
從上面的描述上看,序列化功能類一般會用在連接model、return response,以及數據庫操作上。
再看Demo代碼
1、真正的第一步應該是設計并建立數據庫,而demo是要實現項目用戶的操作,使用了自帶的用戶信息所在數據庫。
2、實現序列化功能
在知道后端有什么(數據庫),前端要什么之后,我們就可以設計序列化功能。因為數據庫有什么我們就可以知道應該反序列出來什么,前端要什么,我們就知道需要序列化到response什么內容了。
Django提供了通用的serializers、ModelSerializer、HyperlinkedModelSerializer等封裝等級不同和類型不同的序列化器輔助類,唯一的區別就是簡化代碼的程度不同,如果對序列化的具體操作不熟悉的話,可以參照https://darkcooking.gitbooks.io/django-rest-framework-cn/content/chapter1.html 使用普通的serializers來實現一遍該類會幫助很大。
from django.contrib.auth.models import User, Group
from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
"""
用戶信息的序列化器(根據前端需要的數據信息來組織結構)
"""
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
class GroupSerializer(serializers.HyperlinkedModelSerializer):
"""
組信息的序列化器
"""
class Meta:
model = Group
fields = ('url', 'name')
3、實現View
這個Demo變得晦澀難懂的一大原因就是直接用了最簡潔的代碼方式,這里的viewsets就是其中之一,包括上面HyperlinkedModelSerializer。差別之處可以參考:https://darkcooking.gitbooks.io/django-rest-framework-cn/content/chapter6.html
"""
View中,必然會看到model(操作數據庫)和序列化器(組織response內容)
"""
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from quickstart.serializers import UserSerializer, GroupSerializer
class UserViewSet(viewsets.ModelViewSet):
"""
處理查看、編輯用戶的界面
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
class GroupViewSet(viewsets.ModelViewSet):
"""
處理查看、編輯用戶組的界面
"""
queryset = Group.objects.all()
serializer_class = GroupSerializer
為了理解這些晦澀的用法,還是總結下,Django Rest對視圖寫法的精簡可以一步步歸納為:
- 1、使用常規方式實現,最顯式的實現方式,也是最好理解的方式:(讀取數據庫字節序列->反序列化->序列化后response)
@csrf_exempt
def snippet_list(request):
"""
展示所以snippets,或創建新的snippet.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return JSONResponse(serializer.data)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = SnippetSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201)
return JSONResponse(serializer.errors, status=400)
- 2、使用基于函數視圖的@api_view裝飾器,加快實現
@api_view(['GET', 'POST'])
def snippet_list(request):
"""
展示或創建snippets.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- 3、使用基于類視圖的APIView
class SnippetList(APIView):
"""
List all snippets, or create a new snippet.
"""
def get(self, request, format=None):
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- 4、基于ViewSets的抽象行為(配合Routers實現),該方法也可以簡化urlconf,所以這可能就是Django生態的好處,讓我們更加關注api的交互,但是這之前還是需要清楚這些方法的演變過程。從這個角度上看,讀了N遍官方文檔之后,能發現官方真正想要傳達的意思。
class SnippetViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions.
Additionally we also provide an extra `highlight` action.
"""
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
@detail_route(renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
4、實現urlpatterns
使用viewsets和router后,由于封裝處理了“get”、“post” 等默認動作,所以在url.py指出特定的uri便可,這就是所謂的RESTFul,因為所有的“動作”都沒有暴露到api的命名中。
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from rest_framework import routers
from quickstart import views
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)
# 使用URL路由來管理我們的API
# 另外添加登錄相關的URL
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('admin/', admin.site.urls),
]
5、運行測試。
python manage.py runserver
便可看到一套自帶界面的api,一開始可能會有疑問,說好是寫一套apis的,怎么成了一個界面了,其實就是Django MTV的福利(自帶doc有沒有),大家試著去訪問:http://127.0.0.1:8000/users/?format=json ,就能看到一個正常的接口返回了。