drf源碼系列

過濾器

對查詢出來的數據進行篩選可寫可不寫javascript

from rest_framework.filters import BaseFilterBackend
源碼
'''
    def filter_queryset(self, request, queryset, view):
    #繼承了這個類必須寫這個方法否則報錯
        raise NotImplementedError(".filter_queryset() must be overridden.")
'''
view是當前視圖self
queryset是篩選以後的數據
request 就是request
def filter_queryset(self,request,queryset,view):
    pass

使用方法

對查詢出來的數據進行篩選可寫可不寫
#第一部分
from rest_framework.filters import BaseFilterBackend
#繼承類
class MyFilterBack(BaseFilterBackend):
def filter_queryset(self,request,queryset,view):
    val = request.query_params.get('cagetory')
        return queryset.filter(category_id=val)

#先實例化一個
class Indexview(APIview):
    def get(self,request,*arg,**kwargs):
        #就是查詢一個表不理他
        queryset=models.News.objects.all()
        #實例化一個類對象
        obj=MyFilterBack()
 #傳值順序request,queryset,self
result=obj.filter_queryset(request,queryset,self)
        return Response('...')

視圖類

F查詢

def get(self,request,*args,**kwargs):
        # 1.獲取單挑數據再作序列化
        result = super().get(request,*args,**kwargs)
        # 2.對瀏覽器進行自加1
        pk = kwargs.get('pk')
        models.Article.objects.filter(pk=pk).update(read_count=F('read_count')+1)
        # 3.對瀏覽器進行自加1
        # instance = self.get_object()
        # instance.read_count += 1
        # instance.save()
        return result

apiview

提供了公共方法
request.data 是request.post的封裝 json序列化
dispatch()分發
self封裝了request屬性
self.request.method

源碼剖析

@classmethod
    def as_view(cls, **initkwargs):
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation
        #執行父類的as_view方法
        view = super().as_view(**initkwargs)
        #執行父類的super().as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs
        #閉包 csrf_exempt免除csrftoken的認證
        return csrf_exempt(view)
    
    
    
--------------------------------------    
    
    @classonlymethod
    def as_view(cls, **initkwargs):
        """
        Main entry point for a request-response process.
        """
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        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
            #執行本類的self.dispatch
            return self.dispatch(request, *args,**kwargs) 
        #執行view
        return view
  
-------------apiview的dispatch--------
    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 = self.initialize_request(request, *args, **kwargs)
        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

            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

GenericAPIView

#一 初識
from rest_framework.generics import GenericAPIView
GenericAPIview繼承了APIview
class NewView(GenericAPIview):
    querset=model.News.objects.all()
    def get(self,request,*arg,**kwargs):
        #self對應NewView的對象
        self.filter_queryset(self.queryset)
1.執行對應的filter_queryset方法
2.本類沒有去尋找父類GenericAPIview
'''
    def filter_queryset(self, queryset):
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

'''#因此默認返回原來的queryset
3.尋找filter_backends
'''
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
    filter_backends是空的
'''

1. 本類重寫filter_backends

from rest_framework.generics import GenericAPIView
#GenericAPIview繼承了APIview
class NewFiltrBackend(BaseFilterBackend):
    def filter_queryset(self,request,queryset,view):
        val = request.query_params.get('cagetory')
        return queryset.filter(category_id=val)
    class NewView(GenericAPIview):
        querset=model.News.objects.all()
        filter_backends=[NewFiltrBackend ,]
        def get(self,request,*arg,**kwargs):
            #self對應NewView的對象
            v=self.get_queryset()
          queryset=self.filter_queryset(v)
 1.執行對應的filter_queryset方法
2.本類沒有去尋找父類GenericAPIview
'''
源碼 繼承類不寫self.queryset會報錯
assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )
'''

'''
    def filter_queryset(self, queryset):
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

'''4.backend等於對應NewFiltrBackend類名() 實例化對象
··  執行NewFiltrBackend裏面的filter_queryset方法
3.尋找filter_backends,本類的filter_backends
    #filter_backends=[NewFiltrBackend ,]
5.queryset=self.filter_queryset(self.queryset)是篩選以後的結果
#v=self.get_queryset()
queryset=self.filter_queryset(v)
6.尋找對應get_queryset
'''
源碼
 queryset = self.queryset
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
        return queryset
        
'''#返回等於篩選以後的queryset
    #queryset=self.filter_queryset(queryset)
#queryset=get_queryset()
·········self.get_serializer()··············· 
class Newserializers(serializers.ModelSerializer):
    class Meta:
        model=models.News
        fields="__all__"
class NewFiltrBackend(BaseFilterBackend):
    def filter_queryset(self,request,queryset,view):
        val = request.query_params.get('cagetory')
        return queryset.filter(category_id=val)
    class NewView(GenericAPIview):
        querset=model.News.objects.all()
        filter_backends=[NewFiltrBackend ,]
        def get(self,request,*arg,**kwargs):
            #self對應NewView的對象
            v=self.get_queryset()
            queryset=self.filter_queryset(v)
            self.get_serializer()#本類沒有去父類找
            #代替了                                    ser=Newserializers(instance=queryset,many=True)
            #ser.data
            
  1.尋找get_serializer()
2.
'''
源碼
    def get_serializer(self, *args, **kwargs):
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)
'''
3.進行尋找get_serializer_class()本類沒有去父類找
#serializer_class = self.get_serializer_class()
4.
'''
    def get_serializer_class(self):
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class
''' #return self.serializer_class 返回serializer_class
#get_serializer()=serializer_class
在本類定義seializer_class
seializer_class=Newserializers

至關於                                     #ser=Newserializers(instance=queryset,many=True)
#ser.data
ser=self.get_serializer(instance=queryset,many=True)
ser.data
​``````````````````````````````
分頁
querset=model.News.objects.all()
pagination_class =PageNumberPagination
self.paginate_queryset(queryset)
'''
源碼
self.paginate_queryset()

self._paginator = self.pagination_class()
須要定義
    def paginator(self):
        if not hasattr(self, '_paginator'):
            if self.pagination_class is None:
                self._paginator = None
            else:
                self._paginator = self.pagination_class()
        return self._paginator
        
'''pagination_class()須要本地定義
pagination_class =PageNumberPagination

GenericAPIView源碼總結

源碼的剖析
    def get_queryset(self):
        """
        Get the list of items for this view.
        This must be an iterable, and may be a queryset.
        Defaults to using `self.queryset`.

        This method should always be used rather than accessing `self.queryset`
        directly, as `self.queryset` gets evaluated only once, and those results
        are cached for all subsequent requests.

        You may want to override this if you need to provide different
        querysets depending on the incoming request.

        (Eg. return a list of items that is specific to the user)
        """
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )

        queryset = self.queryset
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
        return queryset

    def get_object(self):
        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.

        You may want to override this if you need to provide different
        serializations depending on the incoming request.

        (Eg. admins get full serialization, others get basic serialization)
        """
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }

    def filter_queryset(self, queryset):
        """
        Given a queryset, filter it with whichever filter backend is in use.

        You are unlikely to want to override this method, although you may need
        to call it either from a list view, or from a custom `get_object`
        method if you want to apply the configured filtering backend to the
        default queryset.
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset
源碼解釋
from rest_framework.generics import GenericAPIView
from rest_framework.filters import BaseFilterBackend
from rest_framework import serializers
GenericAPIview繼承了APIview
class Newserializers(serializers.ModelSerializer):
    class Meta:
        model=models.News
        fields="__all__"
