django restframework

  django restframework是基於django和restful協議開發的框架html

  在restful協議裏,一切皆是資源,操做是經過請求方式控制前端

  在開始前,你須要對CBV和FBV兩種架構模式有個大概的瞭解,django restframework用的就是CBV架構,這裏提供一篇博客供你欣賞CBVpython

  安裝:pip install djangorestframework數據庫

 

django原生request

  首先看到下面這段代碼django

def post(self, request):
    print(request.body)
    print(request.POST)

   請求數據通常封裝在請求頭中,而上面打印的數據,都是通過處理後的數據,那背後是怎麼進行封裝的呢?json

  對於get請求,直接去url後面的數據後端

  而對於post請求api

request.body: a=1&b=2
request.POST:
    if contentType:urlencoded: a=1&b=2  ---> {"a":1,"b":2}

   當發urlencoded數據時,兩個都能打印數據,可是若是就發json數據,就只有request.body裏有了restful

 

  request源碼剖析

  怎麼看源碼呢?打印下request的類型就能夠了,print(type(request)), 查看打印的WSGIRequest就能夠了閉包

from django.core.handlers.wsgi import WSGIRequest

   去它下面找POST,發現這麼一句

    POST = property(_get_post, _set_post)

   在_get_post方法中,進入這個方法self._load_post_and_files(),在它裏面就這麼一段代碼

        elif self.content_type == 'application/x-www-form-urlencoded':
            #若是urlencoded類型才把self.body賦給了self._post
            self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
        else:
            self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()

 

restframework request

  對於restframework的request,咱們須要瞭解下在restframework裏請求流程,它和django大體相同,由於它的APIView繼承是django的View,但在APiView中重寫了dispatch方法

  看到這段代碼

url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"),

   執行PublishViewSet就是APIView的as_view方法

class APIView(View):

   APIView繼承了View,APIView中有as_view方法,因此會執行這個方法,方法中有這麼一句代碼

view = super(APIView, cls).as_view(**initkwargs)

   最終仍是執行了父類裏的as_view方法,因此最終執行結果,獲得這麼這個view函數

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)

   當請求來時,會執行view函數,把dispatch結果返回,而這裏dispatch方法則不是View裏的,而是APIView的,由於APIView重寫了這個方法,而django restframework的精髓就所有在這裏邊了

    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        #構建一個新的request,把舊的request封裝給新的request的_request字段
        #執行.data會執行新request類中的data屬性,而在self._load_data_and_files()裏
        '''
        if not _hasattr(self, '_data'):
            self._data, self._files = self._parse()
            if self._files:
                self._full_data = self._data.copy()
                self._full_data.update(self._files)
            else:
                self._full_data = self._data
        '''
        #最終返回self._full_data,而上面就是對請求內容進行解析並封裝
        request = self.initialize_request(request, *args, **kwargs)
        '''
        #post
        print("request.data", request.data)
        print("request.data type", type(request.data))
        #get
        print(request._request.GET)
        print(request.GET)
        '''
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            #獲取get post等方法後執行,這裏用是新的request
            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

  在dispatch方法,經過請求方式映射,獲取到咱們寫好的請求方法,並執行

 

序列化

  restframework在先後端傳輸數據時,主要是json數據,過程當中就要須要把其餘數據轉換成json數據,好比數據庫查詢全部數據時,是queryset對象,那就要把這對象處理成json數據返回前端

models

from django.db import models

# Create your models here.

class Book(models.Model):
    title=models.CharField(max_length=32)
    price=models.IntegerField()
    pub_date=models.DateField()
    publish=models.ForeignKey("Publish", on_delete=True)
    authors=models.ManyToManyField("Author")
    def __str__(self):
        return self.title

class Publish(models.Model):
    name=models.CharField(max_length=32)
    email=models.EmailField()
    def __str__(self):
        return self.name

