1. 程式人生 > 實用技巧 >drf路由與認證

drf路由與認證

目錄

一、路由

三種路由配置

1 沒有繼承檢視集的檢視類

# urls.py
path('books4/', views.Book4View.as_view()),
re_path('books4/(?P<pk>\d+)', views.Book4DetailView.as_view())

2 繼承了檢視集的檢視類

# urls.py
path('books5/', views.Book5View.as_view(actions={'get':'list','post':'create'})), #當路徑匹配,又是get請求,會執行Book5View的list方法
re_path('books5/(?P<pk>\d+)', views.Book5View.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),
# 這裡的方法名可以修改

3 繼承自ModelViewSet的路由寫法(自動生成)

# 1
# 匯入routers類
from rest_framework import routers # 2
# 例項化得到routers物件
# 這種比下面simple多幾個沒用的路由
# router = routers.DefaultRouter()
# 只有兩個路由,一個是帶pk引數,一個不帶引數
# ^student4/$ [name='student-list'] student4/
# ^student4/(?P<pk>[^/.]+)/$ [name='student-detail'] student4/4/
router = routers.SimpleRouter() # 3
# 註冊 router.register('字首','繼承自ModelViewSet檢視類','別名')
router.register('student4',views.Students4API) # 4
urlpatterns+=router.urls

action引數

# 當我們需要自定義方法名也能自動生成路由的時候
from rest_framework.decorators import action
# methods:放一個需要對於的請求列表
# detail=True 帶pk引數,False 不帶pk引數
@action(methods=['GET'],detail=True)
def get_xxx(self,request,pk):
return Response({'pk':pk})

二、認證

1 drf認證的原始碼分析

# 分析APIView重寫的dispatch方法,發現它添加了一個initial方法,這個方法內涵了drf的三大校驗:認證,許可權,頻率
# 點進去這個方法,刪掉沒用的,發現最後就呼叫了這三個方法,第一個是認證的方法
def initial(self, request, *args, **kwargs):
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
# perform_authentication內部只有一個程式碼就是request.user
# 這裡要注意,APIView重寫了request,所以這裡看到的request實際上是drf寫的
# 所以我們在找user的時候要去drf的Request類中
# 最後發現user原來是一個被封裝成屬性的方法
@property
def user(self):
# 如果request沒有_user屬性,執行self._authenticate()
# 到Request的__init__方法看,果然沒有
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
# 最後我們點到_authenticate()方法,再展開這個方法前,我們需要找到其中最關鍵的引數authenticators
# 先去例項化的物件裡,也就是dispatch內定義的request找
# 是通過一個initialize_request方法例項化了request
# 在這裡發現了我們要找的authenticators
# 是呼叫了get_authenticators得到的
# 內部返回了一個[auth() for auth in self.authentication_classes]
# 這是一個列表生成式,迴圈self.authentication_classes,物件內沒有這個屬性,去Request類中找
# 發現他的來源是api_settings.xxx
# 這裡就不再展開了,只要知道這是一個配置的常量,內部是一個列表,存放著我們要認證的類
# 查詢順序是先從檢視類(區域性),其次專案settings(全域性),最後apisettings(預設)
# 只要在一個地方配置了,就會向下覆蓋
# 再回到這個列表生成式,是呼叫了存放在列表中的認證類,在把例項化的物件放入新的列表authenticators
def _authenticate(self):
# 迴圈拿到的是一個個認證物件
for authenticator in self.authenticators:
try:
# 呼叫了認證物件的authenticate,把request傳進去
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
# 如果沒有這個方法,就會拋異常,所以從這裡可以知道,如果要自定義認證類,就必須重寫類中的authenticate方法
# 並按固定格式返回資料
self._not_authenticated()
raise
# 把返回的引數賦值給request,第一個引數預設必須是使用者,第二個隨便
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
# 如果返回值user_auth_tuple為空,代表認證通過,但是沒有登陸使用者與登陸認證資訊,代表遊客
self._not_authenticated()

2 自定義認證類的使用

# 自定義認證類
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class TokenAuthentication(BaseAuthentication):
def authenticate(self,request):
token = request.GET.get('token')
username = request.GET.get('username')
obj = UserToken.objects.filter(token=token)
user = User.objects.filter(username=username).first()
if obj:
return user,obj
else:
raise AuthenticationFailed('認證失敗')

view.py

class Students2API(GenericAPIView,ListModelMixin,CreateModelMixin):
queryset = models.Student.objects
serializer_class = ser.StudentModelSerializer
authentication_classes = [TokenAuthentication]
# 區域性配置
def get(self,request):
return self.list(request)
def post(self,request):
return self.create(request)
class Student2API(GenericAPIView,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin):
queryset = models.Student.objects
serializer_class = ser.StudentModelSerializer
def put(self,request,pk):
return self.update(request,pk)
def get(self,request,pk):
return self.retrieve(request,pk)
def delete(self,request,pk):
return self.destroy(request,pk) class UserAPI(APIView):
# 登入檢視
def post(self,request):
username = request.data.get('username')
password = request.data.get('password')
user_obj = models.User.objects.filter(username=username,password=password).first()
back_dic = {'code':200,'msg':'登入成功'}
if user_obj:
# 登入成功
import uuid
token = uuid.uuid4()
models.UserToken.objects.update_or_create(user=user_obj,defaults={'token':token})
back_dic['user'] = username
back_dic['token'] = token
else:
back_dic['msg'] = '賬號或密碼錯誤'
return Response(back_dic)