目錄
- 一、認證組件
- (1)、登錄介面
- (2)、認證組件使用步驟
- (3)、整體代碼
- 權限組件
- (1)、權限組件的使用步驟
- (2)、代碼用法
- 三、頻率組件
- (1)、頻率組件的使用步驟
- (2)、代碼用法
- 四、過濾的多種用法
- (1)、繼承APIView自己寫
- (2)、使用drf的內置過濾(繼承GenericAPIview)
- (3)、使用第三方插件過濾(精準過濾)
- 4、使用過濾組件
- 五、排序的使用
- (2)、代碼用法
- 六、分頁
一、認證組件
簡介:
-
登錄認證的限制
-
認證組件是drf框架給我們提供的認證介面,它能夠在請求進入視圖函式/類前進驗證(例如:認證用戶是否登錄),對不符合認證的請求進行攔截并回傳校驗失敗的資訊
(1)、登錄介面
# 認證是基于登錄的介面上面操作的 所以前戲撰寫一個簡單的登錄介面
models.py
class User(models.Model): # 簡易的用戶資訊賬號密碼
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
def __str__(self):
return self.username
'跟User表是一對一外鍵關聯,存盤用戶登錄狀態用的 [這個表可以沒有,如果沒有,把欄位直接寫在User表上也可以]'
class UserToken(models.Model): # 用戶資訊登錄記錄表
user = models.OneToOneField(to='User', on_delete=models.CASCADE) # 一對一關聯
token = models.CharField(max_length=32, null=True) # 如果用戶沒有登錄則沒有值 如果登錄則有值
views.py
'登錄介面功能:自動生成路由+登錄功能,不用序列化,因此繼承ViewSet即可'
class UserView(ViewSet):
@action(methods=['POST'], detail=False, url_path='login', url_name='login')
def login(self, request):
username = request.data.get('username') # 獲取用戶名與密碼
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first() # 比對用戶名與密碼
if user:
token = str(uuid.uuid4())
# uuid4 隨機獲得永不重復的字串 機制跟Cookie中的驗證碼一樣
# 在userToken表中存盤一下:1 從來沒有登錄過,插入一條, 2 登錄過,修改記錄
UserToken.objects.update_or_create(defaults={'token': token}, user=user)
# 通過user去UserToken表中查資料,如果能查到,使用defaults的資料更新,如果查不到,直接通過user和defaults的資料新增
# kwargs 傳入的東西查找,能找到,使用defaults的更新,否則新增一條
return Response({'code': 100, 'msg': '登錄成功', 'token': token})
else:
return Response({'code': 101, 'msg': '用戶名或密碼錯誤'})
urls.py
from rest_framework.routers import SimpleRouter, DefaultRouter
router = SimpleRouter()
router.register('users', views.UserView, 'users')
urlpatterns += router.urls
'''這個時候一個簡單的登錄介面就寫好了 每次登錄都會更新Token 相當于登錄了之前的設備就無效了 '''