class NewFiltrBackend(BaseFilterBackend):
    def filter_queryset(self,request,queryset,view):
        val = request.query_params.get('cagetory')
        return queryset.filter(category_id=val)
    class NewView(GenericAPIview):
        querset=model.News.objects.all()
        filter_backends=[NewFiltrBackend ,]
        seializer_class=Newserializers
        def get(self,request,*arg,**kwargs):
            #self對應NewView的對象
            v=self.get_queryset()
            queryset=self.filter_queryset(v)
     self.get_serializer(instance==queryset,many=True)
     retutn Response(ser.data)
 '''
        1.querset=model.News.objects.all()
        2.filter_backends=[NewFiltrBackend ,]
        3.seializer_class=Newserializers
        4.pagination_class =PageNumberPagination
 1.查詢   
 self.get_queryset()#等於querset=model.News.objects.all()
 2.序列化
 self.get_serializer()等於seializer_class=Newserializers()
 3.篩選
 self.filter_queryset()
 等於filter_backends=[NewFiltrBackend ,]
 內部有一個for循環列表 同等與NewFiltrBackend()
 4.分頁
 self.paginate_queryset(queryset)
 等於page_obj=PageNumberPagination()
 '''

基於GenericAPIViewcss

listapiview查看

基於GenericAPIViewhtml

模擬get請求

1.queryset

2.分頁


#settings的配置
paginator.paginate_queryset(queryset, self.request, view=self)
"DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
    
"PAGE_SIZE":2,

3.序列化

4.返回序列化數據

源碼剖析

#源碼
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
1.執行list
     def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
#self.get_queryset()執行自定的
#queryset=model.News.objects.all()
#self.filter_queryset()篩選執行
#filter_backends=[NewFiltrBackend ,]
#執行對應本類的filter_queryset
       2. page = self.paginate_queryset(queryset)
    #分頁
    #執行 pagination_class =PageNumberPagination
        if page is not None:
          3.serializer = self.get_serializer(page, many=True)
        #序列化 執行seializer_class=Newserializers
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
    #最後返回序列化的數據

RetrieveAPIView單條查看

模擬get請求前端

執行流程

1. #對應得到對象 本身要定義單條數據 queryset
2.    #序列化
3.     #返回數據

源碼剖析

class RetrieveAPIView(mixins.RetrieveModelMixin,
                      GenericAPIView):  
    #從左往右繼承
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)
    #去找retrieve這個方法
class RetrieveModelMixin:
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        #對應得到對象
        instance = self.get_object()
        #序列化
        serializer = self.get_serializer(instance)
        #返回數據
        return Response(serializer.data)
    
    執行
    
     def get_object(self):
        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        return obj

基於GenericAPIViewjava

createapiview建立

基於GenericAPIViewpython

封裝了post請求jquery

執行流程

1.序列化
2.序列化驗證
3.保存數據

源碼剖析

def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)
    
    1.執行create
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        #序列化
        serializer.is_valid(raise_exception=True)
        #序列化驗證
        self.perform_create(serializer)
        '''
        保存數據
         def perform_create(self, serializer):
        serializer.save()
        '''
           
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

示例使用

#第一種寫法重寫create書寫 多條序列化器
    def create(self, request, *args, **kwargs):

        article = articleseralizer(data=request.data)
        articleDetail = atricleDetaliser(data=request.data)
        if article.is_valid() and articleDetail.is_valid():
            article_obj = article.save(author=request.user)
            print(">>>>>>>>>", request.user, type(request.user))
            articleDetail.save(article=article_obj)
            return Response('修改爲功')
        else:
            return Response(f"{article.errors},{articleDetail.errors}")
        
        
#第二種方式
重寫序列化器分發
def get_serializer_class(self):
    self.request.method=="POST":
    return 對應序列化器
    self.request.method=="GET":
    return 對應序列化器
def perform_create(self,serializer):
    Aritlel=serializer.save(author)
    serializer_second=AricleSerializer(data=request.data)
    serializer.save(aritle=Aritlel)
    字段對象=對象

UpdateAPIView全局局部

封裝了局部更新和全局更新web

基於GenericAPIViewajax

執行流程

1.獲取更新那條數據的對象
2.序列化
2.序列化校驗
3.校驗成功更新

源碼剖析

def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)
    '''
     def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        '''
        #執行queryset查詢處理的model對象
#至關於
#queryset=model.表名.object.filter(pk=pk).frist()
'''
        serializer = self.get_serializer(instance, data=request.data, partial=partial)默認不爲Ture
        #執行對應的序列化
        serializer.is_valid(raise_exception=True)
        #序列化驗證
        self.perform_update(serializer)
     '''
        # def perform_update(self, serializer):
            #serializer.save()
        
    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)
    '''
        def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)
    '''
執行
 '''
     def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        '''
        #執行queryset查詢處理的model對象
#至關於
#queryset=model.表名.object.filter(pk=pk).frist()
'''
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        kwargs['partial'] = True 局部更新
        #執行對應的序列化
        serializer.is_valid(raise_exception=True)
        #序列化驗證
        self.perform_update(serializer)
     '''
        # def perform_update(self, serializer):
            #serializer.save()

刪除DestroyAPIView

執行流程

1.傳入對象  
2.執行def perform_destroy(self, instance):
3.刪除

源碼剖析

def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)
        def destroy(self, request, *args, **kwargs):
            instance = self.get_object()
            #c傳入對象刪除
            self.perform_destroy(instance)
            return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()#刪除

Viewset視圖類

是APIview視圖類的繼承對象算法

createapiview
UpdateAPIview。。。。等等

使用方法

1.直接繼承便可
2.由於沒有封裝對應get post等請求方法 一點點去找父類的方法
3.GenericViewSet(ViewSetMixin)它繼承的ViewSetMixin
4.重寫了asview()須要傳參
5.使用兩個類進行id和沒有id的區分
url(r'^article/$',article.AtricleView.as_view({"get":"list"}))
url(r'^article/(?P<pk>\d+)/$',article.AtricleView.as_view({"get":"retrieve"}))

知識點

from rest_framework.viewsets import GenericVIewSet
class GenericViewSet(ViewSetMixin,generics.GenericAPIView
                    ):
默認繼承GenericAPIView
    def get_serializer_class(self):
        pk = self.kwargs.get('pk')
        if pk:
            return ArticleDetailSerializer
        return ArticleSerializer

ListModelMixin添加

源碼

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)

RetrieveModelMixin單條查詢

源碼

def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

UpdateModelMixin更新

源碼

def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

        #
    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)
    class

DestroyModelMixin:

刪除

源碼

class DestroyModelMixin:
"""
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

版本

setting配置

default_version = api_settings.DEFAULT_VERSION
    #默認版本
    allowed_versions = api_settings.ALLOWED_VERSIONS
    #指定版本
    version_param = api_settings.VERSION_PARAM
    #url版本傳輸關鍵字
      #指定版本類類
 "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",

知識點

dispath 老的request 封裝了不少功能

request.version#版本
 scheme#版本對象
#執行返回一個版本元組 第一個是版本第二個是版本對象
 def determine_version(self, request, *args, **kwargs):
        """
        If versioning is being used, then determine any API version for the
        incoming request. Returns a two-tuple of (version, versioning_scheme)
        """
        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class()
        return (scheme.determine_version(request, *args, **kwargs), scheme) 
        request.version, request.versioning_scheme = version, scheme

源碼執行流程

class APIView(View):
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
    
    def dispatch(self, request, *args, **kwargs):
       
        # ###################### 第一步 ###########################
        """
        request,是django的request,它的內部有:request.GET/request.POST/request.method
        args,kwargs是在路由中匹配到的參數,如:
            url(r'^order/(\d+)/(?P<version>\w+)/$', views.OrderView.as_view()),
            http://www.xxx.com/order/1/v2/
        """
        self.args = args
        self.kwargs = kwargs

        """
        request = 生成了一個新的request對象,此對象的內部封裝了一些值。
        request = Request(request)
            - 內部封裝了 _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)

            執行視圖函數。。
    
    def initial(self, request, *args, **kwargs):
       
        # ############### 2.1 處理drf的版本 ##############
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
        ...
        
    def determine_version(self, request, *args, **kwargs):
        if self.versioning_class is None:
            return (None, None)
        #是版本類的實例化對象
        scheme = self.versioning_class() 
        # obj = XXXXXXXXXXXX()
        return (scheme.determine_version(request, *args, **kwargs), scheme)
        
