一、認證
1、自定義認證
在前面說的 APIView 中封裝了三大認證,分別為認證、許可權、頻率。認證即登入認證,許可權表示該使用者是否有許可權訪問介面,頻率表示使用者指定時間內能訪問介面的次數。整個請求最開始的也是認證。
(1)需求
- 登陸認證
- 使用者登陸成功--》簽發token
- 以後需要登陸才能訪問的介面,必須寫到當時登陸簽發的token過來
- 後端驗證--》驗證透過--》繼續後續操作
(2)定義模型表
from django.contrib.auth.models import User
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=64)
price = models.IntegerField()
publish = models.CharField(max_length=64)
class UserInfo(AbstractUser):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
user_type=models.IntegerField(choices=((1,'註冊使用者'),(2,'普通管理員'),(3,'超級管理員')),default=1)
@property
def is_authenticated(self):
return True
class Meta:
verbose_name_plural = '使用者表'
class UserToken(models.Model):
# 用於存放uuid
token = models.CharField(max_length=64) # 存使用者登陸狀態【作用等同於session表】
user = models.OneToOneField(to='UserInfo', on_delete=models.CASCADE) # 跟User表是一對一
(3)登陸介面
import uuid
from rest_framework.viewsets import ViewSet
from django.contrib import auth
# 繼承 ViewSet,可以自動新增路由,也可以使用 action 裝飾器自定義請求方法
class LoginView(ViewSet):
# 登入功能不需要認證,設為空列表
authentication_classes = []
permission_classes = []
@action(methods=['post', ], detail=False)
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
# 驗證使用者名稱以及密碼是否正確
user = auth.authenticate(request, username=username, password=password).first()
if not user:
return Response({'code': 10001, 'msg': '使用者名稱或密碼錯誤'})
else:
token = str(uuid.uuid4())
# defaults 是需要更新的資料, user可以理解為是篩選的條件
# UserToken.objects.filter(user=user).update('token':token)
models.UserToken.objects.update_or_create(defaults={'token': token}, user=user)
return Response({'code': 10000, 'msg': '登入成功', 'token': token})
(4) 認證類
-
自定義認證表需要建立認證類,首先繼承擴充 BaseAuthentication
-
匯入
from rest_framework.authentication import BaseAuthentication
from rest_framework.authentication import BaseAuthentication
from .models import UserToken
from rest_framework.exceptions import AuthenticationFailed # 異常
'''
1 寫個類,繼承BaseAuthentication
2 重寫 authentication 方法
3 在authentication 中完成使用者登陸的校驗
-使用者攜帶token--》能查到,就是登陸了
-使用者沒帶,或查不到,就是沒登入
4 如果校驗透過,返回當前登入使用者和token
5 如果沒校驗透過,拋AuthenticationFailed
'''
class LoginAuth(BaseAuthentication):
# 重寫 BaseAuthentication 中的 authenticate 方法
def authenticate(self, request):
# request 是新的request,從request中取出使用者攜帶的token
# 使用者帶在哪了? 後端定的:能放哪? 請求地址 ?token 請求體中 請求頭中
# 放在請求頭中
token=request.META.get('HTTP_TOKEN')
# 校驗
user_token=UserToken.objects.filter(token=token).first()
if user_token:
# 返回的第一個值是當前登入使用者,第二個值是 token
user=user_token.user
return user,token
else:
raise AuthenticationFailed('很抱歉,您沒有登陸,不能操作')
(5)使用
① 區域性使用
- 區域性使用,只需要在檢視類里加入:
class BookView(GenericViewSet, ListModelMixin, CreateModelMixin, DestroyModelMixin):
# 寫個認證類,在檢視類上配置即可
authentication_classes = [LoginAuth]
② 全域性使用
- 在配置檔案中新增配置,DEFAULT_AUTHENTICATION_CLASSES 的值為列表,其包含認證類路徑
REST_FRAMEWORK = {
# 許可權認證
'DEFAULT_AUTHENTICATION_CLASSES': [
'utils.authentication.LoginAuth'
],
}
- 如果想區域性禁用,也可以實現
class UserView(ViewSet):
authentication_classes = [] # 列表為空就行了,因為區域性優先
③ 選擇使用
- 可以選擇某一些方法可以認證,在檢視類中新增 get_authenticators
def get_authenticators(self):
if self.request.method != 'GET':
return [LoginAuth(), ]
(6)總結流程
- 繼承 BaseAuthentication 類
- 重寫 authenticate 方法,方法中編寫邏輯,存在需要返回元組,為登入使用者和 token,不存在則報 AuthenticationFailed 認證異常
- 編寫好認證類可以在區域性使用,也可以在全域性使用
2、內建認證類
- SessionAuthentication 之前老的 session 認證登入方式用,後期不用
- BasicAuthentication 基本認證方式
- TokenAuthentication 使用 token 認證方式,也可以自己寫
可以在配置檔案中配置全域性預設的認證方案
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication', # session認證
'rest_framework.authentication.BasicAuthentication', # 基本認證
)
}
也可以在每個檢視中透過設定authentication_classess屬性來設定
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
...
二、許可權
登入認證成功後,還需要認證許可權,有一些介面需要指定許可權才能訪問。所以許可權需要和登入認證相關聯。每個人的許可權在表中預設設為普通使用者。
注意:如果設定了許可權,那麼必需先透過使用者認證才能進行許可權校驗。
1、自定義許可權
- 自定義許可權需要繼承 BasePermission 編寫許可權類
- 匯入語句:
from rest_framework.permissions import BasePermission
from rest_framework.permissions import BasePermission
from .models import UserToken, UserInfo
class UserPermission(BasePermission):
# message 為認證失敗提示資訊
message = ''
# 需要重寫 has_permission 方法,原方法預設返回 True
def has_permission(self, request, view):
# 獲取當前登入使用者
user = request.user
# 獲取當前使用者許可權型別
user_type = user.user_type
if user_type == 1:
# 許可權符合返回 True
return True
else:
# 許可權不符合,新增提示資訊,並返回 False
# 只要使用了choice,可以透過get_欄位名_display()拿到數字對應的中文
self.message = '你是: %s,許可權不夠' % user.get_user_type_display()
return False
2、使用
(1)區域性使用
- 區域性使用,只需要在檢視類里加入該許可權類即可:
permission_classes = [UserPermission,]
(2)全域性使用
- 全域性使用也是在配置檔案中新增
REST_FRAMEWORK = {
# 使用者認證
'DEFAULT_AUTHENTICATION_CLASSES': [
'app01.authentication.LoginAuth'
],
'DEFAULT_Permission_CLASSES': [
'utils.permission.UserPermission'
],
}
- 如果想區域性禁用,也可以實現
# 例如區域性禁用---》登入介面
class PublishView(GenericViewSet):
permission_classes = []
(3)選擇使用
- 在檢視類中新增 get_permissions 判斷如果請求方式符合就去認證
def get_permissions(self):
if self.request.method == 'DELETE':
return [UserPermission(), ]
3、總結流程
- 繼承 BasePermission 類
- 重寫 has_permission 方法,方法中編寫邏輯,有許可權返回 True,沒有許可權可以新增 message 並返回 False
- 編寫好許可權類可以在區域性使用,也可以在全域性使用
4、內建許可權類
from rest_framework.permissions import AllowAny,IsAuthenticated,IsAdminUser,IsAuthenticatedOrReadOnly
-AllowAny 允許所有使用者
-IsAdminUser 校驗是不是 auth 的超級管理員許可權
-IsAuthenticated 後面用,驗證使用者是否登入,登入後才有許可權,沒登入就沒有許可權
-IsAuthenticatedOrReadOnly 瞭解即可
(1)區域性使用
- 可以在具體的檢視中透過 permission_classes 屬性來設定,如下
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class ExampleView(APIView):
permission_classes = [IsAuthenticated,]
...
(2)全域性使用
- 也可以在配置檔案中全域性設定預設的許可權管理類,如下
REST_FRAMEWORK = {
....
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
- 如果未指明,則採用如下預設配置
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
]
三、 頻率
作用:限制介面的訪問頻率
1、 頻率類
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class CommonThrottle(SimpleRateThrottle):
rate = '3/m' # 一分鐘3次
def get_cache_key(self, request, view):
# 返回什麼,就會以什麼做限制--》ip地址限制;使用者id
return request.META.get('REMOTE_ADDR')
2、使用
(1)區域性使用
在檢視類中新增
from .throttling import CommonThrottle
class PublishView(GenericViewSet):
throttle_classes = [CommonThrottle]
(2)全域性使用
同樣,區域性禁用只要賦值空列表即可
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'app01.throttling.CommonThrottle'
],
}
3、總結流程
-
繼承 SimpleRateThrottle 類
-
重寫 get_cache_key 方法,該方法返回的值是限制的依據,例如按照 IP 地址來作為限制條件,設定每個 IP 地址訪問的次數。需要注意的是,IP 地址在 request.META 中獲取
'REMOTE_ADDR'
-
接下來需要配置 setting ,需要設定訪問的頻率。首先需要設定屬性 scope,該屬性的值會作為頻率的鍵名,在 setting 配置檔案 REST_FRAMEWORK 中的 DEFAULT_THROTTLE_RATES 配置,鍵名是 scope,鍵值是字串,格式為 'x/y',x 表示訪問的次數,y 表示訪問的時間區間(可以為 s(秒)、m(份)、h(時)、d(天))
-
編寫好頻率類可以在區域性使用,也可以在全域性使用
4、 內建頻率類
(1)AnonRateThrottle
AnonRateThrottle 內建頻率類的功能:對於登入使用者不限制次數,只未登入使用者限制次數,限制的次數需要在配置檔案中配置。使用也支援全域性和區域性。
- setting.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'anon': '5/day',
}
}
(2)UserRateThrottle
UserRateThrottle 內建頻率類的功能:限制登入使用者的頻率,限制的次數需要在配置檔案中配置。也支援全域性和區域性使用。
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.UserRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'user': '1000/day',
}
}
5、自定義頻率類
VISIT_RECORD = {}
class CommonThrottle(BaseThrottle):
def __init__(self):
self.history = None
def allow_request(self, request, view):
# 實現限流的邏輯
# 以IP限流
# 訪問列表 {IP: [time1, time2, time3]}
# 1、獲取請求的IP地址
ip = request.META.get("REMOTE_ADDR")
# 2、判斷IP地址是否在訪問列表
now = time.time()
if ip not in VISIT_RECORD:
# (1)不在 需要給訪問列表新增key,value
VISIT_RECORD[ip] = [now, ]
return True
# (2)在 需要把這個IP的訪問記錄 把當前時間加入到列表
history = VISIT_RECORD[ip]
history.insert(0, now)
# 3、確保列表裡最新訪問時間以及最老的訪問時間差 是1分鐘
while history and history[0] - history[-1] > 60:
history.pop()
self.history = history
# 4、得到列表長度,判斷是否是允許的次數
if len(history) > 3:
return False
else:
return True
def wait(self):
# 返回需要再等多久才能訪問
if not self.history:
return 0
time = max(0, 60 - (self.history[0] - self.history[-1]))
return time
在這段程式碼中,history 是一個記錄特定 IP 地址訪問時間戳的列表。這個列表儲存了特定 IP 地址在一分鐘內的訪問時間戳,用於限制該 IP 地址的訪問頻率。
在 allow_request 方法中,當一個請求到達時,會檢查該請求的 IP 地址是否已經存在於 VISIT_RECORD 中。如果存在,會將當前時間戳插入到該 IP 地址對應的歷史訪問時間戳列表中,並且清理超過一分鐘的舊時間戳。
在 wait 方法中,會計算當前時間與最新訪問時間戳的時間差,以確定需要等待多久才能再次允許該 IP 地址的訪問。如果 self.history 為空,表示該 IP 地址尚未有訪問記錄,因此返回等待時間為 0。
因此,self.history 在這段程式碼中用於跟蹤特定 IP 地址的訪問時間戳列表,以便在限制訪問頻率時進行判斷和計算。
6、其他頻率類
- AnonRateThrottle
- 限制所有匿名未認證使用者,使用 IP 區分使用者。
- 使用 DEFAULT_THROTTLE_RATES[‘anon’] 來設定頻次
- UserRateThrottle
- 限制認證使用者,使用 User id 來區分。
- 使用 DEFAULT_THROTTLE_RATES[‘user’] 來設定頻次
- ScopedRateThrottle
- 限制使用者對於每個檢視的訪問頻次,使用 ip 或 user id
四、排序
一般涉及到了查詢所有才有排序,對於表模型查詢所有資料時需要根據某個欄位進行排序時,我們就可以使用到REST framework
提供的內建排序元件OrderingFilter
來幫助我們快速指名資料按照指定欄位進行排序,遵循了restful規範中:位址列中帶過濾條件。
http://127.0.0.1:8008/app01/api/v1/books/?ordering=price 升序
http://127.0.0.1:8008/app01/api/v1/books/?ordering=price 倒序
1、使用步驟
from rest_framework.filters import OrderingFilter
# 在檢視類中配置
class BookListView(GenericViewSet,ListModelMixin):
# 必須是繼承 GenericAPIView 的檢視類---》繼承APIView是不能這麼配置的
filter_backends = [OrderingFilter]
# 必須指定表模型資料欄位,可以寫多個排序欄位
ordering_fields=['price','name'] # 預設是按id排序
# 訪問
http://127.0.0.1:8008/app01/api/v1/books1/?ordering=-price
http://127.0.0.1:8008/app01/api/v1/books1/?ordering=price
http://127.0.0.1:8008/app01/api/v1/books1/?ordering=-price,-name
# 定製返回格式--》重寫list--》重寫了list,過濾還生效嗎?
-如果純自己寫了:不生了
-如果還是使用父類的list方法:生
-半自己寫:只要有他就會生效 self.filter_queryset()
-filter_queryset(get_queryset())
{code:100,msg:成功,results:[]}
注意:繼承APIView的是使用不了以上DRF提供的排序元件,需要自己寫,自己從請求地址中取出排序規則,然後自己排序
2、繼承APIView編寫排序
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
from rest_framework.mixins import ListModelMixin
# 這裡沿用上面自動生成路由的方式,所以還是需要配置ViewSetMixin
class BookView(ViewSetMixin, APIView, ListModelMixin):
# 由於是APIView需要自己重寫list方法,在自動生成路由中是 {'get':'list'}
def list(self, request, *args, **kwargs):
# 從位址列中取出過濾條件
print(request.query_params) # ?ordering=-price,id
query_params = request.query_params # {ordering:price}
'''支援多個條件排序ordering=-price,id'''
# http://127.0.0.1:8008/app01/api/v1/books/?ordering=price
if ',' in query_params.get('ordering'):
query = query_params.get('ordering').split(',')
book_list = models.Book.objects.all().order_by(*query)
else:
book_list = models.Book.objects.all().order_by(query_params.get('ordering'))
ser = BookSerializer(instance=book_list, many=True)
return Response(ser.data)
五、過濾
restful規範中,要求請求地址中帶過濾條件,五個介面中,只有查詢所有介面需要過濾和排序。其實排序也是過濾的一種,所以過濾跟排序並不衝突,是可以同時使用多個的。但是要注意一個大原則:把一次性過濾掉很多的往前放,因為我們猜測過濾的內部實現也就是for迴圈一個個過濾類,執行過濾類的filter_queryset方法。另外,實現過濾也有三種方式:
1、繼承GenericAPIView使用DRF內建過濾器實現過濾
首先匯入:from rest_framework.filters import SearchFilter
- views.py
# 過濾和排序組合使用
http://127.0.0.1:8008/app01/api/v1/books1/?search=夢&ordering=price
# 只要出版社或名字帶海都能查出來---》search_fields=['name','publish']
http://127.0.0.1:8008/app01/api/v1/books1/?search=海
# 使用方式
from rest_framework.filters import OrderingFilter,SearchFilter
class BookListView(GenericViewSet,ListModelMixin):
filter_backends = [OrderingFilter,SearchFilter]
ordering_fields = ['price', 'name']
# 也可以多個欄位模糊匹配
search_fields=['name','publish']
使用DRF內建過濾器,它搜尋結果的關係為or(或)的關係,不是and關係
。並且只能是使用關鍵字search
才能使用,如果我們想要用name=東&price=66
這種方式的得使用別的方法來實現。
2、使用第三方模組django-filter實現and關係的過濾
- 首先需要安裝第三方模組:
pip install django-filter
- 然後再檢視類中配置
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.viewsets import ViewSetMixin
from rest_framework.generics import ListAPIView
# 使用這個第三方模組可以實現and關係,並且只能是精準匹配。並且暫不支援or關係
class BookView(ViewSetMixin,ListAPIView):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'price']
# 但是對於django-filter來講它是支援擴寫的,所以是可以支援模糊匹配,具體操作自尋查詢
- 訪問
# 查詢價格為66的圖書
http://127.0.0.1:8008/app01/api/v1/books1/?price=66
# 查詢價格為66的圖書 並且名字為 兩天 的圖書
http://127.0.0.1:8008/app01/api/v1/books1/?price=66&name=兩天
- 此外,django-filter還有更強大的搜尋,感興趣的可以深入瞭解一下
3、自定製過濾類
(1)使用步驟
- 先定義一個過濾類,並且繼承BaseFilterBackend
- 然後重寫filter_queryset方法,並且在內部完成過濾規則
- 最後在檢視類中配置自定義的過濾類
(2)過濾類
from rest_framework.filters import BaseFilterBackend
from django.db.models import Q
class CommonFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
# 完成過濾,返回 qs物件
# 查詢價格為66 或者 名字中包含海的資料
# http://127.0.0.1:8008/app01/api/v1/books1/?price=66&name=海
price = request.query_params.get('price', None)
name = request.query_params.get('name', None)
if price and name:
queryset = queryset.filter(Q(price=price) | Q(name__contains=name))
if price:
queryset=queryset.filter(price=price)
if name:
queryset = queryset.filter(name__contains=name)
return queryset
(3)檢視函式
from .filters import CommonFilter
class BookView(ViewSetMixin,ListAPIView):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
# 無需再配置欄位了,因為在自定製類中已經寫好了過濾規則,所以在檢視類中無需配置
filter_backends = [CommonFilter]
# 這樣就實現了模糊匹配name欄位並且精準匹配price欄位,以及查詢單個欄位的規則
# 訪問:127.0.0.1:8000/api/v2/books/?name=遊記&price=666
4、排序搭配過濾使用
from .filters import CommonFilter # 使用的是上面的自定製過濾類
from rest_framework.filters import OrderingFilter
class BookView(ViewSetMixin,ListAPIView):
queryset = models.Book.objects.all()
serializer_class = BookSerializer
filter_backends = [OrderingFilter,CommonFilter] # 排序加過濾,從左到右依次執行
ordering_fields = ['price','id']
六、分頁
分頁也是隻針對查詢所有的介面,其他四個介面不需要分頁。drf內建了三個分頁器,對應三種分頁方式,內建的分頁類不能直接使用,需要繼承,定製一些引數後才能使用。一個介面只能有一種分頁方式,不能混合分頁方式。
另外,分頁跟過濾排序不衝突,都可以一起使用。分頁在web端、移動端都有涉及,後端一定會需要配合實現分頁介面。
使用步驟:
- 寫個類,繼承某個分頁類
- 在檢視類中配置:檢視類必須繼承 GenericAPIView
1、分頁器一:Pagination(基本分頁)
- 透過
自定義Pagination類
,來為檢視新增不同分頁行為。在檢視中透過pagination_clas
屬性來指明。
from rest_framework.pagination import PageNumberPagination
class CommonPageNumberPagination(PageNumberPagination):
page_size = 3 # 預設每頁顯示的條數
# http://127.0.0.1:8008/app01/api/v1/books1/?page=2
page_query_param = 'page' # 使用該關鍵字指定頁碼(客戶端使用)
# http://127.0.0.1:8008/app01/api/v1/books1/?page=2&size=3 查詢第2頁,每頁顯示3條
page_size_query_param = 'size' # 透過該關鍵字可以手動指定頁面顯示的資料條數(客戶端使用)
# 每頁最多顯示10條
max_page_size = 5 # 只針對客戶端手動指定頁面顯示的資料條數做出一個限制,但不影響page_size屬性。
2、分頁器二:LimitOffsetPagination(偏移分頁)
- 偏移分頁,該分頁元件作用是:從哪一條資料之後開始顯示,以及制定資料顯示的條數
- 前端訪問形式:
http://127.0.0.1:8080/book/?offset=2&limit=4
,這表示從第3條資料之後顯示4條資料
from rest_framework.pagination import LimitOffsetPagination
class CommonLimitOffsetPagination(LimitOffsetPagination):
# 等同於上面的:page_size
default_limit = 2 # 如果前端沒有手動指定獲取的資料條數時,使用該屬性值
limit_query_param = 'limit' # 前端透過該關鍵字指定資料條數
'每頁顯示條數,查詢的條數 例子 ?limit=100 意思就是每頁顯示100條,如果不傳應用default_limit的引數'
offset_query_param = 'offset' # 透過該關鍵字指定從哪條資料之後開始獲取資料
'偏移量 舉個例子offset=6&limit=30 意思就是從第6條開始,拿30條'
# 等同於上面的:max_page_size
max_limit = 5 # 只限制limit_query_param能夠獲取的最大資料條數,而不影響default_limit
- 從第二條資料之後開始指定獲取資料的條數,使用了
limit
指定了獲取6條,但是被max_limit=5
給限制了,所以後端只能返回2條資料,如果不使用limit
指定獲取的資料條數,那麼預設使用default_limit=2
後端會返回2條資料。
3、分頁器三:CursorPagination(遊標分頁)
- 使用遊標分頁的方式後就不能再其中使用排序了,因為其內部已經排好序了'
- 並且只能選擇上一頁和下一頁,不能指定跳轉某一頁,但是速度快,針對與特別大的資料量,遊標分頁有優勢
from rest_framework.pagination import CursorPagination
class CommonCursorPagination(CursorPagination):
cursor_query_param = 'cursor' # 按遊標查詢的查詢條件,value值前端是不知道的,只能透過後臺返回
page_size = 2 # 每頁顯示多少條啊
ordering = 'id' # 排序規則,必須是表中的欄位
4、檢視類
class BookListView(GenericViewSet, ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = []
throttle_classes = []
# pagination_class = CommonPageNumberPagination # 分頁方式只能選擇一種
# pagination_class = CommonLimitOffsetPagination # 分頁方式只能選擇一種
pagination_class = CommonCursorPagination # 分頁方式只能選擇一種
5、瞭解 基於APIView實現分頁功能
from rest_framework.viewsets import ViewSet
from .pagination import CommonPageNumberPagination,CommonLimitOffsetPagination
from .serializer import BookSerializer
from rest_framework.response import Response
class BookView(ViewSet):
def list(self, reqeust):
queryset = models.Book.objects.all()
# 呼叫咱們寫的分頁類物件的paginate_queryset方法返回了,分頁後的queryset物件
'''使用PageNumberPagination(普通分頁)'''
# pagenation = CommonPageNumberPagination() # 例項化得到物件
'''使用LimitOffsetPagination(偏移分頁)'''
# pagenation = CommonLimitOffsetPagination() # 例項化得到物件
'''使用CursorPagination(遊標分頁)'''
pagenation = CommonCursorPagination() # 例項化得到物件
page = pagenation.paginate_queryset(queryset, reqeust, self)
serializer = BookSerializer(page, many=True) # page分頁後的物件
'''使用它自己的方法(三種分頁方式都相容)'''
# return pagenation.get_paginated_response(serializer.data)
'''
使用定製的分頁返回格式,得去pagination原始碼裡面的對應每種分頁方法中的
def get_paginated_response(self, data):方法裡面看返回格式怎麼寫
'''
'''也可以自己定製PageNumberPagination(普通分頁)'''
return Response({
'status': 100,
'message': '查詢成功',
'count': pagenation.page.paginator.count,
'next': pagenation.get_next_link(),
'previous': pagenation.get_previous_link(),
'results': serializer.data,
})
'''定製LimitOffsetPagination'''
return Response({
'status': 100,
'message': '查詢成功',
'count': pagenation.count,
'next': pagenation.get_next_link(),
'previous': pagenation.get_previous_link(),
'results': serializer.data,
})
'''定製CursorPagination'''
return Response({
'status': 100,
'message': '查詢成功',
'next': pagenation.get_next_link(),
'previous': pagenation.get_previous_link(),
'results': serializer.data,
})
七、補充
1、斷言
- 在python庫的原始碼中經常見到斷言,那麼怎麼理解斷言呢?
# 語法:
assert 條件,'字串'
# 翻譯成if
if 條件:
條件成立,繼續走後續程式碼
else:
raise Exception('字串')
- 案例:
a = '1'
assert isinstance(a,int),'不行,a必須是int'
原始碼中使用:
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
)
2、模組與包
(1)什麼是模組?
- 一個py 檔案,如果被匯入使用,它就是模組
- 一個py檔案,點右鍵執行,它就叫 指令碼檔案
(2)什麼是包?
- 一個資料夾下 有 init.py ,下面又有很多資料夾和py檔案,匯入使用的
(3)報錯資訊
ModuleNotFoundError: No module named 'xx'
- 但是這個模組有
- 那麼我們就應該知道是匯入的問題:路徑不對
3、相對匯入和絕對匯入
在Python中,相對匯入和絕對匯入是用於匯入模組的兩種不同方式。它們的主要區別在於匯入模組時使用的路徑方式。
(1)絕對匯入
-
絕對匯入是指從頂層包開始的完整匯入路徑,相對於環境變數
sys.path
。在絕對匯入中,你需要指定完整的包路徑來匯入模組,從專案的根目錄開始一直到目標模組。絕對匯入可以透過使用絕對匯入語法來實現,例如:from package.subpackage import module # 例如 ['D:\\PyCharm 2023.2.1\\workspace\\drf05', 'D:\\PyCharm 2023.2.1\\workspace\\drf05', 'D:\\PyCharm 2023.2.1\\plugins\\python\\helpers\\pycharm_display', 'D:\\python\\python311\\python311.zip', 'D:\\python\\python311\\Lib', 'D:\\python\\python311\\DLLs', 'D:\\python\\python311', 'D:\\python\\python311\\Lib\\site-packages', 'D:\\PyCharm 2023.2.1\\plugins\\python\\helpers\\pycharm_matplotlib_backend'] 其中 1.專案根路徑 2.site-packages 下載的第三方模組 3.python內建的,都在環境變數中
-
絕對匯入的優點是清晰明確,能夠避免模組名衝突,並且在程式碼重構時更加穩定。
(2)相對匯入
-
相對匯入是指從當前模組所在的位置開始的匯入路徑。相對匯入使用與當前模組相關的路徑來匯入模組,而不是從頂層包開始。相對匯入可以透過使用相對匯入語法來實現,例如:
from . import module
-
相對匯入的優點是更具靈活性,因為它依賴於模組的相對位置,而不是絕對路徑。這使得模組可以更容易地移動或重新命名,而無需更改匯入語句。
(3)Python中相對匯入的規則
- 單個點
.
表示當前目錄。 - 兩個點
..
表示父目錄。 - 相對匯入只能在Python包中使用,而不能在指令碼中使用。因為如果指令碼檔案
- Python 3中預設禁用隱式相對匯入,必須使用顯式相對匯入。
在實際開發中,通常建議使用絕對匯入,因為它更加明確和穩定。相對匯入在某些情況下可能會引起混亂,尤其是當專案結構較為複雜時。然而,相對匯入在某些特定情況下也是很有用的,特別是在編寫可移植的包或模組時。
(4)建議
- django專案,如果在同一個app下
- 建議:相對匯入
from .views import index
- 如果用絕對匯入,會相對來說要寫很多路徑
from app01.views import index
4、修改專案名字出現的問題及解決方案
(1)改資料夾名改不了
- 別的程序開啟了---->重啟電腦
(2)改專案名改不了
- 專案路徑下的 .idea資料夾----->專案配置
- 如果開啟專案有問題----> 把 .idea刪除---->再開啟重新執行,它會再次生成的
- 注意:當我們打包檔案的時候,.idea資料夾是不用加進去的,因為每個電腦的配置都不一樣
(3)改專案中某個檔案的名字(慎改)
- 首先模組路徑都定好了
- 如果我們一旦改了----> 專案百分百執行不了
- 如果改了,那麼我們需要右擊專案----> 找到replace in files---> 替換掉專案中所有叫原來資料夾名的 字串
(4)問題
- 如果Django專案執行不了了
- 嘗試django-server刪除重新建立
- setting中搜django---> 配置專案路徑和配置檔案路徑
5、瀏覽器特點
- 瀏覽器預設訪問http是80埠,https是443埠
- 先訪問某個頁面【域: 協議,地址,埠】,然後再訪問另一個頁面,只要域一樣就會把當時存在cookie中的值,帶到後端
- 這種思想方便我們寫登入介面
- 登入介面:自定義使用者表,UserToken表[使用者登入資訊存在後端--》本質就是django-session]
- 登入成功--》返回給前端隨機字串
- 目的:後期它再訪問,要攜帶字串---》我們透過校驗--》確認是我們給的,在UserToken表中有記錄
6、什麼是重寫,什麼是派生,什麼又是過載?
-
在繼承關係下,子類再寫一遍父類中的方法---》稱之為重寫
- 重寫的目的是定製新功能
-
在繼承關係下,子類寫父類中沒有的方法---》稱之為派生方法
-
過載就是方法名一樣,引數或返回值不一樣----》多了引數或者返回值不一樣
- python中沒有過載