class Author(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    def __str__(self):
        return self.name

  

  那麼這裏提供三種序列化的方式:

  1. 對查詢的數據類型進行基礎數據類型的強轉,好比list(queryset對象.values('name', 'sex')), 單個數據對象 model_to_dict(obj)
  2. django提供的serialize方法,data=serializers.serialize("json",book_list)
  3. restframework提供的serialize方法,但事先要定義序列化模型,BookSerializers(book_list,many=True),而且使用restframework提供Response返回數據(須要注意的是,你必如今app裏註冊了rest_framework)
from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from .models import *

# Create your views here.

from rest_framework import serializers
from rest_framework.response import Response

class AuthorSerializers(serializers.Serializer):
    name = serializers.CharField(max_length=32)
    age = serializers.IntegerField()


class AuthorView(APIView):

    def get(self, request, *args, **kwargs):
        authors = Author.objects.all()
        # 方式1
        # data = authors.values("name","age")

        # from django.forms.models import model_to_dict
        # data = []
        # for obj in authors:
        #     data.append(model_to_dict(obj))

        #方式2
        # from django.core import serializers
        # data = serializers.serialize("json", authors)
        # return HttpResponse(data)

        #方式3
        author_ser = AuthorSerializers(authors, many=True)
        # return Response(author_ser.data)

  

  上面也只是說了下單表序列化,若是表中涉及到一對多,多對多怎麼操做呢?

  1. 一對多,經過source="publish.name"指定字段
  2. 多對多,經過get_字段名鉤子函數來定義要獲取的內容
from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from .models import *

# Create your views here.

from rest_framework import serializers
from rest_framework.response import Response

class BookSerializers(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()
    pub_date = serializers.DateField()

    # 一對多
    # publish = serializers.CharField()  #不加source時,默認給的是Publish模型定義__str__返回的字段
    publish = serializers.CharField(source="publish.name")

    # 多對多
    # authors = serializers.CharField(source="authors.all")  #獲取是一個queryset對象  字符串
    authors = serializers.SerializerMethodField()  #經過鉤子函數自定製須要的信息
    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append({'name':author.name, 'email':author.age})
        return temp

class BookView(APIView):

    def get(self, request, *args, **kwargs):
        books = Book.objects.all()
        bs = BookSerializers(books, many=True)
        return Response(bs.data)

  

  固然上面的過程,定義serializers模型,針對表定義每一個字段,有些繁瑣,因此序列化模型也有相似於ModelForm用法,不過一對多,多對多都是默認值,取得都是對應對象的id

  若是你想定製多對多和一對多,在ModelSerializers重寫這類型字段,可是須要注意的是,裏面提供的create方法不支持source定製,因此你還須要重寫create方法

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from .models import *

# Create your views here.

from rest_framework import serializers
from rest_framework.response import Response

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"

    #source指定字段,不影響get查看序列化,可是影響post建立數據,因此須要你重寫create方法
    publish = serializers.CharField(source='publish.name')
    authors = serializers.SerializerMethodField()  #經過鉤子函數自定製須要的信息
    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append({'name':author.name, 'email':author.age})
        return temp

    def create(self, validated_data):
        # author_list = validated_data.pop("authors")
        obj = Book.objects.create(**validated_data)
        # obj.authors.add(*author_list)
        return obj


class BookView(APIView):

    def get(self, request, *args, **kwargs):
        books = Book.objects.all()
        bs = BookSerializers(books, many=True)
        return Response(bs.data)

    def post(self, request, *args, **kwargs):
        bs=BookSerializers(data=request.data, many=False)
        if bs.is_valid():
            print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

  序列化超連接:HyperlinkedIdentityField序列化字段,指定三個參數 view_name url別名lookup_field填入連接裏的值對應字段, lookup_url_kwarg url裏對應的形參名

class BookSerializers(serializers.ModelSerializer):
      publish= serializers.HyperlinkedIdentityField(
                     view_name='publish_detail',
                     lookup_field="publish_id",
                     lookup_url_kwarg="pk")
      class Meta:
          model=Book
          fields="__all__"
          #depth=1
urlpatterns = [
    url(r'^books/$', views.BookViewSet.as_view(),name="book_list"),
    url(r'^books/(?P<pk>\d+)$', views.BookDetailViewSet.as_view(),name="book_detail"),
    url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"),
    url(r'^publishers/(?P<pk>\d+)$', views.PublishDetailViewSet.as_view(),name="publish_detail"),
]

 

  對於重寫create方法情景的總結(Django 1.10.1):

  1.序列化類繼承serializers.Serializer,須要重寫create,update方法,看源碼這兩個方法只是定義了拋錯

class AuthorSerializers(serializers.Serializer):
    name = serializers.CharField(max_length=32)
    age = serializers.IntegerField()

    def create(self, validated_data):
        obj = Author.objects.create(**validated_data)
        return obj

  2.表對象爲單表,序列化類繼承serializers.ModelSerializer,不須要重寫create,update方法,源碼裏就在這個類下實現了這兩個方法,另外這兩個方法是在序列化類下,因此它跟你使用哪一個視圖類無關,好比使用generics.ListCreateAPIView

class PublishSerializers(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = "__all__"

  3.表對象爲關聯表,序列化類繼承serializers.ModelSerializer,不存在定製某個字段,不須要重寫create,update方法

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book #book表關聯表,有一對多,多對多
        fields = "__all__"

  4.表對象爲關聯表,序列化類繼承serializers.ModelSerializer,source指定某個字段,提示指定字段要發實例(一對多),存在的疑問就是怎麼在前端添加實例?

  多對多,若是經過SerializerMethodField生成,序列化時,不提供多對多字段傳過來的值,等同這種情景下,多對多隻是隻讀顯示,不用來post添加操做,若是要操做把添加對象和多對多添加分紅兩步操做

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book #book表關聯表,有一對多,多對多
        fields = "__all__"

    authors = serializers.SerializerMethodField()
    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append({'name':author.name, 'email':author.age})
        return temp

    publish = serializers.CharField(source="publish.email")

    def create(self, validated_data):
        # authors = validated_data.pop("authors")
        obj = Book.objects.create(**validated_data)
        # obj.authors.add(*authors)
        return obj

  

視圖

  第一階段 老老實實的幹

  下面視圖代碼,能夠說是規規矩矩的作法,每一個視圖類下,都寫各個請求方法,細心的你確定發現了,每一個視圖的同類請求方法實現過程處理序列化模型和查詢的表不一樣,其餘的都同樣,代碼重複

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from django.shortcuts import HttpResponse
from django.core import serializers


from rest_framework import serializers


class BookSerializers(serializers.ModelSerializer):
      class Meta:
          model=Book
          fields="__all__"
          #depth=1


class PublshSerializers(serializers.ModelSerializer):

      class Meta:
          model=Publish
          fields="__all__"
          depth=1


class BookViewSet(APIView):

    def get(self,request,*args,**kwargs):
        book_list=Book.objects.all()
        bs=BookSerializers(book_list,many=True,context={'request': request})
        return Response(bs.data)


    def post(self,request,*args,**kwargs):
        print(request.data)
        bs=BookSerializers(data=request.data,many=False)
        if bs.is_valid():
            print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class BookDetailViewSet(APIView):

    def get(self,request,pk):
        book_obj=Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,context={'request': request})
        return Response(bs.data)

    def put(self,request,pk):
        book_obj=Book.objects.filter(pk=pk).first()
        bs=BookSerializers(book_obj,data=request.data,context={'request': request})
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class PublishViewSet(APIView):

    def get(self,request,*args,**kwargs):
        publish_list=Publish.objects.all()
        bs=PublshSerializers(publish_list,many=True,context={'request': request})
        return Response(bs.data)


    def post(self,request,*args,**kwargs):

        bs=PublshSerializers(data=request.data,many=False)
        if bs.is_valid():
            # print(bs.validated_data)
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class PublishDetailViewSet(APIView):

    def get(self,request,pk):

        publish_obj=Publish.objects.filter(pk=pk).first()
        bs=PublshSerializers(publish_obj,context={'request': request})
        return Response(bs.data)

    def put(self,request,pk):
        publish_obj=Publish.objects.filter(pk=pk).first()
        bs=PublshSerializers(publish_obj,data=request.data,context={'request': request})
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

  

  第二階段 mixin封裝類編寫

  restframework已經對咱們使用的這些請求方法,封裝到一些類裏面,咱們只要繼承這些類,並調用特定的方法,把結果返回便可

  至因而哪一個序列化模型和哪一個模型的數據,經過靜態字段queryset和serializer_class指定

  而GenericAPIView繼承了APIView,因此在程序啓動時,執行的仍是APIView的as_view方法

代碼簡化以下

from rest_framework import mixins
from rest_framework import generics

class BookViewSet(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):

    queryset = Book.objects.all()
    serializer_class = BookSerializers

    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 BookDetailViewSet(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers

    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)

   咱們大概能夠看下這些類封裝方法的實現過程,其中源碼中的方法能夠在GenericAPIView類找到

    def list(self, request, *args, **kwargs):
        #self.get_queryset()獲取靜態字段指定數據
        #filter_queryset支持配置文件篩選數據
        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)
        #get_serializer獲取靜態字段中指定的序列化類,並實例對象
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

   

  第三階段  通用的基於類

  固然上面這個實現過程,在定義請求方法,仍是有點重複,rest框架提供了一組已經混合好(minxed-in)的通用視圖進一步封裝

  generics.ListCreateAPIView 查看多條和添加視圖

  generics.RetrieveUpdateDestroyAPIView  查看單條,修改和刪除視圖

from rest_framework import mixins
from rest_framework import generics

class BookViewSet(generics.ListCreateAPIView):

    queryset = Book.objects.all()
    serializer_class = BookSerializers

class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers

class PublishViewSet(generics.ListCreateAPIView):

    queryset = Publish.objects.all()
    serializer_class = PublshSerializers

class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublshSerializers

   點進去一個類,發現它其實就是經過多繼承組合了第二階段中的類

class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):
    """
    Concrete view for listing a queryset or creating a model instance.
    """
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

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

   

  第四階段 viewsets.ModelViewSet

  上面的代碼已經夠簡潔了吧,它還能夠簡潔,你能夠觀察一下,就是在對全部的書操做和單本書操做,他們使用的數據和序列化模型的同樣的,rest框架還作到這方面的簡化,他們兩本質的區別在於操做全部書是get,post方法,而單本就是get,put,delete方法,實現原理是在url上對請求方法進行映射

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