class OrderView(APIView):
    versioning_class = URLPathVersioning
    def get(self,request,*args,**kwargs):
        print(request.version)
        print(request.versioning_scheme)
        return Response('...')

    def post(self,request,*args,**kwargs):
        return Response('post')

1.局部配置

視圖寫法

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.versioning import URLPathVersioning


class OrderView(APIView):

    versioning_class = URLPathVersioning
    def get(self,request,*args,**kwargs):
        print(request.version)
        print(request.versioning_scheme)
        return Response('...')

    def post(self,request,*args,**kwargs):
        return Response('post')

setting配置

REST_FRAMEWORK = {
    "ALLOWED_VERSIONS":['v1','v2'],
    'VERSION_PARAM':'version'
 }

2.全局配置

不用手動一個個加所有都配置上

url

url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),

url(r'^(?P<version>\w+)/users/$', users_list, name='users-list'),

settings配置

REST_FRAMEWORK = { 
 指定版本類 必需要寫要些路徑   "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
    #限制版本範圍
    "ALLOWED_VERSIONS":['v1','v2'],
    #指定url有名分組的關鍵字
    'VERSION_PARAM':'version'
}

認證

自定義認證token
把數據庫的user拿出來比較
若是想使用**request.data 就要把他變爲字典 因此前端要返回json數據類型
源碼寫法
#配置
 authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

局部配置

和登錄配合使用
import uuid
from django.shortcuts import render
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from rest_framework.versioning import URLPathVersioning
from rest_framework.views import APIView
from rest_framework.response import Response
class Loginview(APIView):
    versioning_class = None
    #認證寫法
    authentication_classes = [Myauthentication, ]

def post(self,request,*args,**kwargs):
        user_object = models.UserInfo.objects.filter(**request.data).first()
        if not user_object:
            return Response('登陸失敗')
        random_string = str(uuid.uuid4())
        user_object.token = random_string
        user_object.save()
        return Response(random_string)

class MyAuthentication:
    def authenticate(self, request):
        """
        Authenticate the request and return a two-tuple of (user, token).
        """
        token = request.query_params.get('token')
        user_object = models.UserInfo.objects.filter(token=token).first()
        if user_object:
            return (user_object,token)
        return (None,None)

class OrderView(APIView):
    authentication_classes = [MyAuthentication, ]
    def get(self,request,*args,**kwargs):
        print(request.user)
        print(request.auth)
        return Response('order')

class UserView(APIView):
    authentication_classes = [MyAuthentication,]
    def get(self,request,*args,**kwargs):
        print(request.user)
        print(request.auth)
        return Response('user')

    
    
    
···············手寫認證類····························

class Myauthentication:
    # 認證
    # versioning_class = None
    #手寫認證
    def authenticate(self, request):
        token = request.query_params.get('token')
        print(token)
        user_object = models.UserInfo.objects.filter(token=token).first()
        print(user_object)
        if user_object:
            print(1)
            #必須返回一個元組的形式
            #返回對應的user_object和token值
            
            return (user_object, token)
        else:
            print(2)
        return (None, None)
    
    
    
    
    
----------------------
    爲何要返回元組
----------------------
    #爲何要返回元組
  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
                #給user和auth進行賦值
                self.user, self.auth = user_auth_tuple#必須返回一個元組的形式
                return#結束函數

        self._not_authenticated()

    def _not_authenticated(self):
        """
        Set authenticator, user & authtoken representing an unauthenticated request.

        Defaults are None, AnonymousUser & None.
        """
        self._authenticator = None
        
        if api_settings.UNAUTHENTICATED_USER:
            #user 沒有設置返回none
            self.user = 
            #匿名用戶
            api_settings.UNAUTHENTICATED_USER()
        else:
            self.user = None
          
        if api_settings.UNAUTHENTICATED_TOKEN:
            self.auth =
            #對應author沒有設置返回none
            api_settings.UNAUTHENTICATED_TOKEN()
        else:
            self.auth = None

全局配置

DEFAULT_AUTHENTICATION_CLASSES=['寫一個認證的類手寫的']
class Myauthentication:
    # 認證
    # versioning_class = None
    #手寫認證
    def authenticate(self, request):
        token = request.query_params.get('token')
        print(token)
        user_object = models.UserInfo.objects.filter(token=token).first()
        print(user_object)
        if user_object:
            print(1)
            #必須返回一個元組的形式
            #返回對應的user_object和token值
            
            return (user_object, token)
        else:
            print(2)
        return (None, None)
        '''
          # raise Exception(), 不在繼續往下執行,直接返回給用戶。
        # return None ,本次認證完成,執行下一個認證
        # return ('x',"x"),認證成功,不須要再繼續執行其餘認證了,繼續日後權限、節流、視圖函數
        '''

view視圖寫法

class LoginView(APIView):
    authentication_classes = []
    def post(self,request,*args,**kwargs):
        user_object = models.UserInfo.objects.filter(**request.data).first()
        if not user_object:
            return Response('登陸失敗')
        random_string = str(uuid.uuid4())
        user_object.token = random_string
        user_object.save()
        return Response(random_string)

class OrderView(APIView):
    # authentication_classes = [TokenAuthentication, ]
    def get(self,request,*args,**kwargs):
        print(request.user)
        print(request.auth)
        if request.user:
            return Response('order')
        return Response('滾')

class UserView(APIView):
    同上

源碼執行流程

'''
    當請求發過來的實話會先執行 apiview裏面的dispath方法
   1.執行認證的dispatch的方法
    2.dispatch方法會執行initialize_request方法封裝舊的request對象
 
    3.在封裝的過程當中會執行
    get_authenticators(self): 並實例化列表中的每個認證類
    若是本身寫的類有對應的認證類 會把認證類的實例化對象封裝到新的request當中
    繼續執行initial 
    會執行認證裏面的perform_authentication 執行request.user 
    會執行循環每個對象執行對應的authenticate方法
    會有三種返回值
     # raise Exception(), 不在繼續往下執行,直接返回給用戶。
        # return None ,本次認證完成,執行下一個認證
        # return ('x',"x"),認證成功,不須要再繼續執行其餘認證了,繼續日後權限、節流、視圖函數
    
    
    
    裏面會執行四件事
    版本
    認證
    權限
    節流
'''
    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 = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
    循環自定義的認證
    #request封裝舊的request執行initialize_request
    
        def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    
    
    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        
        return [auth() for auth in self.authentication_classes]

        def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
    
    #執行完四件事在執行視圖

權限

message=消息 自定義錯誤消息
能夠自定義錯誤信息
'''
 for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
'''

本質上自定義和
return Flase
單條驗證
驗證兩次 
不寫url_kwarg默認封裝識別pk關鍵字

重寫錯誤信息的兩種方式

第一種

message={"code":"恭喜你報錯了"}

第二種

from rest_framework.permissions import BasePermission
from rest_framework import exceptions

class MyPermission(BasePermission):
    #第一種
    message = {'code': 10001, 'error': '你沒權限'}
    def has_permission(self, request, view):
        #request.user執行認證
        if request.user:
            return True
    #自定義錯誤信息
         raise exceptions.PermissionDenied({'code': 10001, 'error': '你沒權限'})
        return False

    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return False
    
    
#對應源碼
至關於重寫了錯誤信息
#執行順序 
dispatch---》intital--》check_permissions--》permission_denied
    def permission_denied(self, request, message=None):
        """
        If request is not permitted, determine what kind of exception to raise.
        """
        if request.authenticators and not request.successful_authenticator:
            raise exceptions.NotAuthenticated()
        raise 
        #等於message 或者自定義錯誤函數
        #raise exceptions.PermissionDenied({'code': 10001, 'error': '你沒權限'})
        exceptions.PermissionDenied(detail=message)
        
        
 ················源碼··················
  def check_permissions(self, request):
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )

