DjangoREST_framework框架02-創(chuàng)新互聯(lián)

mixin類編寫視圖

將上一篇文章中的寫法進一步封裝簡化

成都創(chuàng)新互聯(lián)主要從事成都網站設計、成都網站制作、網頁設計、企業(yè)做網站、公司建網站等業(yè)務。立足成都服務唐縣,10多年網站建設經驗,價格優(yōu)惠、服務專業(yè),歡迎來電咨詢建站服務:18982081108

urls

from app01 import views
urlpatterns = [
    ......
    url(r'^authors/$', views.AuthorView.as_view(), name="author"),
    url(r'^authors/(?P<pk>\d+)/$', views.AuthorDetailView.as_view(), name="detail_author"),
]

還要寫一個ModelSerializer,方法與上一篇博文中相同
views

from rest_framework import mixins
from rest_framework import generics

#GenericAPIView繼承了APIView
class AuthorView(mixins.ListModelMixin,    #查看所有
                 mixins.CreateModelMixin,  #添加
                 generics.GenericAPIView):
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerializers

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

class AuthorDetailView(mixins.DestroyModelMixin,   #刪除
                       mixins.RetrieveModelMixin,  #查看單條
                       mixins.UpdateModelMixin,    #更新
                       generics.GenericAPIView):
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerializers

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

viewsets.ModelViewSet

這是最終封裝版本,關鍵在于讓兩條不同的url(帶pk值和不帶pk值)都匯聚到同一個視圖類中

urls.py:

url(r'^authors/$', views.AuthorView.as_view({"get":"list","post":"create"}),name="author"),
url(r'^authors/(?P<pk>\d+)$', views.AuthorView.as_view({
            'get': 'retrieve',
            'put': 'update',
            'patch': 'partial_update',
            'delete': 'destroy'
        }),name="detail_author"),

views.py:

from rest_framework import viewsets

class AuthorView(viewsets.ModelViewSet):
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerializers

源碼解析

以下面這個url為例子,我們可以看到這條url大的變化就是as_view后面?zhèn)髦盗耍虼艘纯词侨绾翁幚淼?/p>

url(r'^authors/$', views.AuthorView.as_view({"get": "list", "post": "create"}), name="author")

我們需要看看此時的as_view是如何用一個視圖類處理兩條url的,首先尋找這個as_view方法在哪里,事實上它已經不是原來的as_view方法了
AuthorView類-ModelViewSet類-GenericViewSet類-ViewSetMixin類

在ViewSetMixin類中找到as_view方法

def as_view(cls, actions=None, **initkwargs):
    ......
    return csrf_exempt(view)

找到同在ViewSetMixin類中的view:

def as_view(cls, actions=None, **initkwargs):
    ......
    def view(request, *args, **kwargs):
        ......
        for method, action in actions.items(): #循環(huán)actions{"get": "list", "post": "create"}
            handler = getattr(self, action)    #handler = self.list或handler = self.create
            setattr(self, method, handler)     #self.get = self.list或self.post = self.create
            ......
        return self.dispatch(request, *args, **kwargs)

Django啟動后的url就等同于下面的情況,等待用戶訪問

url(r'^authors/$', ViewSetMixin.view({"get": "list", "post": "create"}), name="author")


用戶訪問開始后:
在views.APIView中找到self.dispatch:

class APIView(View):
    def dispatch(self, request, *args, **kwargs):
        try:
            ......
            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), #此處的request.method.lower()是字符串,get或post
                                  self.http_method_not_allowed)
                #因為上面已經通過反射綁定self.get = self.list或self.post = self.create,
                #因此這里:
                #handler = self.list或self.create
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs) #這里去找self.list或self.create,將執(zhí)行的結果返回給response

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response #將ListModelMixin處理后的結果返回給請求者

self.list或self.create在ModelViewSet類的父類mixins.ListModelMixin或mixins.CreateModelMixin中

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass

ListModelMixin類將數(shù)據(jù)處理并序列化后返回給APIView下的dispatch

class ListModelMixin(object):
    """
    List a queryset.
    """
    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)

思考:viewsets.ModelViewSet通過覆蓋APIView中同名的as_view來實現(xiàn)了新功能,如果有需求的話我們也可以通過覆蓋同名方法來實現(xiàn)新的功能,例如我們可以自己寫一個list方法來實現(xiàn)不同的需求

認證組件

#認證組件
self.perform_authentication(request)
#權限組件
self.check_permissions(request)
#頻率組件
self.check_throttles(request)

局部視圖認證

在app01.service.auth.py:

from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication

class TokenAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get("token")
        token_obj = Token.objects.filter(token=token).first()
        if not token_obj: #認證失敗拋錯,被源碼中的try捕獲
            raise exceptions.AuthenticationFailed("驗證失敗!")
        return token_obj.user, token_obj.token #需要返回一個元組

