極大簡化Django開發(fā),讓程序員沒代碼敲的DRF框架(django的drf框架的優(yōu)點)
前置基礎(chǔ)知識
web開發(fā)的兩種模式
1. 前后端不分離
完整的html頁面是在后端生成的,后端給前端返回完整的頁面,前端只是進(jìn)行展示,前端與后端的耦合度很高。
缺點:只適用于純網(wǎng)頁的應(yīng)用。
優(yōu)點:有利于網(wǎng)站的SEO優(yōu)化
2. 前后端分離
完整的html頁面是在前端生成的,后端只給前端返回所需的數(shù)據(jù),前端將數(shù)據(jù)填充在頁面上,前端與后端的耦合度很低。
優(yōu)點:可以對接不同類型的客戶端。
缺點:不利于SEO優(yōu)化
3.API: 在前后端分離開發(fā)模式中,我們通常將后端開發(fā)的每個視圖都稱為一個接口或者API。
4.RESTful設(shè)計風(fēng)格
統(tǒng)一的接口設(shè)計方式就是普遍采用的RESTful API設(shè)計風(fēng)格
Restful風(fēng)格設(shè)計-關(guān)鍵點
- URL地址盡量使用名詞復(fù)數(shù),不要使用動詞。
- 訪問同一個URL地址,采用不同的請求方式,代表要執(zhí)行不同的操作。(GET)獲取 POST(新增) PUT(修改)DELETE(刪除) 不常用:PATCH(修改) HEAD(只返回請求頭沒有請求體) OPTIONS(獲取信息)
- 過濾參數(shù)可以放在查詢字符串中。常見的參數(shù):
?limit=10:指定返回記錄的數(shù)量?offset=10:指定返回記錄的開始位置。?page=2&pagesize=100:指定第幾頁,以及每頁的記錄數(shù)。?sortby=name&order=asc:指定返回結(jié)果按照哪個屬性排序,以及排序順序。
- 針對不同操作,服務(wù)器向用戶返回不同的響應(yīng)數(shù)據(jù)。一般遵循以下規(guī)范:
1. 獲取一組數(shù)據(jù),返回一組數(shù)據(jù)2. 獲取指定數(shù)據(jù),返回指定數(shù)據(jù)3. 新增數(shù)據(jù),返回新增的數(shù)據(jù)4. 修改數(shù)據(jù),返回修改的數(shù)據(jù)5. 刪除數(shù)據(jù),返回空
- 服務(wù)器返回的響應(yīng)數(shù)據(jù)格式,應(yīng)該盡量使用JSON。
- 響應(yīng)狀態(tài)碼,服務(wù)器向客戶端返回的狀態(tài)碼和提示信息,常見的狀態(tài)碼如下:
200 OK - [GET/PUT]:服務(wù)器成功返回用戶請求的數(shù)據(jù)201 CREATED - [POST]:用戶新建數(shù)據(jù)成功。204 NO CONTENT - [DELETE]:用戶刪除數(shù)據(jù)成功。400 INVALID request - [POST/PUT]:用戶發(fā)出的請求有錯誤,服務(wù)器沒有進(jìn)行新建或修改數(shù)據(jù)的操作404 NOT FOUND - [*]:用戶發(fā)出的請求針對的是不存在的記錄,服務(wù)器沒有進(jìn)行操作,該操作是冪等的。。500 INTERNAL SERVER ERROR - [*]:服務(wù)器發(fā)生錯誤,用戶將無法判斷發(fā)出的請求是否成功。
了解:
- 域名:使用專有域名
- 版本:將版本信息放在url地址
- 錯誤:將錯誤信息返回
- 在訪問api接口時,將和接口相關(guān)的其他API接口的地址也在響應(yīng)數(shù)據(jù)中返回
使用Django開發(fā)REST API
需求: 設(shè)計一套符合RestAPI風(fēng)格的接口,提供以下5個接口: 1. 獲取所有直播間數(shù)據(jù):GET /lives/ 2. 新增一本直播間數(shù)據(jù):POST /lives/ 3. 獲取指定的直播間數(shù)據(jù)(根據(jù)id):GET /lives/(?P<pk>d )/ 4. 修改指定的直播間數(shù)據(jù)(根據(jù)id):PUT /lives/(?P<pk>d )/ 5. 刪除指定的直播間數(shù)據(jù)(根據(jù)id):DELETE /lives/(?P<pk>d )/
模型類定義
# models.pyclass LiveInfo(models.Model): live_id = models.IntegerField(verbose_name='直播間號') live_streamer = models.CharField(max_length=20, verbose_name='主播名字') live_title = models.CharField(max_length=30, verbose_name='直播間標(biāo)題') live_pop = models.IntegerField(default=0, verbose_name='人氣') live_content = models.CharField(default='未設(shè)定', max_length=20, verbose_name='直播類型') is_delete = models.BooleanField(default=False, verbose_name='刪除標(biāo)記') class Meta: db_table = 'tb_lives' verbose_name = '直播間' verbose_name_plural = verbose_name def __str__(self): return self.live_streamer
views.py文件中定義視圖實現(xiàn)API接口:
class LiveListView(View): def get(self, request): """獲取所有直播間""" lives = LiveInfo.objects.all() lives_list = [] for live in lives: lives_list.append({ 'live_id': live.live_id, 'live_streamer': live.live_streamer, 'live_title': live.live_title, 'live_pop': live.live_pop, 'live_content': live.live_content }) return JsonResponse(lives_list, safe=False) def post(self, request): """新增直播間""" passclass LiveDetailView(View): def get(self, request, pk): """查詢一間直播間信息""" pass def put(self, request, pk): """修改直播間信息""" pass def delete(self, request, pk): """刪除直播間""" pass
RestAPI開發(fā)核心工作
分析上節(jié)直播間管理的5個API接口,可以發(fā)現(xiàn),在開發(fā)REST API接口時,視圖中做的最主要有三件事:
- 將請求的數(shù)據(jù)(如JSON格式)轉(zhuǎn)換為模型類對象
- 操作數(shù)據(jù)庫
- 將模型類對象轉(zhuǎn)換為響應(yīng)的數(shù)據(jù)(如JSON格式)
在以上操作中,涉及到兩個概念:序列化和反序列化。
序列化Serialization
將程序中的一個數(shù)據(jù)結(jié)構(gòu)類型轉(zhuǎn)換為其他格式(字典、JSON、XML等),例如將Django中的模型類對象轉(zhuǎn)換為字典或JSON字符串,這個轉(zhuǎn)換過程我們稱為序列化。
反序列化
將其他格式(字典、JSON、XML等)轉(zhuǎn)換為程序中的數(shù)據(jù),例如將JSON字符串或字典轉(zhuǎn)換保存為Django中的模型類對象,這個過程我們稱為反序列化。
在開發(fā)REST API接口時,我們在視圖中在做的最核心的事是:
- 將數(shù)據(jù)庫數(shù)據(jù)序列化為前端所需要的格式,并返回。
- 將前端發(fā)送的數(shù)據(jù)反序列化保存到模型類對象,并保存到數(shù)據(jù)庫中。
Django REST framework 簡介
Django REST framework 框架是一個用于構(gòu)建Web API 的強大而又靈活的工具。通常簡稱為DRF框架 或 REST framework。
作用:幫助我們大大提高REST API的開發(fā)速度
核心功能:
- 序列化器:序列化和反序列化
- 類視圖,MiXin擴(kuò)展類:簡化視圖代碼的編寫
環(huán)境安裝與使用:
pip install djangorestframework# 注:DRF框架依賴于Django,需要先安裝Django環(huán)境,再安裝djangorestframework。
在Django項目中使用DRF框架進(jìn)行開發(fā)時,需要將rest_framework在INSTALLED_APPS中進(jìn)行注冊:
INSTALLED_APPS = [ ... 'rest_framework',]
序列化器
序列化器類定義
from rest_framework import serializersclass LiveInfoSerializer(serializers.Serializer): id = serializers.IntegerField(label='ID', read_only=True) live_id = serializers.IntegerField(label='直播間號') live_streamer = serializers.CharField(max_length=20, label='主播名字', required=False) live_title = serializers.CharField(max_length=30, label='直播間標(biāo)題') live_pop = serializers.IntegerField(default=0, label='人氣', required=False) live_content = serializers.CharField(default='未設(shè)定', max_length=20, label='直播類型', required=False) is_delete = serializers.BooleanField(default=False, label='刪除標(biāo)記', required=False)
選項參數(shù):
- write_only:為True,字段只在反序列化時使用
- read_only:為True,字段只在序列化時使用
- required:為True,如果字段在反序列化時使用,該字段必傳傳入
- default:設(shè)置序列化和反序列化操作時的默認(rèn)值
- max_length和min_length:設(shè)置字符串的最大長度和最小長度
- max_value和min_value:設(shè)置數(shù)字的最大值和最小值
序列化操作
- 序列化單個對象:序列化單個對象obj時,在創(chuàng)建序列化器對象時,將obj傳遞給instance即可
from livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveDetailView(View): def get(self, request, pk): """查詢一間直播間信息""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: return HttpResponse(status=404) # 創(chuàng)建序列化器對象 serializer = LiveInfoSerializer(live) # 返回序列化之后的數(shù)據(jù) return JsonResponse(serializer.data)
- 序列化多個對象:如果要被序列化的是包含多個對象的查詢集QuerySet或list,在創(chuàng)建序列化器對象時,需要添加many=True參數(shù)
class LiveListView(View): def get(self, request): """獲取所有直播間""" lives = LiveInfo.objects.all() # 創(chuàng)建序列化器對象 serialize# 獲取序列化之后的數(shù)據(jù)r = LiveInfoSerializer(lives, many=True) return JsonResponse(serializer.data, safe=False)
- 關(guān)聯(lián)對象嵌套序列化
如果在序列化對象數(shù)據(jù)時,需要將其關(guān)聯(lián)的對象一并序列化,則定義序列化器類的字段時,需要在定義對應(yīng)的關(guān)聯(lián)對象嵌套序列化字段,比如和直播分類關(guān)聯(lián)的直播間。對于嵌套關(guān)聯(lián)字段,可以采用以下3種方式進(jìn)行定義:
1.PrimaryKeyRelatedField :將關(guān)聯(lián)對象序列化為關(guān)聯(lián)對象的主鍵。
# 在LiveInfoSerializer中添加此字段Ltype = serializers.PrimaryKeyRelatedField(label='直播類型', read_only=True)或Ltype = serializers.PrimaryKeyRelatedField(label='直播類型', queryset=LiveInfo.objects.all())
2.使用關(guān)聯(lián)對象的序列化器
Ltype = LiveInfoSerializer()
3.StringRelatedField:將關(guān)聯(lián)對象序列化為關(guān)聯(lián)對象模型類__str__方法的返回值。
Ltype = serializers.StringRelatedField(label='直播類型')
如果和一個對象關(guān)聯(lián)的對象有多個,在序列化器類中定義嵌套序列化字段時,需要多添加一個many=True參數(shù)。
反序列化操作
# 1. 創(chuàng)建序列化器對象serializer = 序列化器類(data=<待校驗字典數(shù)據(jù)>)# 2. 數(shù)據(jù)校驗:成功返回True,失敗返回Falseserializer.is_valid()# 3. 獲取校驗成功之后的數(shù)據(jù)serializer.validated_data# 4. 如果校驗失敗,獲取失敗的錯誤提示信息serializer.errors
調(diào)用is_valid時,會根據(jù)對應(yīng)序列化類字段是否需要傳遞、字段類型以及一些選項參數(shù)對data中的數(shù)據(jù)進(jìn)行校驗。
在調(diào)用is_valid進(jìn)行數(shù)據(jù)校驗時,除了一些基本的默認(rèn)驗證行為,可能還需要補充一些驗證行為,可以使用以下三種方法:
數(shù)據(jù)校驗通過之后,可以調(diào)用序列化對象的save方法進(jìn)行數(shù)據(jù)保存,save方法內(nèi)部會調(diào)用對應(yīng)序列化器類中的create或update方法,可以在create中實現(xiàn)數(shù)據(jù)新增,在update中實現(xiàn)數(shù)據(jù)更新。
ModelSerializer使用
如果序列化器類對應(yīng)的是Django的某個模型類,則定義序列化器類時,可以直接繼承于ModelSerializer。
ModelSerializer是Serializer類的子類,相對于Serializer,提供了以下功能:
- 基于模型類字段自動生成序列化器類的字段
- 包含默認(rèn)的create()和update()方法的實現(xiàn)
class LiveInfoSerializer(serializers.ModelSerializer): class Meta: model = LiveInfo # 使用fields來指明依據(jù)模型類的哪些字段生成序列化器類的字段,__all__表明包含所有字段,也可以指明具體哪些字段 # fields = '__all__' # fields = ('id', 'title', ...) # 使用exclude可以指明排除哪些字段 exclude = ['is_delete'] extra_kwargs = { # 'live_title': {'validators': [live_name]}, 'live_streamer': {'required': False}, 'live_pop': {'min_value': 0, 'required': False}, }
案例-序列化器改寫后的視圖代碼:
import jsonfrom django.http import JsonResponse, HttpResponsefrom django.views import Viewfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(View): def get(self, request): """獲取所有直播間""" lives = LiveInfo.objects.all() serializer = LiveInfoSerializer(lives, many=True) return JsonResponse(serializer.data, safe=False) def post(self, request): """新增直播間""" json_dict = json.dumps(request.body.decode()) serializer = LiveInfoSerializer(data=json_dict) serializer.is_valid(raise_exception=True) serializer.save() # 返回新增直播間 return JsonResponse(serializer.data, status=201)class LiveDetailView(View): def get(self, request, pk): """查詢一間直播間信息""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: return HttpResponse(status=404) serializer = LiveInfoSerializer(live) return JsonResponse(serializer.data) def put(self, request, pk): """修改直播間信息""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: return HttpResponse(status=404) json_dict = json.dumps(request.body.decode()) serializer = LiveInfoSerializer(live, data=json_dict) serializer.is_valid(raise_exception=True) serializer.save() return JsonResponse(serializer.data) def delete(self, request, pk): """刪除直播間""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: return HttpResponse(status=404) # 參數(shù)校驗 live.delete() return HttpResponse(status=204)
視圖部分
APIView
APIView是REST framework提供的所有視圖的基類,繼承自Django的View類。
APIView和View的區(qū)別:
- 請求對象:Request類的對象request.data:解析之后的請求體數(shù)據(jù) ,request.query_params:解析之后的查詢字符串?dāng)?shù)據(jù)
- 響應(yīng)對象:Response類對象統(tǒng)一返回Response對象,原始響應(yīng)數(shù)據(jù)根據(jù)客戶端請求頭Accept轉(zhuǎn)換為對應(yīng)的格式進(jìn)行返回
- 異常處理機(jī)制默認(rèn)異常處理機(jī)制,會將視圖中出現(xiàn)的異常處理成合適的響應(yīng)返回給客戶端
- 其他:認(rèn)證&權(quán)限&限流
案例-使用APIView改寫RestAPI:
GenericAPIView
繼承自APIView,在APIView功能基礎(chǔ)上,主要增加了操作序列化器和數(shù)據(jù)庫查詢的屬性和方法。
GenericAPIView和APIView的區(qū)別:
- 繼承自APIView
- 封裝了操作序列化器的屬性和方法屬性:serializer_class方法:get_serializer_class和get_serializer
- 封裝了數(shù)據(jù)庫操作的屬性和方法屬性:queryset方法:get_queryset和get_object
- 其他:過濾和分頁
案例-使用GenericAPIView改寫RestAPI:
from rest_framework import statusfrom rest_framework.generics import GenericAPIViewfrom rest_framework.response import Responsefrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(GenericAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all() def get(self, request): """獲取所有直播間""" queryset = self.get_queryset() serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) def post(self, request): """新增直播間""" serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() # 返回新增直播間 return Response(serializer.data, status=status.HTTP_201_CREATED)class LiveDetailView(GenericAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all() def get(self, request, pk): """查詢一間直播間信息""" instance = self.get_object() serializer = self.get_serializer(instance) return Response(serializer.data) def put(self, request, pk): """修改直播間信息""" instance = self.get_object() serializer = self.get_serializer(instance, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) def delete(self, request, pk): """刪除直播間""" instance = self.get_object() # 參數(shù)校驗 instance.delete() return Response(status=status.HTTP_204_NO_CONTENT)
Mixin擴(kuò)展類
使用GenericAPIView改寫之后的RestAPI接口中,圖書管理的各個代碼已經(jīng)變成了通用的代碼,這些代碼和視圖所使用的序列化器類和查詢集已經(jīng)沒有直接的關(guān)系,DRF框架已經(jīng)將這些代碼做了封裝,就是5個Mixin擴(kuò)展類。
案例-Mixin改寫RestAPI接口:
from rest_framework import mixinsfrom rest_framework.generics import GenericAPIViewfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(mixins.ListModelMixin, mixins.CreateModelMixin,GenericAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all() def get(self, request): """獲取所有直播間""" return self.list(request) def post(self, request): """新增直播間""" return self.create(request)class LiveDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, GenericAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all() def get(self, request, pk): """查詢一間直播間信息""" return self.retrieve(request, pk) def put(self, request, pk): """修改直播間信息""" return self.update(request, pk) def delete(self, request, pk): """刪除直播間""" return self.destroy(request, pk)
子類視圖:Django框架為了方便視圖的編寫,還提供了9個子類視圖類。
子類視圖一定同時繼承了GenericAPIView和對應(yīng)的Mixin擴(kuò)展類,同時類中提供了對應(yīng)的請求處理方法,并且請求處理方法中調(diào)用的就是Mixin擴(kuò)展類中封裝的通用方法。
案例-子類視圖改寫RestAPI接口:
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIViewfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(ListCreateAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all()class LiveDetailView(RetrieveUpdateDestroyAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all()
視圖集ViewSet
將操作同一組資源相關(guān)的處理方法放在同一個類中,這個類叫做視圖集。
基本使用:
常用視圖集父類:
- ViewSet:繼承自APIView與ViewSetMixin,作用也與APIView基本類似,提供了身份認(rèn)證、權(quán)限校驗、流量管理等。
- GenericViewSet:繼承自GenericAPIView與ViewSetMixin,可以直接搭配Mixin擴(kuò)展類使用。
- ModelViewSet:繼承自GenericViewSet和5個Mixin擴(kuò)展類。
- ReadOnlyModelViewSet:繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin。
案例-ViewSet改寫:
from django.http import Http404from rest_framework import statusfrom rest_framework.response import Responsefrom rest_framework.viewsets import ViewSetfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveInfoViewSet(ViewSet): def list(self, request): """獲取所有直播間""" lives = LiveInfo.objects.all() serializer = LiveInfoSerializer(lives, many=True) return Response(serializer.data) def create(self, request): """新增直播間""" serializer = LiveInfoSerializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() # 返回新增直播間 return Response(serializer.data, status=status.HTTP_201_CREATED) def retrieve(self, request, pk): """查詢一間直播間信息""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: raise Http404 serializer = LiveInfoSerializer(live) return Response(serializer.data) def update(self, request, pk): """修改直播間信息""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: raise Http404 serializer = LiveInfoSerializer(live, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) def destroy(self, request, pk): """刪除直播間""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: raise Http404 # 參數(shù)校驗 live.delete() return Response(status=status.HTTP_204_NO_CONTENT)# urls.pyfrom django.conf.urls import urlfrom booktest import viewsurlpatterns = [ url(r'^lives/$', views.LiveInfoViewSet.as_view({ 'get': 'list', 'post': 'create' })), url(r'^lives/(?P<pk>d )/$', views.LiveInfoViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'delete': 'destroy' }))]
案例-GenericViewSet改寫:
案例-ModelViewSet并添加了一些額外的方法改寫:
from rest_framework.decorators import actionfrom rest_framework.response import Responsefrom rest_framework.viewsets import ModelViewSetfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveInfoView(ModelViewSet): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all() lookup_value_regex = 'd ' @action(methods='get', detail=False) def latest(self, request): """查詢最新的直播間""" live = LiveInfo.objects.latest('pk') serializer = self.get_serializer(live) return Response(serializer.data) @action(methods='put', detail=True) def change_pop(self, request, pk): """修改指定直播間的人氣""" live = self.get_object() live.bread = request.data.get('live_pop') live.save() serializer = self.get_serializer(live) return Response(serializer.data)# urls.pyfrom django.conf.urls import urlfrom . import viewsurlpatterns = [ url(r'^lives/$', views.LiveInfoViewSet.as_view({ 'get': 'list', 'post': 'create' })), url(r'^lives/(?P<pk>d )/$', views.LiveInfoViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'delete': 'destroy' })), url(r'^lives/latest/$', views.LiveInfoViewSet.as_view({ 'get': 'latest' })), url(r'^lives/(?P<pk>d )/change_pop/$', views.LiveInfoViewSet.as_view({ 'put': 'change_pop' }))]
路由Router:動態(tài)生成視圖集中處理方法的url配置項。
對于視圖集ViewSet,除了可以自己手動進(jìn)行URL配置指明請求方式與action處理函數(shù)之間的對應(yīng)關(guān)系外,還可以使用路由Router來自動生成路由信息。
REST framework提供了兩個Router類:
- SimpleRouter
- DefaultRouter
案例-router改寫:
# urls.pyfrom rest_framework.routers import DefaultRouterfrom livetest import viewsurlpatterns = []# 創(chuàng)建Router對象router = DefaultRouter()# 注冊routerrouter.register('lives', views.LiveInfoView, base_name='live')# 添加配置項urlpatterns = router.urls
DRF框架到此常用的功能都已經(jīng)講解完畢了,當(dāng)然DRF框架還有其他功能:認(rèn)證、權(quán)限、限流、過濾、排序、分頁和異常處理機(jī)制。將在后面講述,敬請關(guān)注。
作者簡介:Python菜鳥工程師,將在接下來的一段時間內(nèi)與大家分享一些與Python相關(guān)的知識點。如若文中出現(xiàn)問題,各位大佬多多指點,互相學(xué)習(xí)。喜歡的關(guān)注一個吧!謝謝!