from rest_framework.viewsets import ModelViewSet

class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializers

   而ModelViewSet實現過程也是多繼承

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):

    pass

   那咱們再看下,最後這種方式在請求到來時,是怎麼執行的,從多繼承中看,找as_view函數

  最終仍是會找到ViewSetMixin下的as_view執行,把請求映射關係傳給了actions

def as_view(cls, actions=None, **initkwargs):

   傳入到view函數中,閉包

def view(request, *args, **kwargs):
    self = cls(**initkwargs)
    # We also store the mapping of request methods to actions,
    # so that we can later set the action attribute.
    # eg. `self.action = 'list'` on an incoming GET request.
    self.action_map = actions

    # Bind methods to actions
    # This is the bit that's different to a standard view
    for method, action in actions.items():
        handler = getattr(self, action)
        setattr(self, method, handler)

    if hasattr(self, 'get') and not hasattr(self, 'head'):
        self.head = self.get

    self.request = request
    self.args = args
    self.kwargs = kwargs

    # And continue as usual
    return self.dispatch(request, *args, **kwargs)

  當請求來時,執行閉包的view函數,在下列這段代碼進行請求方法的映射

    for method, action in actions.items():
        #method = get,post
        #action=list,create
        handler = getattr(self, action)
        #handler==self.list,self.create函數
        setattr(self, method, handler)  #self.get-->self.list,  self.post-->self.create

  執行dispatch下這段代碼時

            if request.method.lower() in self.http_method_names:
                #get請求時,執行self.list....
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)

 

