對查詢出來的數據進行篩選可寫可不寫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('...')
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
提供了公共方法 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
#一 初識 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是空的 '''
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
源碼的剖析 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
基於GenericAPIViewhtml
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) #最後返回序列化的數據
模擬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
基於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) 字段對象=對象
封裝了局部更新和全局更新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()
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()#刪除
是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
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)
def retrieve(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance) return Response(serializer.data)
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
刪除
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')
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')
REST_FRAMEWORK = { "ALLOWED_VERSIONS":['v1','v2'], 'VERSION_PARAM':'version' }
不用手動一個個加所有都配置上
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"),認證成功,不須要再繼續執行其餘認證了,繼續日後權限、節流、視圖函數 '''
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
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)
- 匿名用戶,用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#不能夠訪問 '''
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()
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
from django.conf.urls import url, include from web.views.s3_throttling import TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ] urls.py
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.py 'DEFAULT_THROTTLE_RATES': { 'Vistor': '3/m', 'User': '10/m' },
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)
第一種:原始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
裝飾器
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()
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)
面向對象中基於繼承+異常處理來作的約束
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
面向對象封裝
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 )
面向對象繼承
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()
反射
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)
發送ajax請求
$.ajax({ url:'地址', type:'GET', data:{...}, success:function(arg){ console.log(arg); } })
瀏覽器具備 "同源策略的限制",致使 發送ajax請求
+ 跨域
存在沒法獲取數據。
<!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>
如何解決ajax+跨域?
CORS,跨站資源共享,本質:設置響應頭。
常見的Http請求方法
get post put patch delete options
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有值。
django中F查詢
django中獲取空Queryset
models.User.object.all().none()
基於django的fbv和cbv都能實現遵循restful規範的接口
def user(request): if request.metho == 'GET': pass class UserView(View): def get()... def post...
基於django rest framework框架實現restful api的開發。
- 免除csrf認證 - 視圖(APIView、ListAPIView、ListModelMinx) - 版本 - 認證 - 權限 - 節流 - 解析器 - 篩選器 - 分頁 - 序列化 - 渲染器
簡述drf中認證流程?
1.用戶發來請求優先執行dispatch方法 2.內部會封裝reqeustd1
簡述drf中節流的實現原理以及過程?匿名用戶/非匿名用戶 如何實現頻率限制?
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)來進行更高級的定製。
jwt以及其優點。
jwt先後端分離 用於用戶認證 jwt的實現原理: -用戶登錄成功,會給前端返回一個tokon值。 此token值只在前端保存 token值分爲 一段類型和算法信息 第二段用戶信息和超時時間 第三段前兩段數據拼接以後進行has256再次加密+base64url
序列化時many=True和many=False的區別?
應用DRF中的功能進行項目開發
***** 解析器:request.query_parmas/request.data 視圖 序列化 渲染器:Response **** request對象封裝 版本處理 分頁處理 *** 認證 權限 節流
因爲瀏覽器具備同源策略的限制 對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
請求
寫法
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
本質在數據返回值設置響應頭 from django.shortcuts import render,HttpResponse def json(request): response = HttpResponse("JSONasdfasdf") response['Access-Control-Allow-Origin'] = "*" return response
<!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 注意:同時知足以上兩個條件時,則是簡單請求,不然爲複雜請求
部署 collectstaic 收集靜態文件
用於在先後端分離時,實現用戶登陸相關。
用戶登陸成功以後,生成一個隨機字符串,給前端。 - 生成隨機字符串 加密信息 #{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,只要一修改就認證失敗。
用戶登錄成功以後,生成一個隨機字符串,本身保留一份,給前端返回一份。 之後前端再來發請求時,須要攜帶字符串 後端對字符串進行校驗
pip3 install djangorestframework-jwt
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']
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的實現原理: - 用戶登陸成功以後,會給前端返回一段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('文章列表')
'JWT_EXPIRATION_DELTA':datetime.timedelta(seconds=300) #默認五分鐘有效 #自定義 import datetime JWT_AUTH = { "JWT_EXPIRATION_DELTA":datetime.timedelta(minutes=10) }
ListModelMixin
執行list方法