update_or_create原始碼如下:
def update_or_create(self, defaults=None, **kwargs):
defaults = defaults or {}
self._for_write = True
with transaction.atomic(using=self.db):
try:
obj = self.select_for_update().get(**kwargs)
except self.model.DoesNotExist:
params = self._extract_model_params(defaults, **kwargs)
obj, created = self._create_object_from_params(kwargs, params, lock=True)
if created:
return obj, created
for k, v in defaults.items():
setattr(obj, k, v() if callable(v) else v)
obj.save(using=self.db)
return obj, False
(2)、認證組件使用步驟
1.需要寫一個認證類,因此我們需要在應用中另外創建一個py檔案撰寫認證類,需要繼承
BaseAuthentication這個類
- 通過查看原始碼我們可以發現有個
authenticate方法需要我們重寫,否則就會報錯,這就是我們需要撰寫認證功能的類
class BaseAuthentication:
def authenticate(self, request):
raise NotImplementedError(".authenticate() must be overridden.")
def authenticate_header(self, request):
pass
2.重寫
authenticate方法,在該方法在中實作登錄認證
-
token在哪帶的?如何認證它是登錄了的? -
用
token來判斷是否登陸,登陸了在訪問的時候帶上token,目前階段我們直接在地址欄中攜帶token的資料,后面可以在請求頭中添加token的資料
3、如果認證成功,回傳兩個值【回傳
None或兩個值(固定的:當前登錄用戶,token)】
4、認證不通過,用
AuthenticationFailed類拋例外
代碼如下:
authenticate.py(認證類)
# 自己寫的認證類,繼承某個類
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserToken
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# 在這里實作認證,如果是登錄的,繼續往后走回傳兩個值,如果不是拋例外
# 請求中是否攜帶token,判斷是否登錄,放在地址欄中
token = request.query_params.get('token', None) # 查找是否有token這個變數名的值,如果沒有就回傳None,默認好像回傳的是數字
if token: # 前端傳入token了,去表中查,如果能查到,登錄了,回傳兩個值[固定的:當前登錄用戶,token]
user_token = UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user, token
else:
# 沒有登錄拋例外
raise AuthenticationFailed('token認證失敗')
else:
raise AuthenticationFailed('token沒傳')
# 前端傳入的請求頭中的資料從哪取? GET,body,POST,data
5、認證類的使用
- 當我們撰寫好了認證類中的認證代碼,接著就需要匯入到視圖層然后使用他
from rest_framework.generics import ListAPIView, RetrieveAPIView
from rest_framework.viewsets import ViewSetMixin
from .authenticate import LoginAuth
# 查詢所有
class BookView(ViewSetMixin, ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
class BookDetailView(ViewSetMixin, RetrieveAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
authentication_classes = [LoginAuth] # 需要寫一個認證類,需要咱們自行撰寫
6、區域使用和全域使用
- 區域使用:只在某個視圖類中使用【當前視圖類管理的所有介面】
class BookDetailView(ViewSetMixin, RetrieveAPIView):
authentication_classes = [LoginAuth]
- 全域使用:在組態檔
settings.py中撰寫,全域所有介面都生效
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES':['app01.authenticate.LoginAuth']
}
注意事項:不要在組態檔中亂匯入不使用的東西,否則會報錯,但是在匯入類似認證類這樣的檔案時,可以寫上匯入的代碼然后再修改,最后寫進配置中,這樣可以減少錯誤
- 區域禁用:(登陸介面很明顯是不需要校驗是否登陸的,因此有了這個區域禁用的需求,我們把他的
authentication_classes配置成空就是區域禁用)
class BookDetailView(ViewSetMixin, RetrieveAPIView):
authentication_classes = []
7、測驗路由參考

(3)、整體代碼
views.py
# 查詢所有
class BookView(ViewSetMixin, ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 查詢單個
class BookDetailView(ViewSetMixin, RetrieveAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
authentication_classes = [LoginAuth] # 需要寫一個認證類,需要咱們自行撰寫
authenticate.py(認證類)
# 自己寫的認證類,繼承某個類
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserToken
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# 在這里實作認證,如果是登錄的,繼續往后走回傳兩個值,如果不是拋例外
# 請求中是否攜帶token,判斷是否登錄,放在地址欄中
token = request.query_params.get('token', None) # 查找是否有token這個變數名的值,如果沒有就回傳None,默認好像回傳的是數字
if token: # 前端傳入token了,去表中查,如果能查到,登錄了,回傳兩個值[固定的:當前登錄用戶,token]
user_token = UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user, token
else:
# 沒有登錄拋例外
raise AuthenticationFailed('token認證失敗')
else:
raise AuthenticationFailed('token沒傳')
# 前端傳入的請求頭中的資料從哪取? GET,body,POST,data
urls.py
from django.contrib import admin
from django.urls import path, include
from app01 import views
from rest_framework.routers import SimpleRouter
router = SimpleRouter() # 后面這個少的用的多,
router.register('user', views.UserView, 'user')
router.register('books', views.BookView, 'books')
router.register('books', views.BookDetailView, 'books')
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include(router.urls)),
]
權限組件
簡介:
- 在我們使用的一些
app或者網頁中(愛奇藝,騰訊視頻),都會有一些會員介面(需要購買會員才能夠使用或者觀看),權限組件就是對用戶的這一權限進行驗證,在請求進入視圖類/函式代碼前進行校驗,校驗失敗后直接將請求攔截,并回傳校驗失敗的資訊
(1)、權限組件的使用步驟
模塊地址:
from rest_framework.permissions import BasePermission
用法簡介:
# 1、創建一個專門用于撰寫權限組件的py檔案,寫一個權限類,繼承BasePermission
# 2、重寫has_permission方法(在該方法在中實作權限認證,在這方法中,request.user就是當前登錄用戶)
# 3、如果有權限,回傳True
# 4、沒有權限,回傳False(定制回傳的中文: self.message='中文')
# 5、區域使用和全域使用
-區域使用: # 在某個視圖類中設定介面(不會影響別的視圖類)
class BookDetailView(ViewSetMixin, RetrieveAPIView):
permission_classes = [CommonPermission]
-全域使用: # django的settings.py中配置,影響全域
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'app01.permissions.CommonPermission',
],
}
-區域禁用:# 全域配置區域禁用
class BookDetailView(ViewSetMixin, RetrieveAPIView):
permission_classes = []
(2)、代碼用法
models.py(修改User表的配置后需要重新進行資料庫遷移)
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
user_type = models.IntegerField(choices=((1, '超級管理員'), (2, '普通用戶'), (3, '2B用戶')), default=2)
perssion.py(權限類代碼)
# 寫權限類,寫一個類,繼承基類BasePermission,重寫has_permission方法,在方法中實作權限認證,如果有權限return True ,如果沒有權限,回傳False
from rest_framework.permissions import BasePermission
class CommonPermission(BasePermission):
def has_permission(self, request, view):
# 實作權限的控制 ---》知道當前登錄用戶是誰?當前登錄用戶是 request.user
if request.user.user_type == 1:
return True
else:
# 沒有權限,向物件中放一個屬性 message
# 如果表模型中,使用了choice,就可以通過 get_欄位名_display() 拿到choice對應的中文
self.message = '您是【%s】,您沒有權限' % request.user.get_user_type_display()
return False
views.py(視圖類代碼)
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 區域認證
authentication_classes = [LoginAuth]
# 權限認證(將撰寫的頻率類匯入過來)
permission_classes = [CommentPermission]