源碼剖析

執行流程
apiview
---
dispatch
---
initial
---
執行權限判斷
若是本身寫了權限類 會先循環權限類並實例化存在列表當中
而後循環列表對象,若是權限判斷返回不爲Ture機會進行主動拋出異常
能夠自定義錯誤信息
具體代碼
class APIView(View):
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    
    def dispatch(self, request, *args, **kwargs):
        封裝request對象
        self.initial(request, *args, **kwargs)
        經過反射執行視圖中的方法

    def initial(self, request, *args, **kwargs):
        版本的處理
        # 認證
        self.perform_authentication(request)
        
        # 權限判斷
        self.check_permissions(request)
        
        
        self.check_throttles(request)
        #執行認證
    def perform_authentication(self, request):
        request.user
    
    def check_permissions(self, request):
        # [對象,對象,]
        self.執行
        '''
def get_permissions(self):
        return [permission() for permission in self.permission_classes]
        '''
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(request, message=getattr(permission, 'message', None))
    def permission_denied(self, request, message=None):
        if request.authenticators and not request.successful_authenticator:
            raise exceptions.NotAuthenticated()
        raise exceptions.PermissionDenied(detail=message)
        
    
# 
   
class UserView(APIView):
    permission_classes = [MyPermission, ]
    
    def get(self,request,*args**kwargs):
        return Response('user')

使用

class MyPermission(BasePermission):
    message = {'code': 10001, 'error': '你沒權限'}
    def has_permission(self, request, view):
        #request.user執行認證
        if request.user:
            return True

         raise exceptions.PermissionDenied({'code': 10001, 'error': '你沒權限'})
        return False

    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return False
    
    
 class UserView(APIView):
    先執行上面的數據
    permission_classes = [MyPermission, ]
    #再執行
    def get(self,request,*args**kwargs):
        return Response('user')

源碼流程

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 = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            #走這
            self.initial(request, *args, **kwargs)
