rest framework組件爲咱們提供了下面的這些功能: 按照HTTP請求的生命週期去記; 先是進入路由,在視圖,進入dispatch()裏面,然 後提供的是版本,權限,認證,頻率,而後從 解析器()裏面取數據,再序列化,分頁,渲染器。
a. 用戶url傳入的token認證(做爲url的參數傳入)html
from django.conf.urls import url, include from web.viewsimport TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ]
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from rest_framework.request import Request from rest_framework import exceptions from .models import * class TokenAuth(BaseAuthentication): def authenticate(self, request): token = request.GET.get("token") # request.query_params ---> request.GET token_obj = Token.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed("驗證失敗123!") else: return token_obj.user, token_obj.token # 返回一個元組 request.user, request.auth # def authenticate_header(self, request): # pass # 只是咱們繼承了BaseAuthentication的認證組件 就不用寫了。 class TestView(APIView): authentication_classes = [TokenAuth, ] permission_classes = [] def get(self, request, *args, **kwargs): print(request.user) print(request.auth) return Response('GET請求,響應內容') def post(self, request, *args, **kwargs): return Response('POST請求,響應內容') views.py
認證和權限python
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ]
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from rest_framework.permissions import BasePermission from rest_framework.request import Request from rest_framework import exceptions from .models import * class TokenAuth(BaseAuthentication): def authenticate(self, request): token = request.GET.get("token") # request.query_params ---> request.GET token_obj = Token.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed("驗證失敗123!") else: return token_obj.user.pk, token_obj.token # 返回一個元組 request.user, request.auth # def authenticate_header(self, request): # pass # 只是咱們繼承了BaseAuthentication的認證組件 就不用寫了。 class SVIPPermisson(BasePermission): message = "沒有SVIP權限" # 錯誤信息 def has_permission(self, request, view): if request.user == 1: # request.user.pk 是從認證組件裏面取的 return True else: return False #沒有權限 class TestView(APIView): # 認證的動做是由request.user觸發 authentication_classes = [TestAuthentication, ] # 權限 # 循環執行全部的權限 permission_classes = [TestPermission, ] 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請求,響應內容') views.py
全局使用權限和認證,就須要在settings裏面去配置全局信息。web
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": [ "web.utils.TestAuthentication", ], "DEFAULT_PERMISSION_CLASSES": [ "web.utils.TestPermission", ], } settings.py
a. 基於用戶IP限制訪問頻率數據庫
這種方法是沒法控制的,由於用戶能夠換代理IP,因此沒有徹底的限制。django
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ] urls.py
#!/usr/bin/env python # -*- coding:utf-8 -*- import time from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import exceptions from rest_framework.throttling import BaseThrottle from rest_framework.settings import api_settings # 保存訪問記錄 RECORD = { '用戶IP': [12312139, 12312135, 12312133, ] } class TestThrottle(BaseThrottle): ctime = time.time def get_ident(self, request): """ 根據用戶IP和代理IP,當作請求者的惟一IP Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR if present and number of proxies is > 0. If not use all of HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR. """ xff = request.META.get('HTTP_X_FORWARDED_FOR') remote_addr = request.META.get('REMOTE_ADDR') num_proxies = api_settings.NUM_PROXIES if num_proxies is not None: if num_proxies == 0 or xff is None: return remote_addr addrs = xff.split(',') client_addr = addrs[-min(num_proxies, len(addrs))] return client_addr.strip() return ''.join(xff.split()) if xff else remote_addr def allow_request(self, request, view): """ 是否仍然在容許範圍內 Return `True` if the request should be allowed, `False` otherwise. :param request: :param view: :return: True,表示能夠經過;False表示已超過限制,不容許訪問 """ # 獲取用戶惟一標識(如:IP) # 容許一分鐘訪問10次 num_request = 10 time_request = 60 now = self.ctime() ident = self.get_ident(request) self.ident = ident if ident not in RECORD: RECORD[ident] = [now, ] return True history = RECORD[ident] while history and history[-1] <= now - time_request: #剔除 超時或者是不符合的時間戳。 history.pop() if len(history) < num_request: #判斷當前IP的訪問的次數(經過時間戳的數量) history.insert(0, now) #把當前的訪問的時間戳放在第一個。 return True def wait(self): """ 多少秒後能夠容許繼續訪問 Optionally, return a recommended number of seconds to wait before the next request. """ #last_time = RECORD[self.ident][0] #last_time 就是表明最長時間後才能夠繼續訪問。 min_time = RECORD[self.ident][-1] #列表中最後一個是最小的時間,當最小的時間失效後就能夠訪問了。 now = self.ctime() return int(60 + min_time - now) #還有多少秒能夠繼續訪問,就表明着列表中最後一個時間失效剔除後,列表中的個數就少於次數了,這樣不就能夠訪問了嗎?也是能夠訪問的最小時間。 class TestView(APIView): throttle_classes = [TestThrottle, ] 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請求,響應內容') def throttled(self, request, wait): """ 訪問次數被限制時,定製錯誤信息 """ class Throttled(exceptions.Throttled): default_detail = '請求被限制.' extra_detail_singular = '請 {wait} 秒以後再重試.' extra_detail_plural = '請 {wait} 秒以後再重試.' raise Throttled(wait) views.py
b. 基於用戶IP顯示訪問頻率(利於Django緩存)經常使用的 json
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES': { 'test_scope': '10/m', }, }
在源碼中api
num, period = rate.split('/') num_requests = int(num) duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] return (num_requests, duration)
即經過/分割,前面是容許訪問的次數,後面是時間。瀏覽器
即在60s內能夠訪問的次數爲10次緩存
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ]
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import exceptions from rest_framework.throttling import SimpleRateThrottle class TestThrottle(SimpleRateThrottle): # 配置文件定義的顯示頻率的Key scope = "test_scope" def get_cache_key(self, request, view): """ Should return a unique cache-key which can be used for throttling. Must be overridden. May return `None` if the request should not be throttled. """ if not request.user: ident = self.get_ident(request) else: ident = request.user return self.cache_format % { 'scope': self.scope, 'ident': ident } class TestView(APIView): throttle_classes = [TestThrottle, ] 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請求,響應內容') def throttled(self, request, wait): """ 訪問次數被限制時,定製錯誤信息 """ class Throttled(exceptions.Throttled): default_detail = '請求被限制.' extra_detail_singular = '請 {wait} 秒以後再重試.' extra_detail_plural = '請 {wait} 秒以後再重試.' raise Throttled(wait) views.py
如何實現的訪問頻率控制? 訪問頻率控制的原理: 用戶進來時候,把它的訪問時間記錄所有放進來,而後每次再訪問進來時候,把超時或者是不符合的時間戳給剔除。 而後根據它時間記錄的個數來作判斷。 匿名用戶:沒法控制,由於用戶能夠換代理IP,因此沒發作。 { 192.168.1.1:[1521223123.232, 1521223122.232, 1521223121.232], 192.168.1.2:[1521223123.232, 1521223122.232, 1521223121.232], 192.168.1.3:[1521223123.232, 1521223122.232, 1521223121.232], 192.168.1.4:[1521223123.232, 1521223122.232, 1521223121.232], 192.168.1.5:[1521223123.232, 1521223122.232, 1521223121.232], 192.168.1.6:[1521223123.232, 1521223122.232, 1521223121.232], 先剔除時間 } 真要作匿名用戶的訪問頻率的話,能夠給匿名用戶給隨機的字符串,而後給每次訪問的時候都帶着,用它來記錄時間,可是這樣也不行,由於瀏覽器若是不帶的話,每次都是第一次訪問。憑我如今的能力,尚未辦法能解決,想問您有什麼解決的好辦法呢 登陸用戶:若是有不少帳號,也沒法限制 { alex:[1521223123.232, 1521223122.232, 1521223121.232], eric:[1521223123.232, 1521223122.232, 1521223121.232], } 參考源碼:from rest_framework.throttling import SimpleRateThrottle
SimpleRateThrottle請求頻率源碼分析app
https://blog.csdn.net/u013210620/article/details/79898512
a. 基於url的get傳參方式(QueryParameterVersioning)
如:/users?version=v1
REST_FRAMEWORK = { 'DEFAULT_VERSION': 'v1', # 默認版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 容許的版本 'VERSION_PARAM': 'version' # URL中獲取值的key }
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'^test/', TestView.as_view(),name='test'), ]
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import QueryParameterVersioning class TestView(APIView): versioning_class = QueryParameterVersioning def get(self, request, *args, **kwargs): # 獲取版本 print(request.version) # 獲取版本管理的類 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse('test', request=request) print(reverse_url) return Response('GET請求,響應內容') def post(self, request, *args, **kwargs): return Response('POST請求,響應內容') def put(self, request, *args, **kwargs): return Response('PUT請求,響應內容')
from rest_framework.versioning import URLPathVersioning,QueryParameterVersioning # URLPathVersioning 是把版本放在url上;最經常使用的 # QueryParameterVersioning 是把版本當作get的參數 ?version
基於url的正則方式(URLPathVersioning)
如:/v1/users/
REST_FRAMEWORK = { 'DEFAULT_VERSION': 'v1', # 默認版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 容許的版本 'VERSION_PARAM': 'version' # URL中獲取值的key }
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'), ]
rom rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import URLPathVersioning class TestView(APIView): versioning_class = URLPathVersioning def get(self, request, *args, **kwargs): # 獲取版本 print(request.version) # 獲取版本管理的類 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse('test', request=request) print(reverse_url) return Response('GET請求,響應內容') def post(self, request, *args, **kwargs): return Response('POST請求,響應內容') def put(self, request, *args, **kwargs): return Response('PUT請求,響應內容')
全局使用
REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning", 'DEFAULT_VERSION': 'v1', 'ALLOWED_VERSIONS': ['v1', 'v2'], 'VERSION_PARAM': 'version' }
更多的方式參考https://www.cnblogs.com/wupeiqi/articles/7805382.html
#解析器 from rest_framework.parsers import JSONParser from rest_framework.parsers import FormParser from rest_framework.parsers import MultiPartParser from rest_framework.parsers import FileUploadParser # parser_classes = [JSONParser,FormParser,MultiPartParser] #解析器,即按照content-type的不一樣來處理數據 #僅處理請求頭content-type爲application/json的請求體爲JSONParser #僅處理請求頭content-type爲application/x-www-form-urlencoded 的請求體爲FormParser #僅處理請求頭content-type爲multipart/form-data的請求體爲MultiPartParser #form表單上傳文件
同時多個Parser,單個使用的使用就寫須要用的那個
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'test/', TestView.as_view(), name='test'), ]
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.request import Request from rest_framework.parsers import JSONParser, FormParser, MultiPartParser class TestView(APIView): parser_classes = [JSONParser, FormParser, MultiPartParser, ] def post(self, request, *args, **kwargs): print(request.content_type) # 獲取請求的值,並使用對應的JSONParser進行處理 print(request.data) # application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值 print(request.POST) print(request.FILES) return Response('POST請求,響應內容') def put(self, request, *args, **kwargs): return Response('PUT請求,響應內容')
全局使用
REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser' 'rest_framework.parsers.FormParser' 'rest_framework.parsers.MultiPartParser' ] }
注意:個別特殊的值能夠經過Django的request對象 request._request 來進行獲取
序列化用於對用戶請求數據進行驗證和數據進行序列化
基於Model自動生成字段
from rest_framework import serializers from app01.models import * class CouserSerializers(serializers.ModelSerializer): ''' 課程的序列化 ''' #自定義的字段 #source爲表裏面的全部字段 level = serializers.CharField(source='get_level_display') #自動的幫你添加() course_type = serializers.CharField(source='get_course_type_display') class Meta: model = Course fields = ["id",'name',"course_img","course_type","brief","level","period"] # depth=2 用深度幫你進行跨表 class CourseDetailViewSerializers(serializers.ModelSerializer): ''' 詳細課程的序列化 ''' #一對一的反向查詢,fk,choice都是用source,只能取一條或者是一個數據 name = serializers.CharField(source='course.name') img = serializers.CharField(source='course.course_img') content = serializers.CharField(source='course.brief') id = serializers.CharField(source='course.id') #多對多的,用SerializerMethodField(),能夠獲取多個 #推薦課程 recommend_courses = serializers.SerializerMethodField() #老師 teachers= serializers.SerializerMethodField() # 每一個時間段的價格 price = serializers.SerializerMethodField() # 課程大綱 course_detail = serializers.SerializerMethodField() #課程章節 coursechapter = serializers.SerializerMethodField() # # #課時目錄 # coursesection = serializers.SerializerMethodField() # 推薦課程 def get_recommend_courses(self,obj): #obj--> CourseDetail.obj # obj.recommendcourse.all() return [{"id":item.id,"title":item.name}for item in obj.recommend_courses.all()] def get_teachers(self,obj): #obj--> CourseDetail.obj # obj.recommendcourse.all() return [{"id":item.id,"name":item.name,"title":item.title}for item in obj.teachers.all()] # 每一個時間段的價格 def get_price(self,obj): return [{"valid_period":item.get_valid_period_display(),"price":item.price}for item in obj.course.price_policy.all()] def get_course_detail(self,obj): return [{"title":item.title,"content":item.content}for item in obj.courseoutline_set.all()] def get_coursechapter(self,obj): return [{"chapter":item.chapter,"title":item.name,"summarys":item.summary}for item in obj.course.coursechapters.all()] # def get_coursesection(self,obj): # return [{"chapter":item.chapter,"name":item.name,"summary":item.summary}for item in obj.course.coursechapters.coursesections.all()] class Meta: model = CourseDetail fields = ["id","name","img","hours","teachers","content",'why_study',"what_to_study_brief", "career_improvement","prerequisite","recommend_courses","price",'course_detail','coursechapter',]
class TestView(APIView): def get(self, request, *args, **kwargs): # 序列化,將數據庫查詢字段序列化爲字典 data_list = models.UserInfo.objects.all() ser = CourseDetailViewSerializers(instance=data_list, many=True) # 或 # obj = models.UserInfo.objects.all().first() # ser = CourseDetailViewSerializers(instance=obj, many=False) return Response(ser.data) def post(self, request, *args, **kwargs): # 驗證,對請求發來的數據進行驗證 print(request.data) ser = CourseDetailViewSerializers(data=request.data) if ser.is_valid(): print(ser.validated_data)
ser.save() #直接保存到數據庫,在這裏會調用GoodSerializer的create方法。
else: print(ser.errors) return Response('POST請求,響應內容')
a根據頁碼進行分頁
from rest_framework.views import APIView from rest_framework import serializers from .. import models from rest_framework.pagination import PageNumberPagination class StandardResultsSetPagination(PageNumberPagination): # 默認每頁顯示的數據條數 page_size = 1 # 獲取URL參數中設置的每頁顯示數據條數 page_size_query_param = 'page_size' # 獲取URL參數中傳入的頁碼key page_query_param = 'page' # 最大支持的每頁顯示的數據條數 max_page_size = 1 class UserSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" class UserViewSet(APIView): def get(self, request, *args, **kwargs): user_list = models.UserInfo.objects.all().order_by('-id') # 實例化分頁對象,獲取數據庫中的分頁數據 paginator = StandardResultsSetPagination() page_user_list = paginator.paginate_queryset(user_list, self.request, view=self) # 序列化對象 serializer = UserSerializer(page_user_list, many=True) # 生成分頁和數據 response = paginator.get_paginated_response(serializer.data) return response
b位置和個數進行分頁
from rest_framework.views import APIView from rest_framework import serializers from .. import models from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination class StandardResultsSetPagination(LimitOffsetPagination): # 默認每頁顯示的數據條數 default_limit = 10 # URL中傳入的顯示數據條數的參數 limit_query_param = 'limit' # URL中傳入的數據位置的參數 offset_query_param = 'offset' # 最大每頁顯得條數 max_limit = None class UserSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" class UserViewSet(APIView): def get(self, request, *args, **kwargs): user_list = models.UserInfo.objects.all().order_by('-id') # 實例化分頁對象,獲取數據庫中的分頁數據 paginator = StandardResultsSetPagination() page_user_list = paginator.paginate_queryset(user_list, self.request, view=self) # 序列化對象 serializer = UserSerializer(page_user_list, many=True) # 生成分頁和數據 response = paginator.get_paginated_response(serializer.data) return response views.py
a. 自定義路由
from django.conf.urls import url, include from web.views import s11_render urlpatterns = [ url(r'^test/$', s11_render.TestView.as_view()), url(r'^test\.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view()), url(r'^test/(?P<pk>[^/.]+)/$', s11_render.TestView.as_view()), url(r'^test/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view()) ]
b. 半自動路由
from django.conf.urls import url,include from django.contrib import admin from app01 import views url(r'^api/(?P<version>\w+)/bookviewset/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name='bookviewset'), url(r'^api/bookviewset/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name='bookviewset'),
# url(r'^bookviewset/(?P<pk>\d+)$', views.BookViewSet.as_view({"get":"retrieve","post":"update"}), name='bookviewsetdetail'),
c. 全自動路由
from django.conf.urls import url,include from rest_framework import routers from app01 import views router = routers.DefaultRouter() router.register("bookviewset",views.BookViewSet) url(r'',include(router.urls)), #自動的幫你生成4個url #^bookviewset/$ [name='book-list'] #^bookviewset\.(?P<format>[a-z0-9]+)/?$ [name='book-list'] .json\?format=json #^bookviewset/(?P<pk>[^/.]+)/$ [name='book-detail'] #^bookviewset/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='book-detail']
根據繼承的類的不一樣有不一樣的形式
class View(object): class APIView(View): class GenericAPIView(views.APIView): class GenericViewSet(ViewSetMixin, generics.GenericAPIView) # ViewSetMixin 是對as_view裏面的參數進行處理 class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet):
ViewSetMixin
url(r'^api/(?P<version>\w+)/bookviewset/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name='bookviewset')
沒有繼承ViewSetMixin 這個類的話get請求就是執行對應的get的方法。
ModelViewSet把上面的增刪改查都包含了,減小了咱們本身寫麻煩。
from app01.forms import TokenAuth,SVIPPermisson #解析器 from rest_framework.parsers import JSONParser from rest_framework.parsers import FormParser from rest_framework.parsers import MultiPartParser #版本 from rest_framework.versioning import URLPathVersioning,QueryParameterVersioning class BookViewSet(viewsets.ModelViewSet): authentication_classes=[TokenAuth,] #認證組件 # permission_classes = [SVIPPermisson,] #權限組件 # throttle_classes = [] #頻率組件 # parser_classes = [JSONParser,FormParser,MultiPartParser] #解析器,即按照content-type的不一樣來處理數據 queryset = Book.objects.all() # 取的數據 print(queryset) serializer_class = BookModelSerializers # 序列化器 pagination_class = MyPageNumberPagination #分頁器
根據 用戶請求URL 或 用戶可接受的類型,篩選出合適的 渲染組件。
a. json
訪問URL:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers from rest_framework.renderers import JSONRenderer from .. import models class TestSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" class TestView(APIView): renderer_classes = [JSONRenderer, ] def get(self, request, *args, **kwargs): user_list = models.UserInfo.objects.all() ser = TestSerializer(instance=user_list, many=True) return Response(ser.data)
b. 表格
訪問URL:
from rest_framework.renderers import AdminRenderer
renderer_classes = [AdminRenderer, ]
c. Form表單
訪問URL:
from rest_framework.renderers import HTMLFormRenderer renderer_classes = [HTMLFormRenderer, ]
等更多渲染器的形式點擊查看
完整全面的內容點擊
# URLPathVersioning 是把版本放在url上;最經常使用的
# QueryParameterVersioning 是把版本當作get的參數 ?version