三、頻率組件
簡介:
- 頻率是指,控制某個介面訪問頻率(次數)
(1)、頻率組件的使用步驟
模塊地址:
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
# BaseThrottle:需要手動撰寫的代碼較多
# SimpleRateThrottle: 需要手動撰寫的代碼較少(用這個)
用法簡介
# 1、創建一個專門用來撰寫頻率組件的py檔案,寫一個頻率類,繼承SimpleRateThrottle
# 2、重寫get_cache_key方法,回傳什么,就以什么做限制----》ip(用戶id做限制)
# 3、配置一個類屬性scope = 'book_5_m'
# 4、在django的組態檔中撰寫頻率次數
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'book_5_m': '5/m', # 一分鐘五次
},
}
# 5、區域使用和全域使用
-區域使用: # 只影響當前的視圖類
class BookView(ModelViewSet):
throttle_classes = [CommentThrottle]
-全域配置:影響全域
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],
}
-區域禁用:
class BookView(ModelViewSet):
throttle_classes = [CommentThrottle]
(2)、代碼用法
throttle.py(頻率類代碼)
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class CommentThrottle(SimpleRateThrottle):
# 創建一個用于控制頻率的變數名(需要傳入組態檔)
scope = 'book_5_m'
def get_cache_key(self, request, view):
# 回傳什么就以什么做限制(request.META.get('REMOTE_ADDR')以IP做限制)
return request.META.get('REMOTE_ADDR')
視圖類代碼
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 頻率組件
throttle_classes = [CommentThrottle]
django組態檔
REST_FRAMEWORK = {
# 控制訪問頻率
'DEFAULT_THROTTLE_RATES': {
'book_5_m': '5/m', # 一分鐘五次
},
}

四、過濾的多種用法
簡介:
- 過濾是指在使用查詢的時候,我們可以通過條件來過濾掉不需要的內容
# restful規范中,要求了,請求地址中帶過濾條件
-5個介面中,只有一個介面需要有過濾和排序,查詢所有介面
(1)、繼承APIView自己寫
class BookView(APIView):
def get(self,request):
# 獲取get請求攜帶的引數
name=request.query_params.get('name')
# 通過filter進行過濾
books = Book.objects.filter(name=name)
(2)、使用drf的內置過濾(繼承GenericAPIview)
模塊地址:
該方法為模糊查詢
from rest_framework.filters import SearchFilter
代碼用法:
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 實體化過濾物件
filter_backends = [SearchFilter]
# 指定過濾的欄位(模糊查詢)
search_fields = ['name', 'price']
搜索方式:
# name或price中只要有關鍵字就會搜出來 (只能用search=xxx的方式)
http://127.0.0.1:8000/api/v1/books/?search=西游記

(3)、使用第三方插件過濾(精準過濾)
第三方插件:
# 插件名稱:
django-filter
# 安裝插件:
pip3.8 install django-filter
模塊地址:
from django_filters.rest_framework import DjangoFilterBackend
代碼用法:
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializer import BookSerializer
from django_filters.rest_framework import DjangoFilterBackend
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 第三方過濾插件
filter_backends = [DjangoFilterBackend]
# 查詢的欄位
filterset_fields = ['pk','name', 'price']
搜索方式:
http://127.0.0.1:8000/api/books/?price=99
http://127.0.0.1:8000/api/books/?price=99&name=吶喊