在views.py:

def get_random_str(user):
    import hashlib, time
    ctime = str(time.time())

    md5 = hashlib.md5(bytes(user, encoding="utf8"))
    md5.update(bytes(ctime, encoding="utf8"))

    return md5.hexdigest()

from django.http import JsonResponse

class loginView(APIView):
    authentication_classes = [TokenAuth]

    def post(self, request):
        res = {"code": 1000, "msg": None}

        user = request.data.get("user")
        pwd = request.data.get("pwd")

        user_obj = User.objects.filter(name=user, pwd=pwd).first()
        if not user_obj:
            res["code"] = 1001
            res["msg"] = "用戶名或密碼錯誤"

        else:
            token = get_random_str(user)
            Token.objects.update_or_create(user=user_obj, defaults={"token": token})
            res["token"] = token

        return JsonResponse(res, json_dumps_params={"ensure_ascii": False})

源碼解析

我們知道在APIView類中可以找到as_view,而此時的as_view又指向了父類View中的as_view,此時父類as_view又會return dispatch,因此我們在APIView類中找到dispatch方法,從這里開始看源碼的執(zhí)行過程。

class APIView(View):
    def dispatch(self, request, *args, **kwargs):
        self.initial(request, *args, **kwargs) #這一步就是在處理認證、權限、頻率

class APIView(View):        
    def initial(self, request, *args, **kwargs):
        self.perform_authentication(request) #認證組件
        self.check_permissions(request)      #權限組件
        self.check_throttles(request)        #訪問頻率組件

class APIView(View):
    def perform_authentication(self, request):
        request.user

這個request是Request類的實例化對象,因此我們要去Request下面去找user方法

class Request(object):
    @property
    def user(self):

        if not hasattr(self, '_user'):
           with wrap_attributeerrors():
                self._authenticate() #調用user過程其實就是在執(zhí)行這個方法
        return self._user

查看self._authenticate

class Request(object):
    def _authenticate(self):
        for authenticator in self.authenticators: #循環(huán)包含著一個個認證類實例的列表,此時就是一個[TokenAuth(),]
            try:
                user_auth_tuple = authenticator.authenticate(self) #將視圖中的authenticate返回結果賦值給user_auth_tuple,此時傳進去的self是Request類的實例化對象
            except exceptions.APIException: #驗證失敗拋錯
                self._not_authenticated()
                raise

            if user_auth_tuple is not None: #如果不為空
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple #user_auth_tuple是個元祖,分成了兩個變量,這兩個變量可以為下面的權限組件所利用
                return #認證成功后返回

self.authenticators是什么?
往上走,發(fā)現(xiàn)構建request時傳進來的參數(shù)

class APIView(View):
    def initialize_request(self, request, *args, **kwargs):
        ......

        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(), #在這里
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

點進去看看,發(fā)現(xiàn)就是self.authentication_classes循環(huán)的結果

def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        return [auth() for auth in self.authentication_classes] #列表解析式,循環(huán)的是我們自己在視圖中寫的一個個認證類

因此可見,self.authenticators就是包含著一個個認證類實例對象的列表

authenticator.authenticate(self)是什么意思?
我們再回到_authenticate方法中看看這句話

authenticator.authenticate(self)

實例化對象調自己的方法是不需要傳self的,因此這是個形參,我要知道這個self是誰

那么這個self是誰?
要往上一級一級找,上一級是_authenticate(self),誰調用的?
找到user(self),誰調用的user(self)?

class APIView(View):
    def perform_authentication(self, request):
        request.user

request.user調的user,因此self就是這個新構建的request,這個request是Request類的實例化對象

GET訪問時加上數(shù)據(jù)庫中已有的一個token就能通過驗證
http://127.0.0.1:8000/books/?token=1a54a64ee1111738c5d8b7b5487e801b

全局視圖認證組件

如果我們自己不設authentication_classes,那么就會去父類APIView中找,里面有這么一段代碼

authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

api_settings是APISettings類的一個實例化對象

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

api_settings.DEFAULT_AUTHENTICATION_CLASSES會去找settings.py中的REST_FRAMEWORK

因此我們自己在settings.py設置這個REST_FRAMEWORK就可以

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.TokenAuth"]
}

["app01.utils.TokenAuth"]這個值是具體路徑,也可以是元祖

如果某個視圖(比如Login)不希望它經過全局認證,那么可以在視圖類中添加一個
authentication_classes = []即可

另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。

標題名稱:DjangoREST_framework框架02-創(chuàng)新互聯(lián)
新聞來源:http://muchs.cn/article42/pdjec.html

成都網站建設公司_創(chuàng)新互聯(lián),為您提供網站策劃、Google移動網站建設、服務器托管、網站建設、定制開發(fā)

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

商城網站建設