day73:drf:drf檢視相關類&路由Routers&建立虛擬環境

Poke發表於2020-10-22

目錄

1.APIView

2.GenericAPIView:通用檢視類

3.5個檢視擴充套件類:ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin

4.GenericAPIView的檢視子類:ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView

5.檢視集基類:ViewSet,GenericViewSet,ModelViewSet,ReadOnlyViewSet

  1.ViewSet

  2.ViewSet+????APIView

  3.GenericViewSet+???Mixin

  4.ModelViewSet

  5.ReadOnlyViewset

6.路由:Routers

  1.如何新增路由資料

  2.在檢視集中附加action宣告

  3.DefaultRouter和SimpleRouter的區別(瞭解)

7.建立虛擬環境

附:APIView思維導圖

附:drf的執行流程圖

APIView

APIView總結

1.APIView中封裝了自己的request和response

2.django:request.GET對應drf中的request.query_params

3.django:request.POST對應drf中的request.data

4.APIView中的response不需要寫引數safe=False,ensure_ascii這些東西了,因為這些功能已經封裝在response裡面了

5.當我們在瀏覽器上訪問一個url,給你回覆的是一個介面。如果我們用postman訪問url,給你返回的就是json資料。這是因為response中有相關配置導致的。

APIView實現程式碼

class Students1View(APIView):
    
    # 獲取所有資料介面
    def get(self,request):
        all_data = models.Student.objects.all() # 獲取資料庫中Student表中所有資料
        serializer = StudentSerializer(instance=all_data,many=True) # 將後端資料序列化
        return Response(serializer.data) # 將序列化之後的資料傳遞給前端

    # 新增一條記錄的介面
    def post(self,request):
        
        data = request.data # 獲取使用者在前端輸入的資料
        serializer = StudentSerializer(data=data) # 將資料反序列化
        if serializer.is_valid(): # 校驗
            instance = serializer.save()  # instance為新增的新紀錄物件
            serializer = StudentSerializer(instance=instance) # 根據API規範,資料儲存之後應該顯示到前端上,所以需要將新新增的資料序列化

            return Response(serializer.data,status=status.HTTP_201_CREATED)

        else:
            print(serializer.errors)


class Student1View(APIView):

    # 獲取單條記錄
    def get(self,request,pk): 
        stu_obj = models.Student.objects.get(pk=pk)
        serializer = StudentSerializer(instance=stu_obj)
        return Response(serializer.data)

    # 更新單條記錄
    def put(self,request,pk):
        stu_obj = models.Student.objects.get(pk=pk)
        data = request.data
        serializer = StudentSerializer(instance=stu_obj, data=data, partial=True)
        if serializer.is_valid():
            instance = serializer.save()  # instance為新增的新紀錄物件
            serializer = StudentSerializer(instance=instance)
            return Response(serializer.data)
        else:
            print(serializer.errors)

    # 刪除單條記錄
    def delete(self,request,pk):
        models.Student.objects.get(pk=pk).delete()
        return Response(None,status=status.HTTP_204_NO_CONTENT)

GenericAPIView:通用檢視類

GenerivAPIView繼承自APIView方法,主要增加了操作序列化器和資料庫查詢的方法。

from rest_framework.generics import GenericAPIView


class Students2View(GenericAPIView,):
    queryset = models.Student.objects.all()  # 指明要使用的資料查詢集[必須指定]
    serializer_class = StudentSerializer  # 指明檢視要使用的序列化器[可寫可不寫]

    # 通過get_serializer_class來控制不同條件下,使用不同的序列化器類
    def get_serializer_class(self):
        if self.request.method == 'GET':
            return Student2Serializer
        else:
            return StudentSerializer

    # 獲取所有資料介面
    def get(self, request):
        # all_data = models.Student.objects.all()
        # serializer = StudentSerializer(instance=all_data, many=True)
        serializer = self.get_serializer(instance=self.get_queryset(), many=True)
        return Response(serializer.data)

    # 新增一條記錄的介面
    def post(self, request):
        data = request.data
        serializer = self.get_serializer(data=data)
        if serializer.is_valid():
            instance = serializer.save()  # instance為新增的新紀錄物件
            serializer = self.get_serializer(instance=instance)

            return Response(serializer.data, status=status.HTTP_201_CREATED)

        else:
            print(serializer.errors)
            return Response({'error':'欄位錯誤'})