4、使用過濾組件
1.定制過濾組件的使用方式與步驟
模塊地址:
from rest_framework.filters import BaseFilterBackend
用法簡介:
# 1、創建一個專門用于過濾的py檔案,寫一個類繼承BaseFilterBackend
# 2、重寫filter_queryset方法,在方法內部進行過濾
# 3、直接回傳過濾后的物件
# 4、如果沒有過濾直接回傳所有資料
# 5、區域使用
-區域使用:
class BookView(ViewSetMixin, ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 在類表內填寫過濾的類
filter_backends = [CommonFilter] # 可以定制多個,從左往右,依次執行
2.代碼用法
過濾類代碼filters.py
from rest_framework.filters import BaseFilterBackend
class CommonFilter(BaseFilterBackend):
# 重寫的類,撰寫過濾吧的內容
def filter_queryset(self, request, queryset, view):
# 獲取過濾的條件
filter_comment = request.query_params.get('price_gt', None)
# 判斷前端是否傳入過濾條件
if filter_comment:
# 根據條件進行賽選內容
books_queryset_filter = queryset.filter(price__gt=100)
# 回傳過濾后的資料
return books_queryset_filter
return queryset
視圖類代碼
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 第三方過濾插件
filter_backends = [CommonFilter]
五、排序的使用
用法簡介
- 排序需要和自定義過濾繼承同一個父類,需要將排序的物件填入在過濾的串列內,并且放在其他引數的前方
模塊地址:
from rest_framework.filters import OrderingFilter
(2)、代碼用法
視圖類代碼
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializer import BookSerializer
from django_filters.rest_framework import DjangoFilterBackend
from .filters import CommonFilter
from rest_framework.filters import OrderingFilter
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 第三方過濾插件(OrderingFilter放在其他過濾引數前)
filter_backends = [OrderingFilter, DjangoFilterBackend, CommonFilter]
# 查詢的欄位
filterset_fields = ['id', 'name', 'price']
# 指定排序的欄位(-是降序,默認升序)
ordering_fields = ['price']
搜索用法
# 默認升序
http://127.0.0.1:8000/api/books/?price_gt=60&ordering=price
# 降序
http://127.0.0.1:8000/api/books/?price_gt=60&ordering=-price
六、分頁
- 分頁功能,只有查詢所有介面,才有分頁
- drf內置了三個分頁器,對應三種分頁方式
- 內置的分頁類不能直接使用,需要繼承,定制一些引數后才能使用
使用步驟
- 步驟一:創建一個py檔案撰寫分頁用到的自定義類,分頁的三個類并不能直接使用,需要我們進行配置
- 步驟二:撰寫這個自定義類
- 步驟三:匯入視圖類中,并添加配置
代碼
page.py(自定義的分頁類)
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
# 網頁用它
class CommonPageNumberPagination(PageNumberPagination):
page_size = 2 # 每頁顯示2條
page_query_param = 'page' # page=10 查詢第10頁的資料,每頁顯示2條
page_size_query_param = 'size' # page=10&size=5 查詢第10頁,每頁顯示5條
max_page_size = 5 # 每頁最大顯示10條
'''
page_size 每頁數目
page_query_param 前端發送的頁數關鍵字名,默認為”page”
page_size_query_param 前端發送的每頁數目關鍵字名,默認為None
max_page_size 前端最多能設定的每頁數量
'''
# LimitOffset
class CommonLimitOffsetPagination(LimitOffsetPagination):
default_limit = 3 # 每頁顯示2條
limit_query_param = 'limit' # limit=3 取3條
offset_query_param = 'offset' # offset=1 從第一個位置開始,取limit條
max_limit = 5
# offset=3&limit=2 0 1 2 3 4 5
'''
default_limit 默認限制,默認值與PAGE_SIZE設定一直
limit_query_param limit引數名,默認’limit’
offset_query_param offset引數名,默認’offset’
max_limit 最大limit限制,默認None
'''
# app 用下面
class CommonCursorPagination(CursorPagination):
cursor_query_param = 'cursor' # 查詢引數
page_size = 2 # 每頁多少條
ordering = 'id' # 排序欄位
'''
cursor_query_param:默認查詢欄位,不需要修改
page_size:每頁數目
ordering:按什么排序,需要指定
'''
views.py
# 分頁功能 必須是繼承GenericAPIView ,如果是繼承APIView,要自己寫(你寫)
from .page import CommonPageNumberPagination as PageNumberPagination
from .page import CommonLimitOffsetPagination as LimitOffsetPagination
from .page import CommonCursorPagination as CommonCursorPagination
class BookView(ViewSetMixin, ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = []
authentication_classes = []
throttle_classes = []
# 之前的東西一樣用 ,內置的分頁類不能直接使用,需要繼承,定制一些引數后才能使用
# pagination_class = PageNumberPagination
#基本分頁方式(基本是這種,網頁端):http://127.0.0.1:8000/api/v1/books/?page=2&size=3
# pagination_class = LimitOffsetPagination
# 偏移分頁 http://127.0.0.1:8000/api/v1/books/?limit=4&offset=1
# 從第一條開始,取4條
pagination_class = CommonCursorPagination
# 游標分頁,只能下一頁,上一頁,不能跳到中間,但它的效率最高,大資料量分頁,使用這種較好
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/543214.html
標籤:其他
上一篇:day04-視圖和視圖決議器
