django restframework是基於django和restful協議開發的框架html
在restful協議裏,一切皆是資源,操做是經過請求方式控制前端
在開始前,你須要對CBV和FBV兩種架構模式有個大概的瞭解,django restframework用的就是CBV架構,這裏提供一篇博客供你欣賞CBV
python
安裝:pip install djangorestframework
數據庫
首先看到下面這段代碼django
def post(self, request): print(request.body) print(request.POST)
請求數據通常封裝在請求頭中,而上面打印的數據,都是通過處理後的數據,那背後是怎麼進行封裝的呢?json
對於get請求,直接去url後面的數據後端
而對於post請求api
request.body: a=1&b=2 request.POST: if contentType:urlencoded: a=1&b=2 ---> {"a":1,"b":2}
當發urlencoded數據時,兩個都能打印數據,可是若是就發json數據,就只有request.body裏有了restful
怎麼看源碼呢?打印下request的類型就能夠了,print(type(request)), 查看打印的WSGIRequest就能夠了閉包
from django.core.handlers.wsgi import WSGIRequest
去它下面找POST,發現這麼一句
POST = property(_get_post, _set_post)
在_get_post方法中,進入這個方法self._load_post_and_files(),在它裏面就這麼一段代碼
elif self.content_type == 'application/x-www-form-urlencoded': #若是urlencoded類型才把self.body賦給了self._post self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict() else: self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
對於restframework的request,咱們須要瞭解下在restframework裏請求流程,它和django大體相同,由於它的APIView繼承是django的View,但在APiView中重寫了dispatch方法
看到這段代碼
url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"),
執行PublishViewSet就是APIView的as_view方法
class APIView(View):
APIView繼承了View,APIView中有as_view方法,因此會執行這個方法,方法中有這麼一句代碼
view = super(APIView, cls).as_view(**initkwargs)
最終仍是執行了父類裏的as_view方法,因此最終執行結果,獲得這麼這個view函數
def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs)
當請求來時,會執行view函數,把dispatch結果返回,而這裏dispatch方法則不是View裏的,而是APIView的,由於APIView重寫了這個方法,而django restframework的精髓就所有在這裏邊了
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs #構建一個新的request,把舊的request封裝給新的request的_request字段 #執行.data會執行新request類中的data屬性,而在self._load_data_and_files()裏 ''' if not _hasattr(self, '_data'): self._data, self._files = self._parse() if self._files: self._full_data = self._data.copy() self._full_data.update(self._files) else: self._full_data = self._data ''' #最終返回self._full_data,而上面就是對請求內容進行解析並封裝 request = self.initialize_request(request, *args, **kwargs) ''' #post print("request.data", request.data) print("request.data type", type(request.data)) #get print(request._request.GET) print(request.GET) ''' self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed #獲取get post等方法後執行,這裏用是新的request response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
在dispatch方法,經過請求方式映射,獲取到咱們寫好的請求方法,並執行
restframework在先後端傳輸數據時,主要是json數據,過程當中就要須要把其餘數據轉換成json數據,好比數據庫查詢全部數據時,是queryset對象,那就要把這對象處理成json數據返回前端
models
from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish", on_delete=True) authors=models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
那麼這裏提供三種序列化的方式:
from django.shortcuts import render, HttpResponse from rest_framework.views import APIView from .models import * # Create your views here. from rest_framework import serializers from rest_framework.response import Response class AuthorSerializers(serializers.Serializer): name = serializers.CharField(max_length=32) age = serializers.IntegerField() class AuthorView(APIView): def get(self, request, *args, **kwargs): authors = Author.objects.all() # 方式1 # data = authors.values("name","age") # from django.forms.models import model_to_dict # data = [] # for obj in authors: # data.append(model_to_dict(obj)) #方式2 # from django.core import serializers # data = serializers.serialize("json", authors) # return HttpResponse(data) #方式3 author_ser = AuthorSerializers(authors, many=True) # return Response(author_ser.data)
上面也只是說了下單表序列化,若是表中涉及到一對多,多對多怎麼操做呢?
from django.shortcuts import render, HttpResponse from rest_framework.views import APIView from .models import * # Create your views here. from rest_framework import serializers from rest_framework.response import Response class BookSerializers(serializers.Serializer): title = serializers.CharField(max_length=32) price = serializers.IntegerField() pub_date = serializers.DateField() # 一對多 # publish = serializers.CharField() #不加source時,默認給的是Publish模型定義__str__返回的字段 publish = serializers.CharField(source="publish.name") # 多對多 # authors = serializers.CharField(source="authors.all") #獲取是一個queryset對象 字符串 authors = serializers.SerializerMethodField() #經過鉤子函數自定製須要的信息 def get_authors(self, obj): temp = [] for author in obj.authors.all(): temp.append({'name':author.name, 'email':author.age}) return temp class BookView(APIView): def get(self, request, *args, **kwargs): books = Book.objects.all() bs = BookSerializers(books, many=True) return Response(bs.data)
固然上面的過程,定義serializers模型,針對表定義每一個字段,有些繁瑣,因此序列化模型也有相似於ModelForm用法,不過一對多,多對多都是默認值,取得都是對應對象的id
若是你想定製多對多和一對多,在ModelSerializers重寫這類型字段,可是須要注意的是,裏面提供的create方法不支持source定製,因此你還須要重寫create方法
from django.shortcuts import render, HttpResponse from rest_framework.views import APIView from .models import * # Create your views here. from rest_framework import serializers from rest_framework.response import Response class BookSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" #source指定字段,不影響get查看序列化,可是影響post建立數據,因此須要你重寫create方法 publish = serializers.CharField(source='publish.name') authors = serializers.SerializerMethodField() #經過鉤子函數自定製須要的信息 def get_authors(self, obj): temp = [] for author in obj.authors.all(): temp.append({'name':author.name, 'email':author.age}) return temp def create(self, validated_data): # author_list = validated_data.pop("authors") obj = Book.objects.create(**validated_data) # obj.authors.add(*author_list) return obj class BookView(APIView): def get(self, request, *args, **kwargs): books = Book.objects.all() bs = BookSerializers(books, many=True) return Response(bs.data) def post(self, request, *args, **kwargs): bs=BookSerializers(data=request.data, many=False) if bs.is_valid(): print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)
序列化超連接:HyperlinkedIdentityField序列化字段,指定三個參數 view_name url別名,lookup_field填入連接裏的值對應字段, lookup_url_kwarg url裏對應的形參名
class BookSerializers(serializers.ModelSerializer): publish= serializers.HyperlinkedIdentityField( view_name='publish_detail', lookup_field="publish_id", lookup_url_kwarg="pk") class Meta: model=Book fields="__all__" #depth=1
urlpatterns = [ url(r'^books/$', views.BookViewSet.as_view(),name="book_list"), url(r'^books/(?P<pk>\d+)$', views.BookDetailViewSet.as_view(),name="book_detail"), url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"), url(r'^publishers/(?P<pk>\d+)$', views.PublishDetailViewSet.as_view(),name="publish_detail"), ]
對於重寫create方法情景的總結(Django 1.10.1):
1.序列化類繼承serializers.Serializer,須要重寫create,update方法,看源碼這兩個方法只是定義了拋錯
class AuthorSerializers(serializers.Serializer): name = serializers.CharField(max_length=32) age = serializers.IntegerField() def create(self, validated_data): obj = Author.objects.create(**validated_data) return obj
2.表對象爲單表,序列化類繼承serializers.ModelSerializer,不須要重寫create,update方法,源碼裏就在這個類下實現了這兩個方法,另外這兩個方法是在序列化類下,因此它跟你使用哪一個視圖類無關,好比使用generics.ListCreateAPIView
class PublishSerializers(serializers.ModelSerializer): class Meta: model = Publish fields = "__all__"
3.表對象爲關聯表,序列化類繼承serializers.ModelSerializer,不存在定製某個字段,不須要重寫create,update方法
class BookSerializers(serializers.ModelSerializer): class Meta: model = Book #book表關聯表,有一對多,多對多 fields = "__all__"
4.表對象爲關聯表,序列化類繼承serializers.ModelSerializer,source指定某個字段,提示指定字段要發實例(一對多),存在的疑問就是怎麼在前端添加實例?
多對多,若是經過SerializerMethodField生成,序列化時,不提供多對多字段傳過來的值,等同這種情景下,多對多隻是隻讀顯示,不用來post添加操做,若是要操做把添加對象和多對多添加分紅兩步操做
class BookSerializers(serializers.ModelSerializer): class Meta: model = Book #book表關聯表,有一對多,多對多 fields = "__all__" authors = serializers.SerializerMethodField() def get_authors(self, obj): temp = [] for author in obj.authors.all(): temp.append({'name':author.name, 'email':author.age}) return temp publish = serializers.CharField(source="publish.email") def create(self, validated_data): # authors = validated_data.pop("authors") obj = Book.objects.create(**validated_data) # obj.authors.add(*authors) return obj
下面視圖代碼,能夠說是規規矩矩的作法,每一個視圖類下,都寫各個請求方法,細心的你確定發現了,每一個視圖的同類請求方法實現過程處理序列化模型和查詢的表不一樣,其餘的都同樣,代碼重複
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from django.shortcuts import HttpResponse from django.core import serializers from rest_framework import serializers class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" #depth=1 class PublshSerializers(serializers.ModelSerializer): class Meta: model=Publish fields="__all__" depth=1 class BookViewSet(APIView): def get(self,request,*args,**kwargs): book_list=Book.objects.all() bs=BookSerializers(book_list,many=True,context={'request': request}) return Response(bs.data) def post(self,request,*args,**kwargs): print(request.data) bs=BookSerializers(data=request.data,many=False) if bs.is_valid(): print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class BookDetailViewSet(APIView): def get(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,context={'request': request}) return Response(bs.data) def put(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,data=request.data,context={'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class PublishViewSet(APIView): def get(self,request,*args,**kwargs): publish_list=Publish.objects.all() bs=PublshSerializers(publish_list,many=True,context={'request': request}) return Response(bs.data) def post(self,request,*args,**kwargs): bs=PublshSerializers(data=request.data,many=False) if bs.is_valid(): # print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class PublishDetailViewSet(APIView): def get(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first() bs=PublshSerializers(publish_obj,context={'request': request}) return Response(bs.data) def put(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first() bs=PublshSerializers(publish_obj,data=request.data,context={'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)
restframework已經對咱們使用的這些請求方法,封裝到一些類裏面,咱們只要繼承這些類,並調用特定的方法,把結果返回便可
至因而哪一個序列化模型和哪一個模型的數據,經過靜態字段queryset和serializer_class指定
而GenericAPIView繼承了APIView,因此在程序啓動時,執行的仍是APIView的as_view方法
代碼簡化以下
from rest_framework import mixins from rest_framework import generics class BookViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class BookDetailViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)
咱們大概能夠看下這些類封裝方法的實現過程,其中源碼中的方法能夠在GenericAPIView類找到
def list(self, request, *args, **kwargs): #self.get_queryset()獲取靜態字段指定數據 #filter_queryset支持配置文件篩選數據 queryset = self.filter_queryset(self.get_queryset()) #分頁 page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) #get_serializer獲取靜態字段中指定的序列化類,並實例對象 serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
固然上面這個實現過程,在定義請求方法,仍是有點重複,rest框架提供了一組已經混合好(minxed-in)的通用視圖進一步封裝
generics.ListCreateAPIView 查看多條和添加視圖
generics.RetrieveUpdateDestroyAPIView 查看單條,修改和刪除視圖
from rest_framework import mixins from rest_framework import generics class BookViewSet(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookSerializers class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializers class PublishViewSet(generics.ListCreateAPIView): queryset = Publish.objects.all() serializer_class = PublshSerializers class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Publish.objects.all() serializer_class = PublshSerializers
點進去一個類,發現它其實就是經過多繼承組合了第二階段中的類
class ListCreateAPIView(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView): """ Concrete view for listing a queryset or creating a model instance. """ def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
上面的代碼已經夠簡潔了吧,它還能夠簡潔,你能夠觀察一下,就是在對全部的書操做和單本書操做,他們使用的數據和序列化模型的同樣的,rest框架還作到這方面的簡化,他們兩本質的區別在於操做全部書是get,post方法,而單本就是get,put,delete方法,實現原理是在url上對請求方法進行映射
url(r'^books/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"), url(r'^books/(?P<pk>\d+)$', views.BookViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }),name="book_detail"),
from rest_framework.viewsets import ModelViewSet
class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializers
而ModelViewSet實現過程也是多繼承
class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet): pass
那咱們再看下,最後這種方式在請求到來時,是怎麼執行的,從多繼承中看,找as_view函數
最終仍是會找到ViewSetMixin下的as_view執行,把請求映射關係傳給了actions
def as_view(cls, actions=None, **initkwargs):
傳入到view函數中,閉包
def view(request, *args, **kwargs): self = cls(**initkwargs) # We also store the mapping of request methods to actions, # so that we can later set the action attribute. # eg. `self.action = 'list'` on an incoming GET request. self.action_map = actions # Bind methods to actions # This is the bit that's different to a standard view for method, action in actions.items(): handler = getattr(self, action) setattr(self, method, handler) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs # And continue as usual return self.dispatch(request, *args, **kwargs)
當請求來時,執行閉包的view函數,在下列這段代碼進行請求方法的映射
for method, action in actions.items(): #method = get,post #action=list,create handler = getattr(self, action) #handler==self.list,self.create函數 setattr(self, method, handler) #self.get-->self.list, self.post-->self.create
執行dispatch下這段代碼時
if request.method.lower() in self.http_method_names: #get請求時,執行self.list.... handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
認證前提是須要登陸的,因此你還須要寫好登陸
models
class User(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32)\ def __str__(self): return self.name class Token(models.Model): user = models.OneToOneField("User") token = models.CharField(max_length=128) def __str__(self): return self.token
登陸視圖
def get_random_str(user): import hashlib,time ctime=str(time.time()) md5=hashlib.md5(bytes(user,encoding="utf8")) md5.update(bytes(ctime,encoding="utf8")) return md5.hexdigest() from .models import User class LoginView(APIView): def post(self,request): name=request.data.get("name") pwd=request.data.get("pwd") user=User.objects.filter(name=name,pwd=pwd).first() res = {"state_code": 1000, "msg": None} if user: random_str=get_random_str(user.name) token=Token.objects.update_or_create(user=user,defaults={"token":random_str}) res["token"]=random_str else: res["state_code"]=1001 #錯誤狀態碼 res["msg"] = "用戶名或者密碼錯誤" import json return Response(json.dumps(res,ensure_ascii=False))
首先你必須明確的是,認證,權限,頻率那都是請求到來時的操做,大概都能猜到會在分發dispatch下執行,那咱們就找到APIView下dispatch看下,下面這幾句代碼
你會發現,在進入到認證..這些操做的時候,request已是從新封裝後的新的request
request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: #認證 權限 頻率都在這裏面作了 self.initial(request, *args, **kwargs)
在initial方法下,有這三句代碼,分別對應認證,權限,頻率組件
self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request)
那咱們就先看認證組件幹了些什麼事,一旦理解認證組件,其餘的兩個就好理解了,實現方式是相似的
在perform_authentication認證方法裏,就一句代碼,你可能驚呼,這啥啊,就這麼一句?給人感受是字段啊,但深究下,若是是字段確定實現不了認證功能,你須要大膽猜想是靜態方法,是否是?咱們去這個request下找下就知道了,注意:這裏request是新的request
def perform_authentication(self, request): """ Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication will instead be performed lazily, the first time either `request.user` or `request.auth` is accessed. """ request.user
也就是這個返回的request
request = self.initialize_request(request, *args, **kwargs)
而它返回的就是這個request對象
return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context )
去它下面還真找到user靜態方法
@property def user(self): """ Returns the user associated with the current request, as authenticated by the authentication classes provided to the request. """ if not hasattr(self, '_user'): with wrap_attributeerrors(): self._authenticate() return self._user
最後它會執行_authenticate方法
def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ for authenticator in self.authenticators: try: user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return self._not_authenticated()
要想搞明白上面代碼,就必需要知道self.authenticators是啥?
在__init__方法裏,有這麼句,而authenticators在實例化對象傳下來的
self.authenticators = authenticators or ()
實例化對象時,傳入是下面這玩意,那它又幹了啥呢?
authenticators=self.get_authenticators(),
下面self就是咱們定義的視圖類,它會去咱們定義的類下面找authentication_classes,循環並實例化
def get_authenticators(self): """ Instantiates and returns the list of authenticators that this view can use. """ return [auth() for auth in self.authentication_classes]
authentication_classes又是些啥了?從名字上看,就是認證類,因此這個是由你來定義的,可是若是咱們不定義的呢?
當前類沒有,就會找父類去中,一直找到APIView下
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
會去api_settings下取個DEFAULT_AUTHENTICATION_CLASSES默認的認證類
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
None,DEFAULTS對應APISettings下__init__方法的user_settings和defaults
而DEFAULTS則是settings配置文件的變量,並配置這個,默認狀況下就是這些認證類
'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication' ),
api_settings.DEFAULT_AUTHENTICATION_CLASSES會去執行APISettings的__getattr__方法
def __getattr__(self, attr): if attr not in self.defaults: raise AttributeError("Invalid API setting: '%s'" % attr) try: # Check if present in user settings #self.user_settings這裏調用時靜態方法 #attr=DEFAULT_AUTHENTICATION_CLASSES ''' @property def user_settings(self): #user_settings爲None,因此這裏hasattr(self, '_user_settings')爲False if not hasattr(self, '_user_settings'): #嘗試去settings配置文件找REST_FRAMEWORK這麼個配置 self._user_settings = getattr(settings, 'REST_FRAMEWORK', {}) return self._user_settings ''' #若是在settings找REST_FRAMEWORK配置就返回配置,沒找到就是{} #沒配置空字典獲取DEFAULT_AUTHENTICATION_CLASSES就報錯,走異常分支 val = self.user_settings[attr] except KeyError: # Fall back to defaults ''' 去默認配置文件取到這兩項 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication' ), ''' val = self.defaults[attr] # Coerce import strings into classes if attr in self.import_strings: val = perform_import(val, attr) # Cache the result self._cached_attrs.add(attr) setattr(self, attr, val) return val
因此咱們能夠得出
若是想局部配置認證類,在咱們定義視圖類下經過authentication_classes指定(列表)
若是想全局在settings下配置
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.TokenAuth",] }
回到request下user屬性方法下的_authenticate方法
def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ for authenticator in self.authenticators: #authenticator 認證類實例 try: #調用認證類實例下的authenticate方法,傳入了self,也就是request #因此你認證類要有authenticate方法,返回值爲元組 user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator #元組內容 用戶信息 和 auth信息,咱們用的token self.user, self.auth = user_auth_tuple return
認證類
from rest_framework.authentication import BaseAuthentication from rest_framework.authentication import exceptions class Authentication(BaseAuthentication): def authenticate(self, request): token = request.GET.get("token") token_obj = Token.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed("驗證失敗") else: return token_obj.user.name, token_obj.token
視圖類
def get_random_str(user): import hashlib,time ctime=str(time.time()) md5=hashlib.md5(bytes(user,encoding="utf8")) md5.update(bytes(ctime,encoding="utf8")) return md5.hexdigest() from app01.service.auth import * from django.http import JsonResponse class LoginViewSet(APIView): authentication_classes = [Authentication,] #通常狀況下登陸頁面是不須要認證的,你能夠把這裏改爲空列表 def post(self,request,*args,**kwargs): res={"code":1000,"msg":None} try: user=request._request.POST.get("user") pwd=request._request.POST.get("pwd") user_obj=UserInfo.objects.filter(user=user,pwd=pwd).first() print(user,pwd,user_obj) if not user_obj: res["code"]=1001 res["msg"]="用戶名或者密碼錯誤" else: token=get_random_str(user) UserToken.objects.update_or_create(user=user_obj,defaults={"token":token}) res["token"]=token except Exception as e: res["code"]=1002 res["msg"]=e return JsonResponse(res,json_dumps_params={"ensure_ascii":False})
瞭解認證組件的源碼後,下面這兩個組件就不帶看了,直接看怎麼用吧
權限類裏重寫has_permission方法
from rest_framework.permissions import BasePermission class SVIPPermission(BasePermission): message="SVIP才能訪問!" def has_permission(self, request, view): if request.user.user_type==3: return True return False
視圖類裏經過permission_classe指定權限類
from app01.service.permissions import * class BookViewSet(generics.ListCreateAPIView): permission_classes = [SVIPPermission,] queryset = Book.objects.all() serializer_class = BookSerializers
全局配置
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",] }
依據什麼來斷定它的訪問頻率,IP?訪問相關的信息你均可以從request.META中獲取
頻率類裏定義allow_request方法
from rest_framework.throttling import BaseThrottle class VisitThrottle(BaseThrottle): ''' 一分鐘內訪問超過5次 禁用兩分鐘 ''' VISIT_RECORD = {} def allow_request(self, request, view): remote_addr = request.META.get('REMOTE_ADDR') print(remote_addr) now_time = time.time() if remote_addr not in VisitThrottle.VISIT_RECORD: VisitThrottle.VISIT_RECORD[remote_addr] = { 'start_time' : [now_time,], 'forbid' : False } return True start_time = VisitThrottle.VISIT_RECORD[remote_addr]['start_time'] forbid_state = VisitThrottle.VISIT_RECORD[remote_addr]['forbid'] while start_time and start_time[-1] < now_time - 60 and not forbid_state: start_time.pop() if forbid_state: if now_time - start_time[-1] > 120: print('兩分鐘過了,解禁') VisitThrottle.VISIT_RECORD[remote_addr] = { 'start_time': [now_time, ], 'forbid': False } print(VisitThrottle.VISIT_RECORD[remote_addr]) return True else: print("兩分鐘內禁止訪問,已通過了%s秒"%(now_time - start_time[-1])) return False else: if len(start_time) < 5: print("訪問%s次"%(len(start_time) + 1)) print(start_time) start_time.insert(0, now_time) return True else: print("訪問%s次,禁止訪問"%len(start_time)) VisitThrottle.VISIT_RECORD[remote_addr] = { 'start_time': [now_time, ], 'forbid': True } return False
視圖類中經過throttle_classes指定頻率類
from app01.service.throttles import * class BookViewSet(generics.ListCreateAPIView): throttle_classes = [VisitThrottle,] queryset = Book.objects.all() serializer_class = BookSerializers
全局配置
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",], "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",] }
內置玩法
class VisitThrottle(SimpleRateThrottle): scope="visit_rate" def get_cache_key(self, request, view): return self.get_ident(request)
settings
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",], "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",], "DEFAULT_THROTTLE_RATES":{ "visit_rate":"5/m", } }
上面已經對django和restframework的request解析過程進行分析過了,解析器主要對請求體裏的信息進行解析
局部視圖
from rest_framework.parsers import JSONParser,FormParser class PublishViewSet(generics.ListCreateAPIView): parser_classes = [FormParser,JSONParser] queryset = Publish.objects.all() serializer_class = PublshSerializers def post(self, request, *args, **kwargs): print("request.data",request.data) return self.create(request, *args, **kwargs)
全局視圖
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",], "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",], "DEFAULT_THROTTLE_RATES":{ "visit_rate":"5/m", }, "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.FormParser',] }
響應器主要是Response返回的內容,它會幫你生成展現數據的頁面,頁面支持你定義好的請求方法,若是你不想看它給頁面,也能夠經過?format=json查看
分兩種狀況,一種是原生get,另一種就是鉤子映射到list方法,因此你想若是想進行分頁,能夠這麼作
重寫get或list方法
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination def get(self,request): book_list=Book.objects.all() pp=PageNumberPagination() pager_books=pp.paginate_queryset(queryset=book_list,request=request,view=self) print(pager_books) bs=BookSerializers(pager_books,many=True) #return Response(bs.data) return pp.get_paginated_response(bs.data)
固然上面這個過程是沒有給一頁顯示多少的,並且分頁內部沒有默認值,它會去settings下取PAGE_SIZE,因此你好配置這個
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.TokenAuth",] "PAGE_SIZE":2 }
固然這個是全局的,若是你想局部,你能夠這麼幹,自定義分頁類,下面實例過程就換成你定義這個類
pp=PageNumberPagination()
自定義分頁類
class PNPagination(PageNumberPagination): page_size = 1 page_query_param = 'page' #url分頁參數名 page_size_query_param = "size" #url每頁數參數名 max_page_size = 5 #當?page=1&size=6 size超過5時就不生效
針對鉤子映射的,還能夠這麼作,經過pagination_class指定分頁器,固然指定能夠不用重寫list方法了,由於在list方法的源碼裏,會自動獲取這個指定的分液器
class AuthorModelView(viewsets.ModelViewSet): pagination_class = PNPagination authentication_classes = [TokenAuth,] permission_classes = [] throttle_classes = []# 限制某個IP每分鐘訪問次數不能超過20次 queryset = Author.objects.all() serializer_class = AuthorModelSerializers
對LimitOffsetPagination偏移分頁,自定義類時 default_limit指定限制多少,url裏 offset參數指定偏移量