class Student2View(GenericAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentSerializer

    # 獲取單條記錄
    def get(self, request, pk):
        # stu_obj = models.Student.objects.get(pk=pk)
        serializer = self.get_serializer(instance=self.get_object())
        return Response(serializer.data)

    # 更新單條記錄
    def put(self, request, pk):
        # stu_obj = models.Student.objects.get(pk=pk)
        data = request.data
        serializer = self.get_serializer(instance=self.get_object(), data=data, partial=True)
        if serializer.is_valid():
            # print('>>>',serializer.data)  #在save方法之前,不能呼叫data屬性,serializer.data
            instance = serializer.save()  # instance為新增的新紀錄物件
            print(serializer.data)  #之後可以看
            serializer = self.get_serializer(instance=instance)

            return Response(serializer.data)
        else:
            print(serializer.errors)

    # 刪除單條記錄

    def delete(self, request, pk):

        models.Student.objects.get(pk=pk).delete()
        return Response(None, status=status.HTTP_204_NO_CONTENT)

GenericAPIView中的屬性和方法

serializer_class 指明檢視使用的序列化器

get_serializer(self, *args, **kwargs) 返回序列化器物件,主要用來提供給Mixin擴充套件類使用,如果我們在檢視中想要獲取序列化器物件,也可以直接呼叫此方法

queryset 指明使用的資料查詢集

 

get_queryset(self) 返回檢視使用的查詢集,主要用來提供給Mixin擴充套件類使用,是列表檢視與詳情檢視獲取資料的基礎,預設返回queryset屬性,可以重寫

get_object(self) 返回詳情檢視所需的模型類資料物件,主要用來提供給Mixin擴充套件類使用。在檢視中可以呼叫該方法獲取詳情資訊的模型類物件。

get_serializer_class(self) 當出現一個檢視類中呼叫多個序列化器時,那麼可以通過條件判斷在get_serializer_class方法中通過返回不同的序列化器類名就可以讓檢視方法執行不同的序列化器物件了

            返回序列化器類,預設返回serializer_class

5個檢視擴充套件類:????ModelMixin

提供了幾種後端檢視(對資料資源進行增刪改查)處理流程的實現,如果需要編寫的檢視屬於這五種,則檢視可以通過繼承相應的擴充套件類來複用程式碼,減少自己編寫的程式碼量。

這五個擴充套件類需要搭配GenericAPIView父類,因為五個擴充套件類的實現需要呼叫GenericAPIView提供的序列化器與資料庫查詢的方法。

換句話說。就是你在檢視中的get post put等這些方法中不用寫裡面的程式碼了,裡面的程式碼相關的操作已經被封裝到對應的Mixin中了。

class Students3View(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = models.Student.objects.all()  
    serializer_class = StudentSerializer  

    # 獲取所有資料介面
    def get(self, request):
        return self.list(request)

    # 新增一條記錄的介面
    def post(self, request):
        return self.create(request)

class Student3View(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = models.Student.objects.all()
    serializer_class = StudentSerializer

    # 獲取單條記錄
    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)

GenericAPIView的檢視子類:????APIView

上面的程式碼還是過於麻煩,因為既要繼承GenericAPIView又要繼承Mixin系列的類。

所以將各自的操作封裝成自己的APIView類。用哪個繼承哪個。

而且連函式都不用寫了,在???APIView類中已經有了,所以不用再寫了,繼承自己的APIView類即可。

from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView

class Students4View(ListAPIView,CreateAPIView):
    queryset = models.Student.objects.all() 
    serializer_class = StudentSerializer 

class Student4View(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentSerializer

檢視集基類:ViewSet,GenericViewSet,ModelViewSet,ReadOnlyViewSet

ViewSet

主要解決問題:之前我們在寫檢視類的時候,獲取多條資料和提交資料放在了一個類中。獲取單條資料,更新單條資料,刪除多條資料放到了一個類中

這是因為多條資料操作時不需要指定pk值,而針對單條資料時,需要指定pk值。也就是說需要知道你要操作哪一條資料

這樣我們就很矛盾。我們沒法將5個方法放到同一個類中。

而下面的ViewSet就可以解決這個問題,將5個方法放到同一個類中

from rest_framework.viewsets import ViewSet

class Students5View(ViewSet):
    # 獲取所有資料介面
    def get_all_student(self,request):  # action
        all_data = models.Student.objects.all()
        serializer = StudentSerializer(instance=all_data,many=True)
        return Response(serializer.data)

    # 新增一條記錄的介面
    def add_student(self,request):
        data = request.data
        serializer = StudentSerializer(data=data)
        if serializer.is_valid():
            instance = serializer.save()  #instance為新增的新紀錄物件
            serializer = StudentSerializer(instance=instance)

            return Response(serializer.data,status=status.HTTP_201_CREATED)

        else:
            print(serializer.errors)

    def get_one(self,request,pk):
        stu_obj = models.Student.objects.get(pk=pk)
        serializer = StudentSerializer(instance=stu_obj)
        return Response(serializer.data)

通過上面程式碼我們可以看出。單條資料操作和多條資料被放到了一個類中。並且方法名也不再侷限於必須要使用get post put...這些方法名了。

這是因為在urls.py中的as_view方法做了一個請求方法和對應函式名的指定

而獲取單條資料和獲取多條資料能夠共存在一個類中,是因為他們處於兩個url中。雖然都是get請求過去的,但是他們在不同的url中,會執行各自的檢視方法

通過下面url,我們就可以發揮出ViewSet的用處了:

path('students5/', views.Students5View.as_view({'get':'get_all_student','post':'add_student'})),
re_path('students5/(?P<pk>\d+)/', views.Students5View.as_view({'get':'get_one'})),

ViewSet+????APIView

用我們剛才學的ViewSet和之前的???APIView做一個結合~

效果:即放到了一個類中,而且各個方法裡不用寫詳細程式碼(????APIView的作用)

"""發揮下ViewSet的作用"""
from rest_framework.viewsets import ViewSet
from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView
class Student2ViewSet(ViewSet,ListAPIView,CreateAPIView,RetrieveAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
def get_all(self,request): return self.list(request) def add_student(self,request): return self.create(request) def get_one(self,request,pk): return self.retrieve(request)

GenericViewSet+???Mixin

效果和Viewset+???APIView是相同的

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin
class Student3ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    def get_all(self,request):
        return self.list(request)

    def add_student(self,request):

        return self.create(request)

    def get_one(self,request,pk):
        return self.retrieve(request)

現在我們的程式碼是這個樣子的。美中不足是我們還需要自己寫def方法,其實我們希望的是類裡面沒有def方法的

其實。這個需求剛才在前面已經提到了。

只需要在urls.py中將get post put這類請求型別直接與Mixin中內建的list,create,update等動作方法關聯起來。

這樣它就會自動去執行裡面的list,create,update方法。不需要你在檢視裡定義一個函式名稱然後再return self.create()了

urls.py如下所示:

urlpatterns = [
    path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students7/(?P<pk>\d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})),
]

url直接就是{請求型別:action動作名},這樣我們在檢視那一塊的程式碼又可以縮減了。函式全部無需定義了。

經過改良後,檢視程式碼如下所示:

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

ModelViewSet

上面的程式碼,在匯入和繼承的時候太麻煩了,需要寫5個Mixin類

這時出現了ModelViewSet,ModelViewSet同時繼承ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin這五個類。

這樣寫起來就更加簡單了。

終極版如下所示:

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
from rest_framework.decorators import action
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

ReadOnlyViewset

繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin。

檢視集中定義附加action動作

在檢視集中,除了上述預設的方法動作外,還可以新增自定義動作,進行擴充套件。

舉例,比如做一個登入方法login:

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def login(self,request):  # 這個就可以稱為自定義的action動作
        """學生登入功能"""
        return Response({"message":"登入成功"})

urls.py這樣寫

urlpatterns = [
    path("students8/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students8/(?P<pk>\d+)/",
            views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),

    path("stu/login/",views.StudentModelViewSet.as_view({"get":"login"}))

]

在檢視集中,我們可以通過action物件屬性來獲取當前請求檢視集時的action動作是哪個

from rest_framework.viewsets import ModelViewSet
from students.models import Student
from .serializers import StudentModelSerializer
from rest_framework.response import Response
class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    def get_new_5(self,request):
        """獲取最近新增的5個學生資訊"""
        # 運算元據庫
        print(self.action) # 獲取本次請求的檢視方法名
        
        
'''通過路由訪問到當前方法中.可以看到本次的action就是請求的方法名'''

路由:Routers

如何新增路由資料

如何新增路由資料,並且和檢視函式做關聯:

from django.urls import path, re_path
from . import views
urlpatterns = [
    ...
]

"""使用drf提供路由類router給檢視集生成路由列表"""
# 例項化路由類
# drf提供一共提供了兩個路由類給我們使用,他們用法一致,功能幾乎一樣
from rest_framework.routers import DefaultRouter
router = DefaultRouter()

# 註冊檢視集
# router.register("路由字首",檢視集類)
router.register("router_stu",views.StudentModelViewSet)

# 把生成的路由列表追加到urlpatterns
urlpatterns += router.urls

上面的程式碼就成功生成了路由地址[增/刪/改/查一條/查多條的功能],但是不會自動幫我們在檢視集自定義方法的路由。

所以我們如果也要給自定義方法生成路由,則需要進行action動作的宣告

在檢視集中附加action宣告

在檢視集中,如果想要讓Router自動幫助我們為自定義的動作生成路由資訊,需要使用rest_framework.decorators.action裝飾器

以action裝飾器裝飾的方法名會作為action動作名,與list、retrieve等同。

action裝飾器可以接收兩個引數:

  • methods: 宣告該action對應的請求方式,列表傳遞

  • detail:

      宣告該action的路徑是否與單一資源對應,及是否是

    xxx/<pk>/action方法名/
    • True 表示路徑格式是xxx/<pk>/action方法名/

    • False 表示路徑格式是xxx/action方法名/

from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action

class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    # methods 設定當前方法允許哪些http請求訪問當前檢視方法
    '''detail 設定當前檢視方法是否是操作一個資料'''
    # detail為True,表示路徑名格式應該為 router_stu/{pk}/login/
    @action(methods=['get'], detail=True)
    def login(self, request,pk):
        """登入"""
        ...

    # detail為False 表示路徑名格式應該為 router_stu/get_new_5/
    @action(methods=['put'], detail=False)
    def get_new_5(self, request):
        """獲取最新新增的5個學生資訊"""
        ...

DefaultRouter和SimpleRouter的區別(瞭解)

DefaultRouter與SimpleRouter的區別是,DefaultRouter會多附帶一個預設的API根檢視,返回一個包含所有列表檢視的超連結響應資料。

比如使用DefaultRouter時你訪問一下http://127.0.0.1:8001/router_stu/ 會看到一個頁面。

而SimpleRouter會報錯,一般都需要有個檢視所有介面的頁面,所以我們基本都是用的是DefaultRouter

建立虛擬環境

1.安裝虛擬環境

# 1.管理員方式開啟終端

# 2.安裝虛擬環境
pip install virtualenv -i https://pypi.douban.com/simple
    
# 3.終端關閉,再重新開啟

# 4.注意:建立[環境名稱]資料夾,放置所有的環境,進入指定目錄 D:/envs

# 5.建立虛擬環境
virtualenv 環境名稱 'C:\python\python3.6.exe'

'''這樣寫的前提是你的電腦已經將python加入到環境變數中'''
virtualenv 環境名稱 --python=python3.6

2.啟用虛擬環境

# 1.進入到虛擬環境資料夾下的Scripts目錄
activate '''啟用虛擬環境'''

# 2.退出虛擬環境
deactivate '''退出虛擬環境'''

3.在虛擬環境中安裝模組

# 1.啟用虛擬環境

# 2.在啟用的虛擬環境中安裝模組
pip3 install django==2.2 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
    
# 3.搭建專案環境,在pycharm選擇直譯器時,選擇你之前已經建立好的虛擬環境

4.在pycharm上使用虛擬環境

附:APIView思維導圖

附:drf的執行流程圖

 

相關文章