認證組件

  認證前提是須要登陸的,因此你還須要寫好登陸

models

class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)\

    def __str__(self):
        return self.name

class Token(models.Model):
    user = models.OneToOneField("User")
    token = models.CharField(max_length=128)

    def __str__(self):
        return self.token

 登陸視圖

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 .models import User

class LoginView(APIView):

    def post(self,request):

        name=request.data.get("name")
        pwd=request.data.get("pwd")
        user=User.objects.filter(name=name,pwd=pwd).first()
        res = {"state_code": 1000, "msg": None}
        if user:

            random_str=get_random_str(user.name)
            token=Token.objects.update_or_create(user=user,defaults={"token":random_str})
            res["token"]=random_str
        else:
            res["state_code"]=1001 #錯誤狀態碼
            res["msg"] = "用戶名或者密碼錯誤"

        import json
        return Response(json.dumps(res,ensure_ascii=False))

 

  首先你必須明確的是,認證,權限,頻率那都是請求到來時的操做,大概都能猜到會在分發dispatch下執行,那咱們就找到APIView下dispatch看下,下面這幾句代碼

  你會發現,在進入到認證..這些操做的時候,request已是從新封裝後的新的request

        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            #認證 權限 頻率都在這裏面作了
            self.initial(request, *args, **kwargs)

  在initial方法下,有這三句代碼,分別對應認證,權限,頻率組件

        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)

  那咱們就先看認證組件幹了些什麼事,一旦理解認證組件,其餘的兩個就好理解了,實現方式是相似的

  在perform_authentication認證方法裏,就一句代碼,你可能驚呼,這啥啊,就這麼一句?給人感受是字段啊,但深究下,若是是字段確定實現不了認證功能,你須要大膽猜想是靜態方法,是否是?咱們去這個request下找下就知道了,注意:這裏request是新的request

    def perform_authentication(self, request):
        """
        Perform authentication on the incoming request.

        Note that if you override this and simply 'pass', then authentication
        will instead be performed lazily, the first time either
        `request.user` or `request.auth` is accessed.
        """
        request.user

   也就是這個返回的request