​`````````````````def  initial``````````````````````````
            

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = 
        '''
        第三步
        '''
        self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
        
        
        
---------------------------------------        
# 第二步
  def check_permissions(self, request):
        # [對象,對象,]
        self.執行
        '''
def get_permissions(self):
        return [permission() for permission in self.permission_classes]
        '''
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(request, message=getattr(permission, 'message', None))
    def permission_denied(self, request, message=None):
        if request.authenticators and not request.successful_authenticator:
            raise exceptions.NotAuthenticated()
        raise exceptions.PermissionDenied(detail=message)

第三步

def determine_version(self, request, *args, **kwargs):
        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class()
        return (scheme.determine_version(request, *args, **kwargs), scheme)

單挑數據的驗證

單挑數據

def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return False
#在執行RetrieveAPIView 會執行單挑數據的驗證

    def get_object(self):
        
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        #不寫url_kwarg默認封裝識別pk關鍵字
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        #檢驗單挑數據的權限
        self.check_object_permissions(self.request, obj)

        return obj
    
···················check_object_permissions··········
    def check_object_permissions(self, request, obj):
        """
        Check if the request should be permitted for a given object.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions():
            if not permission.has_object_permission(request, self, obj):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
                
                
執行has_object_permission
    def has_object_permission(self, request, view, obj):
        return True

版本認證權限執行流程

1.首先全部方法都執行父類的apiview方法
2.執行dispatch方法

3.進行
    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]
4.
intiail request.user

1573465834465

若是代碼中出現了CSRF使用裝飾器便可

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator


def csrf_exempt(view_func):
    """
    Marks a view function as being exempt from the CSRF view protection.
    """
    # We could just do view_func.csrf_exempt = True, but decorators
    # are nicer if they don't have side-effects, so we return a new
    # function.
    def wrapped_view(*args, **kwargs):
        return view_func(*args, **kwargs)
    wrapped_view.csrf_exempt = True
    return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)

drf訪問頻率的限制

- 匿名用戶,用IP做爲用戶惟一標記,但若是用戶換代理IP,沒法作到真正的限制。
- 登陸用戶,用用戶名或用戶ID作標識。能夠作到真正的限制

知識點

當前這個字典存放在django的緩存中
{
    throttle_anon_1.1.1.1:[100121340,],
    1.1.1.2:[100121251,100120450,]
}


限制:60s能訪問3次
來訪問時:
    1.獲取當前時間 100121280
    2.100121280-60 = 100121220,小於100121220全部記錄刪除所有剔除
    3.判斷1分鐘之內已經訪問多少次了? 4 
        判斷訪問次數
    4.沒法訪問
停一會
來訪問時:
    1.獲取當前時間 100121340
    2.100121340-60 = 100121280,小於100121280全部記錄刪除
    3.判斷1分鐘之內已經訪問多少次了? 0
    4.能夠訪問

執行流程

在視圖類中配置 throttle_classes= [] 這是一個列表
實現 allow_request 方法 wait 是一個提示方法
返回 True/False。
True 能夠繼續訪問,
False 表示限制
全局配置 
DEFAULT_THROTTLE_CLASSE,節流限制類
DEFAULT_THROTTLE_RATES 表示頻率限制,好比 10/m 表示 每分鐘 10 次
源碼在 initial 中實現 check_throttles 方法


在進行節流以前已經作了版本認證權限
#dispatch分發    
    def initial(self, request, *args, **kwargs):
        #版本
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        #認證
        self.perform_authentication(request)
        #權限
        self.check_permissions(request)
        #頻率限制
        self.check_throttles(request)
1.先找allow_request本類沒有去父類找 同dispatch
2.執行對應的rate獲取對應scope 節流的頻率
2.key是獲取ip值 
3.history是獲取ip地址 獲取對應的訪問時間記錄
'''
self.history = self.cache.get(self.key, [])
'''
4.對應的訪問記錄等於ture獲取到值,
把當前的時間減去一個本身設定的時間戳
'''
while self.history and self.history[-1] <= self.now - self.duration:
#條件知足 把最後一個值刪除
#再去循環判斷
            self.history.pop()
'''
5.而後判斷訪問次數有沒有大於本身設定的值
'''
    if len(self.history) >= self.num_requests:
            知足條件走這
            return self.throttle_failure()
    不知足條件走這
    return self.throttle_success()
'''
 6.成功   def throttle_success(self):
        """
        Inserts the current request's timestamp along with the key
        into the cache.
        """
        self.history.insert(0, self.now)
        self.cache.set(self.key, self.history, self.duration)
        return True
    6.失敗
        def throttle_failure(self):
        """
        Called when a request to the API has failed due to throttling.
        """
        return False#不能夠訪問
    7若是返回false進入等待時間
        def wait(self):
        """
        Returns the recommended next request time in seconds.
        """
        if self.history:
            remaining_duration = self.duration - (self.now - self.history[-1])
        else:
            remaining_duration = self.duration

        available_requests = self.num_requests - len(self.history) + 1
        if available_requests <= 0:
            return None

        return remaining_duration / float(available_requests)

源碼

#dispatch分發    
    def initial(self, request, *args, **kwargs):
        #版本
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        #認證
        self.perform_authentication(request)
        #權限
        self.check_permissions(request)
        #頻率限制
        self.check_throttles(request)

    def check_throttles(self, request):
        throttle_durations = []
        for throttle in self.get_throttles():
            #找到當前類的allow_request
            if not throttle.allow_request(request, self):
                throttle_durations.append(throttle.wait())

        if throttle_durations:
            durations = [
                duration for duration in throttle_durations
                if duration is not None
            ]

            duration = max(durations, default=None)
            self.throttled(request, duration)

#執行類的allow_request
    def allow_request(self, request, view):
        """
        Implement the check to see if the request should be throttled.

        On success calls `throttle_success`.
        On failure calls `throttle_failure`.
        """
        if self.rate is None:
            return True
        獲取
        '''
            def __init__(self):
        if not getattr(self, 'rate', None):
            self.rate = self.get_rate()
              self.num_requests, self.duration = self.parse_rate(self.rate)
            當前這個類有一個獲取rate的方法
        '''
        
        #執行get_rate
        讀取settings設置的配置文件
        '''
                def get_rate(self):
    #若是不設置會進行報錯
        try:
        #設置了就進行鍵取值
            return self.THROTTLE_RATES[self.scope]
        except KeyError:
            msg = "No default throttle rate set for '%s' scope" % self.scope
            raise ImproperlyConfigured(msg)
        '''
#經過鍵去取值/進行分割獲取值
        '''
            def parse_rate(self, rate):
        if rate is None:
            return (None, None)
        num, period = rate.split('/')
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        return (num_requests, duration)
        '''
    #這步繼續往下執行繼承類的get_cache_key
        self.key = self.get_cache_key(request, view)
        
    #匿名
    #繼承了class AnonRateThrottle(SimpleRateThrottle):
        '''
               
    scope = 'anon'
    def get_cache_key(self, request, view):
        if request.user.is_authenticated:
            return None  # Only throttle unauthenticated requests.

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }
}
#返回字符串格式化
throttle_(匿名)anon_(ip地址的拼接)1.1.1.1:[100121340,],
        '''
 #根據用戶進行判斷 重寫get_cache_key
'''

'''
        
        if self.key is None:
            return True
        
        self.history = self.cache.get(self.key, [])
        self.now = self.timer()

        # Drop any requests from the history which have now passed the
        # throttle duration
        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop()
            #元組返回的值num_requests
        if len(self.history) >= self.num_requests:
            #不能夠訪問
            return self.throttle_failure()
        #能夠訪問
        return self.throttle_success()
    '''
     6.成功 def throttle_success(self):
     #成功把訪問的當前時間插入history
        self.history.insert(0, self.now)
        self.cache.set(self.key, self.history, self.duration)
        return True
    6.失敗
        def throttle_failure(self):
        """
        Called when a request to the API has failed due to throttling.
        """
        return False#不能夠訪問
    
    '''

默認配置文件寫法settings

REST_FRAMEWORK = {    
    throttle_classes = [AnonRateThrottle,]
    "DEFAULT_THROTTLE_RATES":{"anon":"3/m"}
    #這樣寫的緣由 源碼
    #經過匿名        
    scope = 'anon'
    def get_cache_key(self, request, view):
        if request.user.is_authenticated:
            return None  # Only throttle unauthenticated requests.

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }
}

#獲取全局設置的步驟
 def __init__(self):
        if not getattr(self, 'rate', None):
            #第一步
      
            self.rate = self.get_rate()
        '''
        以設置的鍵進行取值獲取時間
    def get_rate(self):
若是不設置就進行報錯
 try:
            return self.THROTTLE_RATES[self.scope]
        except KeyError:
            msg = "No default throttle rate set for '%s' scope" % self.scope
            raise ImproperlyConfigured(msg)
        '''
            
        self.num_requests, self.duration = self.parse_rate(self.rate)
     #後面設置值格式
       #例 "DEFAULT_THROTTLE_RATES":{"anon":"3/m"}
    以/分割 前面是限制的次數 後面的是訪問限制的時間
    
'''
 def parse_rate(self, rate):
        if rate is None:
            return (None, None)
        num, period = rate.split('/')
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        return (num_requests, duration)

'''
以後執行對應字典的取值
key=throttle_(匿名)anon_(ip地址的拼接)1.1.1.1:
[100121340,],#值
self.key = self.get_cache_key(request, view)
        if self.key is None:
            return True

        self.history = self.cache.get(self.key, [])
        self.now = self.timer()

        # Drop any requests from the history which have now passed the
        # throttle duration
        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop()
        if len(self.history) >= self.num_requests:
            return self.throttle_failure()
        return self.throttle_success()

根據匿名用戶和id進行判斷

全局配置

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'api.utils.throttles.throttles.LuffyAnonRateThrottle',
        'api.utils.throttles.throttles.LuffyUserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        #不重寫的默認走這
        'anon': '10/day',   
        'user': '10/day',
        'luffy_anon': '10/m',
        'luffy_user': '20/m',
    },
}

settings

url寫法

from django.conf.urls import url, include
from web.views.s3_throttling import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

settings寫法局部

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    'DEFAULT_THROTTLE_RATES': {
        'luffy_anon': '10/m',
        'luffy_user': '20/m',
    },
}

settings.py

根據匿名ip或者user 進行判斷

視圖寫法

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response

from rest_framework.throttling import SimpleRateThrottle


class LuffyAnonRateThrottle(SimpleRateThrottle):
    """
    匿名用戶,根據IP進行限制
    """
    scope = "luffy_anon"

    def get_cache_key(self, request, view):
        # 用戶已登陸,則跳過 匿名頻率限制
        if request.user:
            return None

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }


class LuffyUserRateThrottle(SimpleRateThrottle):
    """
    登陸用戶,根據用戶token限制
    """
    #重寫scope
    scope = "luffy_user"

    def get_ident(self, request):
        """
        認證成功時:request.user是用戶對象;request.auth是token對象
        :param request: 
        :return: 
        """
        # return request.auth.token
        return "user_token"

    def get_cache_key(self, request, view):
        """
        獲取緩存key
        :param request: 
        :param view: 
        :return: 
        """
        # 未登陸用戶,則跳過 Token限制
        if not request.user:
            return None

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }


class TestView(APIView):
    throttle_classes = [LuffyUserRateThrottle, LuffyAnonRateThrottle, ]

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET請求,響應內容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')

    
    
    
    
​```````````````````````````````````````````    
#源碼
class AnonRateThrottle(SimpleRateThrottle):
    scope = 'anon'
    def get_cache_key(self, request, view):
        if request.user.is_authenticated:
            return None  
        # Only throttle unauthenticated requests.

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }
    
#
def get_cache_key(self, request, view):
        return self.get_ident(request)#獲取對應的ip值

settings

# settings.py
    'DEFAULT_THROTTLE_RATES': {
      'Vistor': '3/m',
      'User': '10/m'
    },

drf總結

django'中能夠免除csrftoken的認證

from django.views.decorators.csrf import csrf_exempt 
from django.shortcuts import HttpResponse
@csrf_exempt def index(request):    
    return HttpResponse('...')
# index = csrf_exempt(index)
urlpatterns = [    
    url(r'^index/$',index), 
              ]

drf中view進行csrftoken的認證

urlpatterns = [    url(r'^login/$',account.LoginView.as_view()), 
              ]
class APIView(View):    
    @classmethod    
    def as_view(cls, **initkwargs):        
        view = super().as_view(**initkwargs)     
        view.cls = cls        
        view.initkwargs = initkwargs
        # Note: session based authentication is explicitly CSRF validated,        
        # all other authentication is CSRF exempt.        
        return csrf_exempt(view)

