只有認證經過的用戶才能訪問指定的url地址,好比:查詢課程信息,須要登陸以後才能查看,沒有登陸,就不能查看,這時候須要用到認證組件python
寫一個認證類 from rest_framework.authentication import BaseAuthentication class MyAuth(BaseAuthentication): def authenticate(self,request): # request 是封裝後的 token = request.query_params.get('token') ret = models.UserToken.objects.filter(token=token).first() if ret: # 認證經過 return else: raise AuthenticationFailed('認證失敗') #能夠不寫了 def authenticate_header(self,ss): pass 局部使用 authentication_classes=[MyAuth,MyAuth2] 全局使用 查找順序:自定義的APIView裏找---》項目settings裏找---》內置默認的 REST_FRAMEWORK={ 'DEFAULT_AUTHENTICATION_CLASSES':['utils.common.MyAuth',] }
寫一個類 class MyPermission(): def has_permission(self,request,view): token=request.query_params.get('token') ret=models.UserToken.objects.filter(token=token).first() if ret.user.type==2: # 超級用戶能夠訪問 return True else: return False 局部使用: permission_classes=[MyPermission,] 全局使用: REST_FRAMEWORK={ 'DEFAULT_PERMISSION_CLASSES':['utils.common.MyPermission',] }
寫一個類: from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope = 'xxx' def get_cache_key(self, request, view): return self.get_ident(request) 在setting裏配置: 'DEFAULT_THROTTLE_RATES':{ 'xxx':'5/h', } 局部使用 throttle_classes=[VisitThrottle,] 全局使用 REST_FRAMEWORK={ 'DEFAULT_THROTTLE_CLASSES':['utils.common.MyPermission',] }
只有認證經過的用戶才能訪問指定的url地址,好比:查詢課程信息,須要登陸以後,沒有登陸,就不能查看,這時候須要認證組件數據庫
class UserInfo(models.Model): name=models.CharField(max_length=32) pwd=models.CharField(max_length=32) ss=((1,'超級用戶'),(2,'普通用戶'),(3,'二逼用戶')) type=models.IntegerField(choices=ss,null=True) class UserToken(models.Model): user=models.OneToOneField(to='UserInfo') token=models.CharField(max_length=64)
from django.shortcuts import render,HttpResponse # Create your views here. import json from rest_framework.views import APIView from app01 import models from utils.common import * from rest_framework.response import Response #登陸類 class Login(APIView): def post(self,request,*args,**kwargs): #實例化響應狀態函數(添加登陸成功後的狀態信息) response=MyResponse() #判斷用戶名、密碼是否正確 name=request.data.get('name') pwd=request.data.get('pwd') user=models.UserInfo.objects.filter(name=name,pwd=pwd).first() #若是登陸成功生成一個隨機字符串 if user: #生成一個隨機字符串 token=get_token(name) #token表裏面的信息,若是不存在,會建立,若是存在會更新token值(由於進行了隨機時間加鹽),使用的是update_or_create ret=models.UserToken.objects.update_or_create(user=user,defaults={'token':token}) # ret=models.UserInfo.objects.update_or_create(id=1,defaults={'token':token}) 不是隻能寫user或者能夠寫id response.status=100 response.msg='登陸成功' response.token=token print(response.get_dic()) else: response.msg="用戶名密碼錯誤" # response.data='ddd' 最後response.get_dic(),均可以把這些信息返回 #裏面須要傳入個字典 return Response(response.get_dic()) #查看課程類 class Course(APIView): #局部登陸認證 authentication_classes = [MyAuth,] #局部權限認證 permission_classes = [Mypermission,] #局部頻率認證 throttle_classes = [VisitThrottle,] def get(self,request): print(request.user) print(request.auth) return HttpResponse(json.dumps({'name':'python'}))
#url from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^course/', views.Course.as_view()), url(r'^login/', views.Login.as_view()), ] #settings #全局使用認證 REST_FRAMEWORK={ #登陸認證,全局的登陸認證剛開始不要加,若是添加後初始登陸也會要求認證,那時數據庫尚未數據,會有問題(因此能夠在認證類裏面設置局部的認證並設置爲空) # 'DEFAULT_AUTHENTICATION_CLASSES':['utils.common.MyAuth',], #頻率 'DEFAULT_THROTTLE_RATES':{ # 'xxx':'5/m', 'xxx':'5/hdddddddddddddddddddddddddddddd', } } 例如解決全局配置後login初始登陸會驗證的問題: class Login(APIView): #爲了能夠在全局裏面統一設置,並不影響初始這裏的登陸 authentication_classes=[] def post(self,request,*args,**kwargs): ... ...
登陸狀態信息django
#帳戶登陸狀態信息 class MyResponse(): def __init__(self): #1001表示失敗 self.status = 1001 self.msg = None def get_dic(self): #返回是字典格式的屬性信息 return self.__dict__
隨機驗證碼:json
#傳入用戶名生成一個隨機驗證碼token def get_token(name): md = hashlib.md5() md.update(name.encode('utf-8')) md.update(str(time.time()).encode('utf-8')) return md.hexdigest()
認證組件類:瀏覽器
from rest_framework.authentication import BaseAuthentication from app01 import models from rest_framework.exceptions import APIException,AuthenticationFailed #認證組件 class MyAuth(BaseAuthentication): def authenticate(self, request): #拿到的是隨機token的值 token = request.query_params.get('token') ret = models.UserToken.objects.filter(token=token).first() if ret: #認證經過 return ret.user,ret else: #認證失敗 raise AuthenticationFailed('認證失敗')
權限組件類:服務器
#權限組件 from rest_framework.permissions import BasePermission class Mypermission(BasePermission): message = '不是超級用戶,查看不了' def has_permission(self, request, view): token = request.query_params.get('token') ret = models.UserToken.objects.filter(token=token).first() #拿到userinfo表格對應的type字段,輸出models裏面userinfo表格裏面type數字對應的「超級用戶」等字符串信息 print(ret.user.get_type_display()) if ret.user.type==1: return True #若是是Flase的狀況就會,打印上面的message信息 else: return False
頻率組件類:app
#頻率組件 from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope = 'xxx' def get_cache_key(self, request, view): return self.get_ident(request)
運行效果展現:ide
準備工做:函數
1.userinfo數據表先添加2條數據,作演示post
狀況1:未登陸狀況下發送get請求,會提示認證失敗(由於沒有帶token信息)
狀況2:post請求登陸login,用戶信息寫入數據庫,同時生成將出入的name+time鹽,寫入UserToken表token字段
狀況3:get請求攜帶token字符串,從而驗證權限認證、頻率認證
自定義頻率類:
自定義邏輯
1
2
3
4
5
|
#(1)取出訪問者ip
# (2)判斷當前ip不在訪問字典裏,添加進去,而且直接返回True,表示第一次訪問,在字典裏,繼續往下走
# (3)循環判斷當前ip的列表,有值,而且當前時間減去列表的最後一個時間大於60s,把這種數據pop掉,這樣列表中只有60s之內的訪問時間,
# (4)判斷,當列表小於3,說明一分鐘之內訪問不足三次,把當前時間插入到列表第一個位置,返回True,順利經過
# (5)當大於等於3,說明一分鐘內訪問超過三次,返回False驗證失敗
|
class MyThrottles(): VISIT_RECORD = {} def __init__(self): self.history=None def allow_request(self,request, view): #(1)取出訪問者ip # print(request.META) ip=request.META.get('REMOTE_ADDR') import time ctime=time.time() # (2)判斷當前ip不在訪問字典裏,添加進去,而且直接返回True,表示第一次訪問 if ip not in self.VISIT_RECORD: self.VISIT_RECORD[ip]=[ctime,] return True self.history=self.VISIT_RECORD.get(ip) # (3)循環判斷當前ip的列表,有值,而且當前時間減去列表的最後一個時間大於60s,把這種數據pop掉,這樣列表中只有60s之內的訪問時間, while self.history and ctime-self.history[-1]>60: self.history.pop() # (4)判斷,當列表小於3,說明一分鐘之內訪問不足三次,把當前時間插入到列表第一個位置,返回True,順利經過 # (5)當大於等於3,說明一分鐘內訪問超過三次,返回False驗證失敗 if len(self.history)<3: self.history.insert(0,ctime) return True else: return False def wait(self): import time ctime=time.time() return 60-(ctime-self.history[-1])
經過DRF的視圖組件,數據接口邏輯被咱們優化到最後剩下一個類,接下來,咱們使用DRF的url控制器來幫助咱們自動生成url,使用步驟以下:
from django.urls import re_path, include # 1.導入模塊 from rest_framework import routers from serializer import views # 2.實例化一個router對象 router = routers.DefaultRouter() # 3.將須要自動生成url的接口註冊到router中 router.register('books', views.BookView) # 4.生成url urlpatterns = [ re_path('^', include(router.urls)) ]
以前咱們使用DRF的Response類來將數據響應給客戶端,不論是POSTMAN仍是瀏覽器,都能瀏覽到通過格式化後的漂亮的數據,DRF是怎麼作的呢?其實就是經過響應器組件。
若是咱們不須要使用DRF提供給瀏覽器的格式化後的數據,只須要禁止該響應方式便可:
from rest_framework.renderers import JSONRenderer class BookView(ModelViewSet): renderer_classes = [JSONRenderer] queryset = Book.objects.all() serializer_class = BookSerializer
這樣,瀏覽器再次訪問,接收到的就是普通的json格式數據,而不是通過DRF格式化後的數據,renderer_classes的查找邏輯與以前的解析器等組件是徹底同樣的。
爲了服務器性能考慮,也爲了用戶體驗,咱們不該該一次將全部的數據從數據庫中查詢出來,返回給客戶端瀏覽器,若是數據量很是大,這對於服務器來說,能夠說是性能災難,而對於用戶來說,加載速度將會很是慢。
因此,咱們須要控制每次返回給客戶端多少數據,這就須要用到分頁器。
# 1.導入模塊 from rest_framework.pagination import PageNumberPagination # 自定義分頁器類,繼承PageNumberPagination class MyPagination(PageNumberPagination): page_size = 3 page_query_param = 'p' page_size_query_param = 'size' max_page_size = 5 class BookView(APIView): def get(self, request): # 2.獲取數據 queryset = Book.objects.all() # 3.建立分頁器對象 paginater = MyPagination() # 4.開始分頁 paged_books = paginater.paginate_queryset(queryset, request) # 5.將分頁後的數據進行序列化 serializer_books = BookSerializer(paged_books, many=True) # 6.返回數據 return Response(serializer_books.data)
page_size:用來控制每頁顯示多少條數據(全局參數名爲PAGE_SIZE);
page_query_param:用來提供直接訪問某頁的數據;
page_size_query_param:臨時調整當前顯示多少條數據;
max_page_size:控制page_size_query_param參數能調整的最大條數;
from rest_framework.viewsets import ModelViewSet from rest_framework.pagination import PageNumberPagination # 自定義分頁器類 class MyPagination(PageNumberPagination): page_size = 3 page_query_param = 'p' page_size_query_param = 'size' max_page_size = 5 class BookView(ModelViewSet): pagination_class = MyPagination # 指定分頁器類 queryset = Book.objects.all() serializer_class = BookSerializer
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 # 每頁數目 }