request = self.initialize_request(request, *args, **kwargs)

    而它返回的就是這個request對象

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

   去它下面還真找到user靜態方法

    @property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user

   最後它會執行_authenticate方法

    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        for authenticator in self.authenticators:
            try:
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

        self._not_authenticated()

   要想搞明白上面代碼,就必需要知道self.authenticators是啥?

  在__init__方法裏,有這麼句,而authenticators在實例化對象傳下來的

        self.authenticators = authenticators or ()

   實例化對象時,傳入是下面這玩意,那它又幹了啥呢?

authenticators=self.get_authenticators(),

   下面self就是咱們定義的視圖類,它會去咱們定義的類下面找authentication_classes,循環並實例化

    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        return [auth() for auth in self.authentication_classes]

   authentication_classes又是些啥了?從名字上看,就是認證類,因此這個是由你來定義的,可是若是咱們不定義的呢?

  當前類沒有,就會找父類去中,一直找到APIView下

    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

   會去api_settings下取個DEFAULT_AUTHENTICATION_CLASSES默認的認證類

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

   None,DEFAULTS對應APISettings下__init__方法的user_settings和defaults

  而DEFAULTS則是settings配置文件的變量,並配置這個,默認狀況下就是這些認證類

    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ),

   api_settings.DEFAULT_AUTHENTICATION_CLASSES會去執行APISettings的__getattr__方法

    def __getattr__(self, attr):
        if attr not in self.defaults:
            raise AttributeError("Invalid API setting: '%s'" % attr)

        try:
            # Check if present in user settings
            #self.user_settings這裏調用時靜態方法
            #attr=DEFAULT_AUTHENTICATION_CLASSES
            '''
            @property
            def user_settings(self):
                #user_settings爲None,因此這裏hasattr(self, '_user_settings')爲False
                if not hasattr(self, '_user_settings'):
                    #嘗試去settings配置文件找REST_FRAMEWORK這麼個配置
                    self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
                return self._user_settings
            '''
            #若是在settings找REST_FRAMEWORK配置就返回配置,沒找到就是{}
            #沒配置空字典獲取DEFAULT_AUTHENTICATION_CLASSES就報錯,走異常分支
            val = self.user_settings[attr]
        except KeyError:
            # Fall back to defaults
            '''
            去默認配置文件取到這兩項
            'DEFAULT_AUTHENTICATION_CLASSES': (
                'rest_framework.authentication.SessionAuthentication',
                'rest_framework.authentication.BasicAuthentication'
            ),
            '''
            val = self.defaults[attr]

        # Coerce import strings into classes
        if attr in self.import_strings:
            val = perform_import(val, attr)

        # Cache the result
        self._cached_attrs.add(attr)
        setattr(self, attr, val)
        return val

   因此咱們能夠得出

    若是想局部配置認證類,在咱們定義視圖類下經過authentication_classes指定(列表)

    若是想全局在settings下配置

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

   回到request下user屬性方法下的_authenticate方法

    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        for authenticator in self.authenticators:
            #authenticator  認證類實例
            try:
                #調用認證類實例下的authenticate方法,傳入了self,也就是request
                #因此你認證類要有authenticate方法,返回值爲元組
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                #元組內容 用戶信息  和 auth信息,咱們用的token
                self.user, self.auth = user_auth_tuple
                return

 認證類

