昨日回顧
#1 book 其實是5個表(自動生成了一個),
-一對一關系,其實是Forainkey,unique
-on_delete:級聯刪除,設置為空,什么都不干,設置成默認值
-字段建索引,字段唯一
-聯合索引,聯合唯一
-日期類型 auto_now 和 auto_now_add
-基表 abstract
#2 book
-單條查詢,多條查詢
-單條增,多條增(生成序列化對象,many=True)
-單條修改,多條修改(BookListSerializer:重寫了update方法)
-單刪,群刪(is_delete),統一用群刪 pk__in=[1,2,3]
# 3 頻率
-自定義頻率(ip,user_id)
-繼承SimpleRateThrottle
-重寫get_cache_key,返回什么就以什么為key進行限制
-scope字段,需要與setting中對應
#4 分頁
-PageNumberPagination,基本分頁
-每頁顯示大小
-get請求路徑中查詢的key
-get請求路徑中每頁顯示條數
-每頁最大顯示多少條
-LimitOffsetPagination,
# default_limit = 3 # 每頁條數
# limit_query_param = 'limit' # 往后拿幾條
# offset_query_param = 'offset' # 標桿
# max_limit = 5 # 每頁最大幾條
-CursorPagination
cursor_query_param = 'cursor' # 每一頁查詢的key
page_size = 2 #每頁顯示的條數
ordering = '-id' #排序字段-
今日內容
1 自定制頻率
# 自定制頻率類,需要寫兩個方法
-# 判斷是否限次:沒有限次可以請求True,限次了不可以請求False
def allow_request(self, request, view):
-# 限次后調用,顯示還需等待多長時間才能再訪問,返回等待的時間seconds
def wait(self):
# 代碼
import time
class IPThrottle():
#定義成類屬性,所有對象用的都是這個
VISIT_DIC = {}
def __init__(self):
self.history_list=[]
def allow_request(self, request, view):
'''
#(1)取出訪問者ip
#(2)判斷當前ip不在訪問字典里,添加進去,並且直接返回True,表示第一次訪問,在字典里,繼續往下走
#(3)循環判斷當前ip的列表,有值,並且當前時間減去列表的最后一個時間大於60s,把這種數據pop掉,這樣列表中只有60s以內的訪問時間,
#(4)判斷,當列表小於3,說明一分鍾以內訪問不足三次,把當前時間插入到列表第一個位置,返回True,順利通過
#(5)當大於等於3,說明一分鍾內訪問超過三次,返回False驗證失敗
'''
ip=request.META.get('REMOTE_ADDR')
ctime=time.time()
if ip not in self.VISIT_DIC:
self.VISIT_DIC[ip]=[ctime,]
return True
self.history_list=self.VISIT_DIC[ip] #當前訪問者時間列表拿出來
while True:
if ctime-self.history_list[-1]>60:
self.history_list.pop() # 把最后一個移除
else:
break
if len(self.history_list)<3:
self.history_list.insert(0,ctime)
return True
else:
return False
def wait(self):
# 當前時間,減去列表中最后一個時間
ctime=time.time()
return 60-(ctime-self.history_list[-1])
#全局使用,局部使用
# SimpleRateThrottle源碼分析
def get_rate(self):
"""
Determine the string representation of the allowed request rate.
"""
if not getattr(self, 'scope', None):
msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
self.__class__.__name__)
raise ImproperlyConfigured(msg)
try:
return self.THROTTLE_RATES[self.scope] # scope:'user' => '3/min'
except KeyError:
msg = "No default throttle rate set for '%s' scope" % self.scope
raise ImproperlyConfigured(msg)
def parse_rate(self, rate):
"""
Given the request rate string, return a two tuple of:
<allowed number of requests>, <period of time in seconds>
"""
if rate is None:
return (None, None)
#3 mmmmm
num, period = rate.split('/') # rate:'3/min'
num_requests = int(num)
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
return (num_requests, duration)
def allow_request(self, request, view):
if self.rate is None:
return True
#當前登錄用戶的ip地址
self.key = self.get_cache_key(request, view) # key:'throttle_user_1'
if self.key is None:
return True
# 初次訪問緩存為空,self.history為[],是存放時間的列表
self.history = self.cache.get(self.key, [])
# 獲取一下當前時間,存放到 self.now
self.now = self.timer()
# Drop any requests from the history which have now passed the
# throttle duration
# 當前訪問與第一次訪問時間間隔如果大於60s,第一次記錄清除,不再算作一次計數
# 10 20 30 40
# self.history:[10:23,10:55]
# now:10:56
while self.history and self.now - self.history[-1] >= self.duration:
self.history.pop()
# history的長度與限制次數3進行比較
# history 長度第一次訪問0,第二次訪問1,第三次訪問2,第四次訪問3失敗
if len(self.history) >= self.num_requests:
# 直接返回False,代表頻率限制了
return self.throttle_failure()
# history的長度未達到限制次數3,代表可以訪問
# 將當前時間插入到history列表的開頭,將history列表作為數據存到緩存中,key是throttle_user_1,過期時間60s
return self.throttle_success()
2 自動生成接口文檔
# 1 安裝:pip install coreapi
# 2 在路由中配置
from rest_framework.documentation import include_docs_urls
urlpatterns = [
...
path('docs/', include_docs_urls(title='站點頁面標題'))
]
#3 視圖類:自動接口文檔能生成的是繼承自APIView及其子類的視圖。
-1 ) 單一方法的視圖,可直接使用類視圖的文檔字符串,如
class BookListView(generics.ListAPIView):
"""
返回所有圖書信息.
"""
-2)包含多個方法的視圖,在類視圖的文檔字符串中,分開方法定義,如
class BookListCreateView(generics.ListCreateAPIView):
"""
get:
返回所有圖書信息.
post:
新建圖書.
"""
-3)對於視圖集ViewSet,仍在類視圖的文檔字符串中封開定義,但是應使用action名稱區分,如
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
"""
list:
返回圖書列表數據
retrieve:
返回圖書詳情數據
latest:
返回最新的圖書數據
read:
修改圖書的閱讀量
"""
3 JWT
jwt=Json Web token
#原理
"""
1)jwt分三段式:頭.體.簽名 (head.payload.sgin)
2)頭和體是可逆加密,讓服務器可以反解出user對象;簽名是不可逆加密,保證整個token的安全性的
3)頭體簽名三部分,都是采用json格式的字符串,進行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
4)頭中的內容是基本信息:公司信息、項目組信息、token采用的加密方式信息
{
"company": "公司信息",
...
}
5)體中的內容是關鍵信息:用戶主鍵、用戶名、簽發時客戶端信息(設備號、地址)、過期時間
{
"user_id": 1,
...
}
6)簽名中的內容時安全信息:頭的加密結果 + 體的加密結果 + 服務器不對外公開的安全碼 進行md5加密
{
"head": "頭的加密字符串",
"payload": "體的加密字符串",
"secret_key": "安全碼"
}
"""
校驗
"""
1)將token按 . 拆分為三段字符串,第一段 頭加密字符串 一般不需要做任何處理
2)第二段 體加密字符串,要反解出用戶主鍵,通過主鍵從User表中就能得到登錄用戶,過期時間和設備信息都是安全信息,確保token沒過期,且時同一設備來的
3)再用 第一段 + 第二段 + 服務器安全碼 不可逆md5加密,與第三段 簽名字符串 進行碰撞校驗,通過后才能代表第二段校驗得到的user對象就是合法的登錄用戶
"""
drf項目的jwt認證開發流程(重點)
"""
1)用賬號密碼訪問登錄接口,登錄接口邏輯中調用 簽發token 算法,得到token,返回給客戶端,客戶端自己存到cookies中
2)校驗token的算法應該寫在認證類中(在認證類中調用),全局配置給認證組件,所有視圖類請求,都會進行認證校驗,所以請求帶了token,就會反解出user對象,在視圖類中用request.user就能訪問登錄的用戶
注:登錄接口需要做 認證 + 權限 兩個局部禁用
"""
# 第三方寫好的 django-rest-framework-jwt
# 安裝pip install djangorestframework-jwt
# 新建一個項目,繼承AbstractUser表()
# 創建超級用戶
# 簡單使用
#urls.py
from rest_framework_jwt.views import ObtainJSONWebToken,VerifyJSONWebToken,RefreshJSONWebToken,obtain_jwt_token
path('login/', obtain_jwt_token),
自定制auth認證類
from rest_framework_jwt.authentication import BaseAuthentication,BaseJSONWebTokenAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.authentication import get_authorization_header,jwt_get_username_from_payload
from rest_framework import exceptions
class MyToken(BaseJSONWebTokenAuthentication):
def authenticate(self, request):
jwt_value=str(request.META.get('HTTP_AUTHORIZATION'))
# 認證
try:
payload = jwt_decode_handler(jwt_value)
except Exception:
raise exceptions.AuthenticationFailed("認證失敗")
user=self.authenticate_credentials(payload)
return user,None
#局部使用,全局使用
補充
1 函數顯示傳參類型和返回值類中
作業
1 什么是集群,什么是分布式
作業:
1 自定義User表,新增mobile唯一約束字段;新增icon圖片字段
2 在自定義User表基礎上,用 GenericViewSet + CreateModelMixin + serializer 完成User表新增接口(就是注冊接口)(重要提示:序列化類要重寫create方法,不然密碼就是明文了)
3 在自定義User表基礎上,用 GenericViewSet + RetrieveModelMixin + serializer 完成User表單查(就是用戶中心)
4 在自定義User表基礎上,用 GenericViewSet + UpdateModelMixin + serializer 完成用戶頭像的修改