1.寫視圖的方法

  • 第一種:原始APIView

    url(r'^login/$',account.LoginView.as_view()),
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework_jwt.settings import api_settings
    from rest_framework.throttling import AnonRateThrottle
    from api import models
    
    
    class LoginView(APIView):
        authentication_classes = []
        def post(self,request,*args,**kwargs):
            # 1.根據用戶名和密碼檢測用戶是否能夠登陸
            user = models.UserInfo.objects.filter(username=request.data.get('username'),password=request.data.get('password')).first()
            if not user:
                return Response({'code':10001,'error':'用戶名或密碼錯誤'})
    
            # 2. 根據user對象生成payload(中間值的數據)
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            payload = jwt_payload_handler(user)
    
            # 3. 構造前面數據,base64加密;中間數據base64加密;前兩段拼接而後作hs256加密(加鹽),再作base64加密。生成token
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            token = jwt_encode_handler(payload)
            return Response({'code': 10000, 'data': token})
  • 第二種:ListApiView等

    url(r'^article/$',article.ArticleView.as_view()),
    url(r'^article/(?P<pk>\d+)/$',article.ArticleDetailView.as_view()),
    from rest_framework.throttling import AnonRateThrottle
    from rest_framework.response import Response
    from rest_framework.generics import ListAPIView,RetrieveAPIView
    from api import models
    from api.serializer.article import ArticleSerializer,ArticleDetailSerializer
    
    class ArticleView(ListAPIView):
        authentication_classes = []
        # throttle_classes = [AnonRateThrottle,]
    
        queryset = models.Article.objects.all()
        serializer_class = ArticleSerializer
    
    class ArticleDetailView(RetrieveAPIView):
        authentication_classes = []
        queryset = models.Article.objects.all()
        serializer_class = ArticleDetailSerializer
  • 第三種:

    url(r'^article/$',article.ArticleView.as_view({"get":'list','post':'create'})),
        url(r'^article/(?P<pk>\d+)/$',article.ArticleView.as_view({'get':'retrieve','put':'update','patch':'partial_update','delete':'destroy'}))
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin
    from api.serializer.article import ArticleSerializer,ArticleDetailSerializer
    
    class ArticleView(GenericViewSet,ListModelMixin,RetrieveModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin):
        authentication_classes = []
        throttle_classes = [AnonRateThrottle,]
    
        queryset = models.Article.objects.all()
        serializer_class = None
    
        def get_serializer_class(self):
            pk = self.kwargs.get('pk')
            if pk:
                return ArticleDetailSerializer
            return ArticleSerializer