from rest_framework.authentication import BaseAuthentication
from rest_framework.authentication import exceptions

class Authentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.GET.get("token")
        token_obj = Token.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed("驗證失敗")
        else:
            return token_obj.user.name, token_obj.token

 視圖類

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 app01.service.auth import *

from django.http import JsonResponse
class LoginViewSet(APIView):
    authentication_classes = [Authentication,]   #通常狀況下登陸頁面是不須要認證的,你能夠把這裏改爲空列表
    def post(self,request,*args,**kwargs):
        res={"code":1000,"msg":None}
        try:
            user=request._request.POST.get("user")
            pwd=request._request.POST.get("pwd")
            user_obj=UserInfo.objects.filter(user=user,pwd=pwd).first()
            print(user,pwd,user_obj)
            if not user_obj:
                res["code"]=1001
                res["msg"]="用戶名或者密碼錯誤"
            else:
                token=get_random_str(user)
                UserToken.objects.update_or_create(user=user_obj,defaults={"token":token})
                res["token"]=token

        except Exception as e:
            res["code"]=1002
            res["msg"]=e

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

 

權限組件

  瞭解認證組件的源碼後,下面這兩個組件就不帶看了,直接看怎麼用吧

  權限類裏重寫has_permission方法

from rest_framework.permissions import BasePermission
class SVIPPermission(BasePermission):
    message="SVIP才能訪問!"
    def has_permission(self, request, view):
        if request.user.user_type==3:
            return True
        return False

   視圖類裏經過permission_classe指定權限類

from app01.service.permissions import *

class BookViewSet(generics.ListCreateAPIView):
    permission_classes = [SVIPPermission,]
    queryset = Book.objects.all()
    serializer_class = BookSerializers

   全局配置

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",]
}

 

訪問頻率組件

  依據什麼來斷定它的訪問頻率,IP?訪問相關的信息你均可以從request.META中獲取

  頻率類裏定義allow_request方法

from rest_framework.throttling import BaseThrottle

class VisitThrottle(BaseThrottle):
    '''
    一分鐘內訪問超過5次  禁用兩分鐘
    '''

    VISIT_RECORD = {}

    def allow_request(self, request, view):
        remote_addr = request.META.get('REMOTE_ADDR')
        print(remote_addr)
        now_time = time.time()

        if remote_addr not in VisitThrottle.VISIT_RECORD:
            VisitThrottle.VISIT_RECORD[remote_addr] = {
                'start_time' : [now_time,],
                'forbid' : False
            }
            return True

        start_time = VisitThrottle.VISIT_RECORD[remote_addr]['start_time']
        forbid_state = VisitThrottle.VISIT_RECORD[remote_addr]['forbid']
        while start_time and start_time[-1] < now_time - 60 and not forbid_state:
            start_time.pop()

        if forbid_state:
            if now_time - start_time[-1] > 120:
                print('兩分鐘過了,解禁')
                VisitThrottle.VISIT_RECORD[remote_addr] = {
                    'start_time': [now_time, ],
                    'forbid': False
                }
                print(VisitThrottle.VISIT_RECORD[remote_addr])
                return True
            else:
                print("兩分鐘內禁止訪問,已通過了%s秒"%(now_time - start_time[-1]))
                return False
        else:
            if len(start_time) < 5:
                print("訪問%s次"%(len(start_time) + 1))
                print(start_time)
                start_time.insert(0, now_time)
                return True
            else:
                print("訪問%s次,禁止訪問"%len(start_time))
                VisitThrottle.VISIT_RECORD[remote_addr] = {
                    'start_time': [now_time, ],
                    'forbid': True
                }
                return False

 

   視圖類中經過throttle_classes指定頻率類

from app01.service.throttles import *

class BookViewSet(generics.ListCreateAPIView):
    throttle_classes = [VisitThrottle,]
    queryset = Book.objects.all()
    serializer_class = BookSerializers

   全局配置

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
    "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",]
}

 內置玩法

class VisitThrottle(SimpleRateThrottle):

    scope="visit_rate"
    def get_cache_key(self, request, view):

        return self.get_ident(request)

 settings

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
    "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
    "DEFAULT_THROTTLE_RATES":{
        "visit_rate":"5/m",
    }
}

 

 解析器和響應器

  上面已經對django和restframework的request解析過程進行分析過了,解析器主要對請求體裏的信息進行解析

局部視圖

from rest_framework.parsers import JSONParser,FormParser
class PublishViewSet(generics.ListCreateAPIView):
    parser_classes = [FormParser,JSONParser]
    queryset = Publish.objects.all()
    serializer_class = PublshSerializers
    def post(self, request, *args, **kwargs):
        print("request.data",request.data)
        return self.create(request, *args, **kwargs)

 全局視圖

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
    "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
    "DEFAULT_THROTTLE_RATES":{
        "visit_rate":"5/m",
    },
    "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.FormParser',]
}

   響應器主要是Response返回的內容,它會幫你生成展現數據的頁面,頁面支持你定義好的請求方法,若是你不想看它給頁面,也能夠經過?format=json查看

 

分頁組件

  分兩種狀況,一種是原生get,另一種就是鉤子映射到list方法,因此你想若是想進行分頁,能夠這麼作

  重寫get或list方法

from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination

  def get(self,request):
        book_list=Book.objects.all()
        pp=PageNumberPagination()
        pager_books=pp.paginate_queryset(queryset=book_list,request=request,view=self)
        print(pager_books)
        bs=BookSerializers(pager_books,many=True)

        #return Response(bs.data)
        return pp.get_paginated_response(bs.data)

  固然上面這個過程是沒有給一頁顯示多少的,並且分頁內部沒有默認值,它會去settings下取PAGE_SIZE,因此你好配置這個

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

   固然這個是全局的,若是你想局部,你能夠這麼幹,自定義分頁類,下面實例過程就換成你定義這個類

pp=PageNumberPagination()

   自定義分頁類

class PNPagination(PageNumberPagination):
        page_size = 1
        page_query_param = 'page'  #url分頁參數名
        page_size_query_param = "size"  #url每頁數參數名
        max_page_size = 5  #當?page=1&size=6 size超過5時就不生效

 

  針對鉤子映射的,還能夠這麼作,經過pagination_class指定分頁器,固然指定能夠不用重寫list方法了,由於在list方法的源碼裏,會自動獲取這個指定的分液器

class AuthorModelView(viewsets.ModelViewSet):
    pagination_class = PNPagination
    authentication_classes = [TokenAuth,]
    permission_classes = []
    throttle_classes = []# 限制某個IP每分鐘訪問次數不能超過20次
    queryset = Author.objects.all()
    serializer_class = AuthorModelSerializers

 

  對LimitOffsetPagination偏移分頁,自定義類時  default_limit指定限制多少,url裏   offset參數指定偏移量

 

 

 

restframe

相關文章
相關標籤/搜索