drf 相關知識點梳理

  1. 裝飾器

    def outer(func):
        def inner(*args,**kwargs):
            return func(*args,**kwargs)
        return inner
    
    @outer
    def index(a1):
        pass
    
    index()
    def outer(func):
        def inner(*args,**kwargs):
            return func(*args,**kwargs)
        return inner
    
    def index(a1):
        pass
    
    index = outer(index)
    
    index()
  2. django中能夠免除csrftoken認證

    from django.views.decorators.csrf import csrf_exempt
    from django.shortcuts import HttpResponse
    
    @csrf_exempt
    def index(request):
        return HttpResponse('...')
    
    # index = csrf_exempt(index)
    
    urlpatterns = [
        url(r'^index/$',index),
    ]
    urlpatterns = [
        url(r'^login/$',account.LoginView.as_view()),
    ]
    
    class APIView(View):
        @classmethod
        def as_view(cls, **initkwargs):
            view = super().as_view(**initkwargs)
            view.cls = cls
            view.initkwargs = initkwargs
    
            # Note: session based authentication is explicitly CSRF validated,
            # all other authentication is CSRF exempt.
            return csrf_exempt(view)
  3. 面向對象中基於繼承+異常處理來作的約束

    class BaseVersioning:
        def determine_version(self, request, *args, **kwargs):
            raise NotImplementedError("must be implemented")
    
    class URLPathVersioning(BaseVersioning):
     def determine_version(self, request, *args, **kwargs):
            version = kwargs.get(self.version_param, self.default_version)
            if version is None:
                version = self.default_version
    
            if not self.is_allowed_version(version):
                raise exceptions.NotFound(self.invalid_version_message)
            return version
  4. 面向對象封裝

    class Foo(object):
     def __init__(self,name,age):
         self.name = name
         self.age = age 
    
    obj = Foo('汪洋',18)
    class APIView(View):
        def dispatch(self, request, *args, **kwargs):
    
            self.args = args
            self.kwargs = kwargs
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
         ...
    
     def initialize_request(self, request, *args, **kwargs):
            """
            Returns the initial request object.
            """
            parser_context = self.get_parser_context(request)
    
            return Request(
                request,
                parsers=self.get_parsers(),
                authenticators=self.get_authenticators(), # [MyAuthentication(),]
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
  5. 面向對象繼承

    class View(object):
         pass
    
    class APIView(View):
        def dispatch(self):
            method = getattr(self,'get')
            method()
    
    class GenericAPIView(APIView):
        serilizer_class = None
    
        def get_seriliser_class(self):
            return self.serilizer_class
    
    class ListModelMixin(object):
        def get(self):
            ser_class = self.get_seriliser_class()
            print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
        pass
    
    class UserInfoView(ListAPIView):
        pass
    
    
    view = UserInfoView()
    view.dispatch()
    class View(object):
         pass
    
    class APIView(View):
        def dispatch(self):
            method = getattr(self,'get')
            method()
    
    class GenericAPIView(APIView):
        serilizer_class = None
    
        def get_seriliser_class(self):
            return self.serilizer_class
    
    class ListModelMixin(object):
        def get(self):
            ser_class = self.get_seriliser_class()
            print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
        pass
    
    class UserInfoView(ListAPIView):
        serilizer_class = "汪洋"
    
    
    view = UserInfoView()
    view.dispatch()
    class View(object):
         pass
    
    class APIView(View):
        def dispatch(self):
            method = getattr(self,'get')
            method()
    
    class GenericAPIView(APIView):
        serilizer_class = None
    
        def get_seriliser_class(self):
            return self.serilizer_class
    
    class ListModelMixin(object):
        def get(self):
            ser_class = self.get_seriliser_class()
            print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
        pass
    
    class UserInfoView(ListAPIView):
    
        def get_seriliser_class(self):
            return "咩咩"
    
    view = UserInfoView()
    view.dispatch()
  6. 反射

    class View(object):
     def dispatch(self, request, *args, **kwargs):
            # Try to dispatch to the right method; if a method doesn't exist,
            # defer to the error handler. Also defer to the error handler if the
            # request method isn't on the approved list.
            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
            return handler(request, *args, **kwargs)
  7. 發送ajax請求

    $.ajax({
     url:'地址',
     type:'GET',
     data:{...},
     success:function(arg){
         console.log(arg);
     }
    })
  8. 瀏覽器具備 "同源策略的限制",致使 發送ajax請求 + 跨域 存在沒法獲取數據。

    • 簡單請求,發送一次請求。
    • 複雜請求,先options請求作預檢,而後再發送真正請求
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>常鑫的網站</h1>
        <p>
            <input type="button" value="點我" onclick="sendMsg()">
        </p>
        <p>
            <input type="button" value="點他" onclick="sendRemoteMsg()">
        </p>
    
    
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <script>
            function sendMsg() {
                $.ajax({
                    url:'/msg/',
                    type:'GET',
                    success:function (arg) {
                        console.log(arg);
                    }
                })
            }
            function sendRemoteMsg() {
                $.ajax({
                    url:'http://127.0.0.1:8002/json/',
                    type:'GET',
                    success:function (arg) {
                        console.log(arg);
                    }
                })
    
            }
        </script>
    </body>
    </html>
  9. 如何解決ajax+跨域?

    CORS,跨站資源共享,本質:設置響應頭。
  10. 常見的Http請求方法

    get
    post
    put
    patch
    delete
    options
  11. http請求中Content-type請起頭

    狀況一:
        content-type:x-www-form-urlencode
        name=alex&age=19&xx=10
    
        request.POST和request.body中均有值。
    
    狀況二:
        content-type:application/json
        {"name":"ALex","Age":19}
    
        request.POST沒值
        request.body有值。
  12. django中F查詢

  13. django中獲取空Queryset

    models.User.object.all().none()
  14. 基於django的fbv和cbv都能實現遵循restful規範的接口

    def user(request):
        if request.metho == 'GET':
            pass
    
    
    class UserView(View):
        def get()...
    
        def post...
  15. 基於django rest framework框架實現restful api的開發。

    - 免除csrf認證
    - 視圖(APIView、ListAPIView、ListModelMinx)
    - 版本
    - 認證
    - 權限
    - 節流
    - 解析器
    - 篩選器
    - 分頁
    - 序列化
    - 渲染器
  16. 簡述drf中認證流程?

    1.用戶發來請求優先執行dispatch方法
    2.內部會封裝reqeustd1
  17. 簡述drf中節流的實現原理以及過程?匿名用戶/非匿名用戶 如何實現頻率限制?

  18. GenericAPIView視圖類的做用?

    他提供了一些規則,例如:
    
    class GenericAPIView(APIView):
        serializer_class = None
        queryset = None
        lookup_field = 'pk'
    
        filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
        pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
    
        def get_queryset(self):
            return self.queryset
    
        def get_serializer_class(self):
            return self.serializer_class
    
        def filter_queryset(self, queryset):
            for backend in list(self.filter_backends):
                queryset = backend().filter_queryset(self.request, queryset, self)
            return queryset
    
        @property
        def paginator(self):
            if not hasattr(self, '_paginator'):
                if self.pagination_class is None:
                    self._paginator = None
                else:
                    self._paginator = self.pagination_class()
            return self._paginator
    
    他至關於提供了一些規則,建議子類中使用固定的方式獲取數據,例如:
    class ArticleView(GenericAPIView):
        queryset = models.User.objects.all()
    
        def get(self,request,*args,**kwargs):
            query = self.get_queryset()
    
    咱們能夠本身繼承GenericAPIView來實現具體操做,可是通常不會,由於更加麻煩。
    而GenericAPIView主要是提供給drf內部的 ListAPIView、Create....
    class ListModelMixin:
        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)
    
    class ListAPIView(mixins.ListModelMixin,GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
    class MyView(ListAPIView):
        queryset = xxxx 
        ser...
    總結:GenericAPIView主要爲drf內部幫助咱們提供增刪改查的類LIstAPIView、CreateAPIView、UpdateAPIView、提供了執行流程和功能,咱們在使用drf內置類作CURD時,就能夠經過自定義 靜態字段(類變量)或重寫方法(get_queryset、get_serializer_class)來進行更高級的定製。
  19. jwt以及其優點。

    jwt先後端分離 用於用戶認證
    jwt的實現原理:
        -用戶登錄成功,會給前端返回一個tokon值。
        此token值只在前端保存
        token值分爲
        一段類型和算法信息
        第二段用戶信息和超時時間
        第三段前兩段數據拼接以後進行has256再次加密+base64url
  20. 序列化時many=True和many=False的區別?

  21. 應用DRF中的功能進行項目開發

    *****
        解析器:request.query_parmas/request.data
        視圖
        序列化
        渲染器:Response
    
    ****
        request對象封裝
        版本處理
        分頁處理
    ***
        認證
        權限
        節流
    • 基於APIView實現呼啦圈
    • 繼承ListAPIView+ GenericViewSet,ListModelMixin實現呼啦圈

跨域

  • 域相同,永遠不會存在跨域。
    • crm,非先後端分離,沒有跨域。
    • 路飛學城,先後端分離,沒有跨域(以前有,如今沒有)。
  • 域不一樣時,纔會存在跨域。
    • l拉勾網,先後端分離,存在跨域(設置響應頭解決跨域)
因爲瀏覽器具備同源策略的限制
對ajax請求的限制 javascript的src對應src不進行限制
同源同端口
不一樣源就是跨域
寫了/api 不跨域 api訪問django
api.xx.com跨域了訪問django

簡單請求

發一次請求

設置響應頭就能夠解決
from django.shortcuts import render,HttpResponse

def json(request):
    response = HttpResponse("JSONasdfasdf")
    response['Access-Control-Allow-Origin'] = "*"
    return response

複雜請求

預檢option

請求

html寫法

 

寫法

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
@csrf_exempt
def put_json(request):
    response = HttpResponse("JSON複雜請求")
    if request.method == 'OPTIONS':
        # 處理預檢
        response['Access-Control-Allow-Origin'] = "*"
        response['Access-Control-Allow-Methods'] = "PUT"
        return response
    elif request.method == "PUT":
        return response

解決跨域:CORS

本質在數據返回值設置響應頭

from django.shortcuts import render,HttpResponse

def json(request):
    response = HttpResponse("JSONasdfasdf")
    response['Access-Control-Allow-Origin'] = "*"
    return response

經過jsonp

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <p>
        <input type="button" onclick="Jsonp1();"  value='提交'/>
    </p>

    <p>
        <input type="button" onclick="Jsonp2();" value='提交'/>
    </p>

    <script type="text/javascript" src="jquery-1.12.4.js"></script>
    <script>
        function Jsonp1(){
            var tag = document.createElement('script');
            tag.src = "http://c2.com:8000/test/";
            document.head.appendChild(tag);
            document.head.removeChild(tag);

        }

        function Jsonp2(){
            $.ajax({
                url: "http://c2.com:8000/test/",
                type: 'GET',
                dataType: 'JSONP',
                success: function(data, statusText, xmlHttpRequest){
                    console.log(data);
                }
            })
        }


    </script>
</body>
</html>

總結

條件:
    一、請求方式:HEAD、GET、POST
    二、請求頭信息:
        Accept
        Accept-Language
        Content-Language
        Last-Event-ID
        Content-Type 對應的值是如下三個中的任意一個
                                application/x-www-form-urlencoded
                                multipart/form-data
                                text/plain
 
注意:同時知足以上兩個條件時,則是簡單請求,不然爲複雜請求

總結

  1. 因爲瀏覽器具備「同源策略」的限制,因此在瀏覽器上跨域發送Ajax請求時,會被瀏覽器阻止。
  2. 解決跨域
    • 不跨域
    • CORS(跨站資源共享,本質是設置響應頭來解決)。
      • 簡單請求:發送一次請求
      • 複雜請求:發送兩次請求

部署 collectstaic 收集靜態文件

jwt

img

用於在先後端分離時,實現用戶登陸相關。

1.知識點

1.2jwt代替token 進行優化

用戶登陸成功以後,生成一個隨機字符串,給前端。
    - 生成隨機字符串
        加密信息                            
        #{typ:"jwt","alg":'HS256'}   
        # 加密手段segments.append(base64url_encode(json_header)) 
        98qow39df0lj980945lkdjflo.
        #第二部分的信息 {id:1,username:'alx','exp':10}
        #加密手段segments.append(base64url_encode(payload))
        saueoja8979284sdfsdf.
        #兩個密文拼接加鹽
        asiuokjd978928374
        - 類型信息經過base64加密
        - 數據經過base64加密
        - 兩個密文拼接在h256加密+加鹽
    - 給前端返回token值只在前端
token是由。分割的三段組成
    - 第一段:類型和算法信息
    -   第二段。 用戶的信息和超時時間
     - 第三段:hs256(前兩段拼接)加密 + base64url 
         - 之後前端再次發來信息時       
            - 超時驗證        
            - token合法性校驗 

前端獲取隨機字符串以後,保留起來。
之後再來發送請求時,攜帶98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928375。


後端接收到以後
    1.取出第二部分進行時間的判斷
    2. 把前面兩個進行加密對第三個值進行加密
    - token只在前端保存,後端只負責校驗。    
    - 內部集成了超時時間,後端能夠根據時間進行校驗是否超時。       - 因爲內部存在hash256加密,因此用戶不能夠修改token,只要一修改就認證失敗。
    
    
    
    
通常在先後端分離時,用於作用戶認證(登陸)使用的技術。
jwt的實現原理:
    - 用戶登陸成功以後,會給前端返回一段token。
    - token是由.分割的三段組成。
        - 第一段:類型和算法信心
        - 第二段:用戶信息+超時時間
        - 第三段:hs256(前兩段拼接)加密 + base64url
    - 之後前端再次發來信息時
        - 超時驗證
        - token合法性校驗
優點:
    - token只在前端保存,後端只負責校驗。
    - 內部集成了超時時間,後端能夠根據時間進行校驗是否超時。
    - 因爲內部存在hash256加密,因此用戶不能夠修改token,只要一修改就認證失敗。

1.2token

用戶登錄成功以後,生成一個隨機字符串,本身保留一份,給前端返回一份。
之後前端再來發請求時,須要攜帶字符串
後端對字符串進行校驗

流程

1.安裝

pip3 install djangorestframework-jwt

2.註冊

INSTALLED_APPS = [    
    'django.contrib.admin',    
    'django.contrib.auth',    
    'django.contrib.contenttypes',    
    'django.contrib.sessions',   
    'django.contrib.messages',    
    'django.contrib.staticfiles',    
    'api.apps.ApiConfig',    
    'rest_framework',    
    'rest_framework_jwt']

3.源碼剖析和實現流程

from rest_framework_jwt.views import obtain_jwt_token

具體流程

用戶信息加密
jwt_payload_hander = api_settings.JWT_PAYLOAD_HANDLER
第三段信息的加密
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
#解密
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER


def jwt_payload_handler(user):
    username_field = get_username_field()
    username = get_username(user)

    warnings.warn(
        'The following fields will be removed in the future: '
        '`email` and `user_id`. ',
        DeprecationWarning
    )

    payload = {
        'user_id': user.pk,
        'username': username,
        'exp': datetime.utcnow() +
        #默認5分鐘
        api_settings.JWT_EXPIRATION_DELTA
        #若是有email會把email配置上
         if hasattr(user, 'email'):
        payload['email'] = user.email
    if isinstance(user.pk, uuid.UUID):
        #若是有user_id會把pk值配置上
        payload['user_id'] = str(user.pk)

    payload[username_field] = username
        
        
        #源碼
       settings= 'JWT_EXPIRATION_DELTA':datetime.timedelta(seconds=300)
 ,
       
    }

加密的具體實現流程

api_settings.JWT_ENCODE_HANDLER -->
    
    加密的具體實現
    
    def jwt_encode_handler(payload):
        #payload傳入
        key = api_settings.JWT_PRIVATE_KEY or jwt_get_secret_key(payload)
        #加鹽
        #SECRET_KEY = '+gr4bbq8e$yqbd%n_h)2(osz=bmk1x2+o6+w5g@a4r1#3%q1n*'
        return jwt.encode(
            payload,#類型信息頭信息
            key,#加鹽
            #默認封裝傳入了hs256
            api_settings.JWT_ALGORITHM
            # 'JWT_ALGORITHM': 'HS256',
            ).decode('utf-8')
    encode默認繼承父類的 
    父類的encode方法
    # Header
        header = {'typ': self.header_typ, 'alg': algorithm}
        #self.header_typ #-- 》   header_typ = 'JWT'
        signing_input = b'.'.join(segments)#把類型信息和數據用.的形式拼接到了一塊兒
         try:
            alg_obj = self._algorithms[algorithm]#執行算法
            key = alg_obj.prepare_key(key)
            signature = alg_obj.sign(signing_input, key)#把拼接起來的值進行二次加密 成爲第三個信息
            '''
        except KeyError:
            if not has_crypto and algorithm in requires_cryptography:
                raise NotImplementedError(
                    "Algorithm '%s' could not be found. Do you have cryptography "
                    "installed?" % algorithm
                )
            else:
                raise NotImplementedError('Algorithm not supported')
        '''
        segments.append(base64url_encode(signature))#再把第三個信息放入列表 對第三個信息進行base64進行加密

        return b'.'.join(segments)#用.的形式再把第三個數據拼接起來 進行返回
        98qow39df0lj980945lkdjflo.saueoja8979284sdfsdf.asiuokjd978928375

解密

將token分割成 header_segment、payload_segment、crypto_segment 三部分
對第一部分header_segment進行base64url解密,獲得header
對第二部分payload_segment進行base64url解密,獲得payload
對第三部分crypto_segment進行base64url解密,獲得signature
對第三部分signature部分數據進行合法性校驗
拼接前兩段密文,即:signing_input
從第一段明文中獲取加密算法,默認:HS256
使用 算法+鹽 對signing_input 進行加密,將獲得的結果和signature密文進行比較。

jwt的原理和優點

通常在先後端分離時,用於作用戶認證(登陸)使用的技術。
jwt的實現原理:
    - 用戶登陸成功以後,會給前端返回一段token。
    - token是由.分割的三段組成。
        - 第一段:類型和算法信心
        - 第二段:用戶信息+超時時間
        - 第三段:hs256(前兩段拼接)加密 + base64url
    - 之後前端再次發來信息時
        - 超時驗證
        - token合法性校驗
#優點:
    - token只在前端保存,後端只負責校驗。
    - 內部集成了超時時間,後端能夠根據時間進行校驗是否超時。
    - 因爲內部存在hash256加密,因此用戶不能夠修改token,只要一修改就認證失敗。

具體使用

用戶登錄

import uuid
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning
from rest_framework import status

from api import models

class LoginView(APIView):
    """
    登陸接口
    """
    def post(self,request,*args,**kwargs):

        # 基於jwt的認證
        # 1.去數據庫獲取用戶信息
        from rest_framework_jwt.settings import api_settings
        #頭信息
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        #第三個數據的加密
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        
        user = models.UserInfo.objects.filter(**request.data).first()
        if not user:
            return Response({'code':1000,'error':'用戶名或密碼錯誤'})
    
        '''
        'user_id': user.pk,
        'username': username,
        'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA
        '''
            #頭信息的處理 
        payload = jwt_payload_handler(user)
        #對三段信息的編碼 加密
        token = jwt_encode_handler(payload)
        return Response({'code':1001,'data':token})
    
    
    
    
#第二種方式
class LoginView(APIView):
    authentication_classes = []
    def post(self,request,*args,**kwargs):
        # 1.根據用戶名和密碼檢測用戶是否能夠登陸
        user = models.UserInfo.objects.filter(username=request.data.get('username'),password=request.data.get('password')).first()
        if not user:
            return Response({'code':10001,'error':'用戶名或密碼錯誤'})

        # 2. 根據user對象生成payload(中間值的數據)
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        payload = jwt_payload_handler(user)

        # 3. 構造前面數據,base64加密;中間數據base64加密;前兩段拼接而後作hs256加密(加鹽),再作base64加密。生成token
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        token = jwt_encode_handler(payload)
        return Response({'code': 10000, 'data': token})

用戶認證

開始解碼

from rest_framework.views import APIView
from rest_framework.response import Response

# from rest_framework.throttling import AnonRateThrottle,BaseThrottle


class ArticleView(APIView):
    # throttle_classes = [AnonRateThrottle,]

    def get(self,request,*args,**kwargs):
        # 獲取用戶提交的token,進行一步一步校驗
        import jwt
        from rest_framework import exceptions
        from rest_framework_jwt.settings import api_settings
        #進行解碼
        jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
        #獲取到加密以後的字符串
        jwt_value = request.query_params.get('token')
        try:
            #對token進行解密
            payload = jwt_decode_handler(jwt_value)
            #判斷簽名是否過時
        except jwt.ExpiredSignature:
            msg = '簽名已過時'
            raise exceptions.AuthenticationFailed(msg)
            #判斷是否被篡改
        except jwt.DecodeError:
            msg = '認證失敗'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()
        print(payload)

        return Response('文章列表')

settings設置

'JWT_EXPIRATION_DELTA':datetime.timedelta(seconds=300)
    #默認五分鐘有效
#自定義
import datetime
JWT_AUTH = {
    "JWT_EXPIRATION_DELTA":datetime.timedelta(minutes=10)
}

視圖的mixins寫法

ListModelMixin

執行list方法
相關文章
相關標籤/搜索