目錄導航 一、RESTful 規范 二、APIView 組件 三、序列化組件 四、認證組件 五、權限組件 六、頻率組件 七、分頁器組件 ?
一、RESTful 規范 ?什么是RESTful規范: REST與技術無關,代表的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯為“表征狀態轉移” REST從資源的角度類審視整個網絡,它將分布在網絡中某個節點的資源通過URL進行標識,客戶端應用通過URL來獲取資源的表征,獲得這些表征致使這些應用轉變狀態 REST與技術無關,代表的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯為“表征狀態轉移” 所有的數據,不過是通過網絡獲取的還是操作(增刪改查)的數據,都是資源,將一切數據視為資源是REST區別與其他架構風格的最本質屬性 對于REST這種面向資源的架構風格,有人提出一種全新的結構理念,即:面向資源架構(ROA:Resource Oriented Architecture) ?
RESTful API 設計: ? ?API與用戶的通信協議,總是使用HTTPs協議。 ? 域名 https://api.example.com? ? ? ? ? ? ? ? ? ? ? ? ?盡量將API部署在專用域名(會存在跨域問題) https://example.org/api/? ? ? ? ? ? ? ? ? ? ? ? API很簡單 ? 版本 URL,如:https://api.example.com/v1/ 請求頭? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 跨域時,引發發送多次請求 ? 路徑,視網絡上任何東西都是資源,均使用名詞表示(可復數) https://api.example.com/v1/zoos https://api.example.com/v1/animals https://api.example.com/v1/employees ? method GET? ? ? :從服務器取出資源(一項或多項) POST? ? :在服務器新建一個資源 PUT? ? ? :在服務器更新資源(客戶端提供改變后的完整資源) PATCH? :在服務器更新資源(客戶端提供改變的屬性) DELETE :從服務器刪除資源 ? 過濾,通過在url上傳參的形式傳遞搜索條件 https://api.example.com/v1/zoos?limit=10:指定返回記錄的數量 https://api.example.com/v1/zoos?offset=10:指定返回記錄的開始位置 https://api.example.com/v1/zoos?page=2&per_page=100:指定第幾頁,以及每頁的記錄數 https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回結果按照哪個屬性排序,以及排序順序 https://api.example.com/v1/zoos?animal_type_id=1:指定篩選條件 ? 狀態碼 ''' 200 OK - [GET]:服務器成功返回用戶請求的數據,該操作是冪等的(Idempotent)。201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。202 Accepted - [*]:表示一個請求已經進入后臺排隊(異步任務)204 NO CONTENT - [DELETE]:用戶刪除數據成功。400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作,該操作是冪等的。401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。403 Forbidden - [*] 表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的。404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操作,該操作是冪等的。406 Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式,但是只有XML格式)。410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的。422 Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。更多看這里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html ''' ? 錯誤處理,應返回錯誤信息,error當做key。 ? 返回結果,針對不同操作,服務器向用戶返回的結果應該符合以下規范。 GET?/collection:返回資源對象的列表(數組) GET /collection/resource:返回單個資源對象 POST?/collection:返回新生成的資源對象 PUT?/collection/resource:返回完整的資源對象 PATCH?/collection/resource:返回完整的資源對象 DELETE?/collection/resource:返回一個空文檔 ? Hypermedia API,RESTful API最好做到Hypermedia,即返回結果中提供鏈接,連向其他API方法,使得用戶不查文檔,也知道下一步應該做什么。 {"link": { ??"rel":???"collection https://www.example.com/zoos", ??"href":??"https://api.example.com/zoos", ??"title":?"List of zoos", ??"type":??"application/vnd.yourformat+json" }} 基于Django實現 1 urlpatterns =
[
2 url(r
' ^users/$ ' , views.Users.as_view()),
3 url(r
' ^users2/$ ' , views.user2),
4
5 ]
路由系統配置 1 import json
2
3 def user2(request):
4 if request.method==
' GET ' :
5 dic = {
' status ' :200,
' name ' :
' lqz2 ' ,
' age ' : 18
}
6 return HttpResponse(json.dumps(dic))
7 elif request.method==
' POST ' :
8 dic = {
' status ' : 200,
' msg ' :
' 修改成功 ' }
9 return JsonResponse(dic)
10
11 class Users(View):
12 def get(self, request):
13 dic = {
' status ' :200,
' name ' :
' lqz ' ,
' age ' : 18
}
14 return HttpResponse(json.dumps(dic))
15
16 def post(self, request):
17 dic = {
' status ' : 200,
' msg ' :
' 修改成功 ' }
18 return JsonResponse(dic)
views.py ?
?
二、APIView 組件 ? 安裝djangorestframework 方式一:pip3 install djangorestframework 方式二:pycharm圖形化界面安裝 方式三:pycharm命令行下安裝(裝在當前工程所用的解釋器下) djangorestframework的APIView分析 1 @classmethod
2 def as_view(cls, **
initkwargs):
3 """
4 Store the original class on the view function.
5
6 This allows us to discover information about the view when we do URL
7 reverse lookups. Used for breadcrumb generation.
8 """
9 if isinstance(getattr(cls,
' queryset ' , None), models.query.QuerySet):
10 def force_evaluation():
11 raise RuntimeError(
12 ' Do not evaluate the `.queryset` attribute directly, '
13 ' as the result will be cached and reused between requests. '
14 ' Use `.all()` or call `.get_queryset()` instead. '
15 )
16 cls.queryset._fetch_all =
force_evaluation
17
18 view = super(APIView, cls).as_view(**
initkwargs)
19 view.cls =
cls
20 view.initkwargs =
initkwargs
21
22 # Note: session based authentication is explicitly CSRF validated,
23 # all other authentication is CSRF exempt.
24 return csrf_exempt(view)
as_view方法 1 def dispatch(self, request, *args, **
kwargs):
2 """
3 `.dispatch()` is pretty much the same as Django's regular dispatch,
4 but with extra hooks for startup, finalize, and exception handling.
5 """
6 self.args =
args
7 self.kwargs =
kwargs
8 request = self.initialize_request(request, *args, **
kwargs)
9 self.request =
request
10 self.headers = self.default_response_headers
# deprecate?
11
12 try :
13 self.initial(request, *args, **
kwargs)
14
15 # Get the appropriate handler method
16 if request.method.lower()
in self.http_method_names:
17 handler =
getattr(self, request.method.lower(),
18 self.http_method_not_allowed)
19 else :
20 handler =
self.http_method_not_allowed
21
22 response = handler(request, *args, **
kwargs)
23
24 except Exception as exc:
25 response =
self.handle_exception(exc)
26
27 self.response = self.finalize_response(request, response, *args, **
kwargs)
28 return self.response
dispatch def initialize_request(self, request, *args, **
kwargs): """ Returns the initial request object. """ parser_context =
self.get_parser_context(request) return Request(request,parsers =
self.get_parsers(),authenticators =
self.get_authenticators(),negotiator =
self.get_content_negotiator(),parser_context =
parser_context) initialize_request 1 def initial(self, request, *args, **
kwargs):
2 """
3 Runs anything that needs to occur prior to calling the method handler.
4 """
5 self.format_kwarg = self.get_format_suffix(**
kwargs)
6
7 # Perform content negotiation and store the accepted info on the request
8 neg =
self.perform_content_negotiation(request)
9 request.accepted_renderer, request.accepted_media_type =
neg
10
11 # Determine the API version, if versioning is in use.
12 version, scheme = self.determine_version(request, *args, **
kwargs)
13 request.version, request.versioning_scheme =
version, scheme
14
15 # Ensure that the incoming request is permitted
16 self.perform_authentication(request)
17 self.check_permissions(request)
18 self.check_throttles(request)
initial方法(內部調用認證,權限和頻率) ?
?
三、序列化組件 rest-framework序列化之Serializer 1 from django.db
import models
2
3 # Create your models here.
4
5
6 class Book(models.Model):
7 title=models.CharField(max_length=32
)
8 price=
models.IntegerField()
9 pub_date=
models.DateField()
10 publish=models.ForeignKey(
" Publish " )
11 authors=models.ManyToManyField(
" Author " )
12 def __str__ (self):
13 return self.title
14
15 class Publish(models.Model):
16 name=models.CharField(max_length=32
)
17 email=
models.EmailField()
18 def __str__ (self):
19 return self.name
20
21 class Author(models.Model):
22 name=models.CharField(max_length=32
)
23 age=
models.IntegerField()
24 def __str__ (self):
25 return self.name
models.py 1 from rest_framework.views
import APIView
2 from rest_framework.response
import Response
3 from .models
import *
4 from django.shortcuts
import HttpResponse
5 from django.core
import serializers
6
7
8 from rest_framework
import serializers
9
10 class BookSerializers(serializers.Serializer):
11 title=serializers.CharField(max_length=32
)
12 price=
serializers.IntegerField()
13 pub_date=
serializers.DateField()
14 publish=serializers.CharField(source=
" publish.name " )
15 # authors=serializers.CharField(source="authors.all")
16 authors=
serializers.SerializerMethodField()
17 def get_authors(self,obj):
18 temp=
[]
19 for author
in obj.authors.all():
20 temp.append(author.name)
21 return temp
22 # 此處可以繼續用author的Serializers,
23 # def get_authors(self,obj):
24 # ret=obj.authors.all()
25 # ss=AuthorSerializer(ret,many=True)
26 # return ss.data
27
28 class BookViewSet(APIView):
29
30 def get(self,request,*args,**
kwargs):
31 book_list=
Book.objects.all()
32 # 序列化方式1:
33 # from django.forms.models import model_to_dict
34 # import json
35 # data=[]
36 # for obj in book_list:
37 # data.append(model_to_dict(obj))
38 # print(data)
39 # return HttpResponse("ok")
40
41 # 序列化方式2:
42 # data=serializers.serialize("json",book_list)
43 # return HttpResponse(data)
44
45 # 序列化方式3:
46 bs=BookSerializers(book_list,many=True)
# many=True代表有多條數據,如果只有一條數據,many=False
47 return Response(bs.data)
48 # 序列化方式4:
49 # ret=models.Book.objects.all().values('nid','title')
50 # dd=list(ret)
51 # return HttpResponse(json.dumps(dd)) views.py 注意:
source 如果是字段,會顯示字段,如果是方法,會執行方法,不用加括號(authors=serializers.CharField(source='authors.all'))如在模型中定義一個方法,直接可以在在source指定執行
class UserInfo(models.Model):user_type_choices =
(( 1,
' 普通用戶 ' ),( 2,
' VIP ' ),( 3,
' SVIP ' ),)user_type = models.IntegerField(choices=
user_type_choices)username = models.CharField(max_length=32,unique=
True)password = models.CharField(max_length=64
) # 視圖
ret=models.UserInfo.objects.filter(pk=1
).first()
aa =
ret.get_user_type_display() # serializer
xx=serializers.CharField(source=
' get_user_type_display ' )
View Code ?
rest-framework序列化之ModelSerializer 1 class BookSerializers(serializers.ModelSerializer):
2 class Meta:
3 model =
models.Book
4 # fields = "__all__"
5 fields=[
' nid ' ,
' title ' ,
' authors ' ,
' publish ' ]
6 # exclude=('nid',) #不能跟fields同時用
7 # depth = 1 #深度控制,寫 幾 往里拿幾層,層數越多,響應越慢,官方建議0--10之間,個人建議最多3層
8 publish=
serializers.SerializerMethodField()
9 def get_publish(self,obj):
10 return obj.publish.name
11 authors=
serializers.SerializerMethodField()
12 def get_authors(self,obj):
13 ret=
obj.authors.all()
14 ss=AuthorSerializer(ret,many=
True)
15 return ss.data
?
1 class BookSerializers(serializers.ModelSerializer):
2 class Meta:
3 model =
models.Book
4 fields =
" __all__ "
5 # 生成連接,直接查看出版社詳情
6 publish = serializers.HyperlinkedIdentityField(view_name=
' ttt ' , lookup_field=
' publish_id ' , lookup_url_kwarg=
' pkk ' )
7 authors=
serializers.SerializerMethodField()
8 def get_authors(self,obj):
9 ret=
obj.authors.all()
10 ss=AuthorSerializer(ret,many=
True)
11 return ss.data
12 # --------------
13
14 res=BookSerializers(ret,many=True,context={
' request ' : request})
15
16 # --------------
17
18 class Publish(APIView):
19 def get(self,request,pkk):
20 print (pkk)
21 return HttpResponse(
' ok ' )
22 # ----路由---
23 url(r
' ^publish/(?P<pkk>\d+)$ ' , views.Publish.as_view(),name=
' ttt ' ),
?
class BookSerializers(serializers.ModelSerializer): class Meta:model =
Bookfields =
" __all__ " # ————————
class BookView(APIView): def post(self, request): # 添加一條數據 print (request.data)bs =BookSerializers(data=
request.data) if bs.is_valid():bs.save() # 生成記錄 return Response(bs.data) else : return Response(bs.errors)
class BookSerializer1(serializers.Serializer):title =serializers.CharField(error_messages={
' required ' :
' 標題不能為空 ' }) # 這種方式要保存,必須重寫create方法 ? 通過源碼查看留的校驗字段的鉤子函數:
1 # is_valid---->self.run_validation-(執行Serializer的run_validation)-->self.to_internal_value(data)---(執行Serializer的run_validation:485行)
2 def validate_title(self, value):
3 from rest_framework
import exceptions
4 raise exceptions.ValidationError(
' 看你不順眼 ' )
5 return value
6
7 # 全局
8 def validate(self, attrs):
9 from rest_framework
import exceptions
10 if attrs.get(
' title ' )== attrs.get(
' title2 ' ):
11 return attrs
12 else :
13 raise exceptions.ValidationError(
' 不想等啊 ' )
?
1 '''
2 序列化組件,先調用__new__方法,如果many=True,生成ListSerializer對象,如果為False,生成Serializer對象
3 序列化對象.data方法--調用父類data方法---調用對象自己的to_representation(自定義的序列化類無此方法,去父類找)
4 Aerializer類里有to_representation方法,for循環執行attribute = field.get_attribute(instance)
5 再去Field類里去找get_attribute方法,self.source_attrs就是被切分的source,然后執行get_attribute方法,source_attrs
6 當參數傳過去,判斷是方法就加括號執行,是屬性就把值取出來
7 ''' 圖書的增刪查改resful接口:
1 class BookSerializers(serializers.ModelSerializer):
2 class Meta:
3 model=
models.Book
4 fields=
' __all__ '
5
6
7 class BookView(APIView):
8
9 def get(self, request):
10 book_list =
models.Book.objects.all()
11 bs = BookSerializers(book_list, many=
True)
12 # 序列化數據
13
14 return Response(bs.data)
15
16 def post(self, request):
17 # 添加一條數據
18 print (request.data)
19
20 bs=BookSerializers(data=
request.data)
21 if bs.is_valid():
22 bs.save()
# 生成記錄
23 return Response(bs.data)
24 else :
25
26 return Response(bs.errors)
27
28 class BookDetailView(APIView):
29 def get(self,request,pk):
30 book_obj=models.Book.objects.filter(pk=
pk).first()
31 bs=BookSerializers(book_obj,many=
False)
32 return Response(bs.data)
33 def put(self,request,pk):
34 book_obj = models.Book.objects.filter(pk=
pk).first()
35
36 bs=BookSerializers(data=request.data,instance=
book_obj)
37 if bs.is_valid():
38 bs.save()
# update
39 return Response(bs.data)
40 else :
41 return Response(bs.errors)
42 def delete(self,request,pk):
43 models.Book.objects.filter(pk=
pk).delete()
44
45 return Response(
"" )
views.py 1 url(r
' ^books/$ ' , views.BookView.as_view()),
2 url(r
' ^books/(?P<pk>\d+)$ ' , views.BookDetailView.as_view()),
urls.py ?
?四、認證組件 只有認證通過的用戶才能訪問指定的url地址,比如:查詢課程信息,需要登錄之后才能查看,沒有登錄,就不能查看,這時候需要用到認證組件
1 class User(models.Model):
2 username=models.CharField(max_length=32
)
3 password=models.CharField(max_length=32
)
4 user_type=models.IntegerField(choices=((1,
' 超級用戶 ' ),(2,
' 普通用戶 ' ),(3,
' 二筆用戶 ' )))
5
6 class UserToken(models.Model):
7 user=models.OneToOneField(to=
' User ' )
8 token=models.CharField(max_length=64)
models.py 1 from rest_framework.authentication
import BaseAuthentication
2 class TokenAuth():
3 def authenticate(self, request):
4 token = request.GET.get(
' token ' )
5 token_obj = models.UserToken.objects.filter(token=
token).first()
6 if token_obj:
7 return
8 else :
9 raise AuthenticationFailed(
' 認證失敗 ' )
10 def authenticate_header(self,request):
11 pass 新建認證類(驗證通過return兩個參數) 1 def get_random(name):
2 import hashlib
3 import time
4 md=
hashlib.md5()
5 md.update(bytes(str(time.time()),encoding=
' utf-8 ' ))
6 md.update(bytes(name,encoding=
' utf-8 ' ))
7 return md.hexdigest()
8 class Login(APIView):
9 def post(self,reuquest):
10 back_msg={
' status ' :1001,
' msg ' :None}
11 try :
12 name=reuquest.data.get(
' name ' )
13 pwd=reuquest.data.get(
' pwd ' )
14 user=models.User.objects.filter(username=name,password=
pwd).first()
15 if user:
16 token=
get_random(name)
17 models.UserToken.objects.update_or_create(user=user,defaults={
' token ' :token})
18 back_msg[
' status ' ]=
' 1000 '
19 back_msg[
' msg ' ]=
' 登錄成功 '
20 back_msg[
' token ' ]=
token
21 else :
22 back_msg[
' msg ' ] =
' 用戶名或密碼錯誤 '
23 except Exception as e:
24 back_msg[
' msg ' ]=
str(e)
25 return Response(back_msg)
26
27
28
29 class Course(APIView):
30 authentication_classes =
[TokenAuth, ]
31
32 def get(self, request):
33 return HttpResponse(
' get ' )
34
35 def post(self, request):
36 return HttpResponse(
' post ' )
views.py 1 def get_token(id,salt=
' 123 ' ):
2 import hashlib
3 md=
hashlib.md5()
4 md.update(bytes(str(id),encoding=
' utf-8 ' ))
5 md.update(bytes(salt,encoding=
' utf-8 ' ))
6
7 return md.hexdigest()+
' | ' +
str(id)
8
9 def check_token(token,salt=
' 123 ' ):
10 ll=token.split(
' | ' )
11 import hashlib
12 md=
hashlib.md5()
13 md.update(bytes(ll[-1],encoding=
' utf-8 ' ))
14 md.update(bytes(salt,encoding=
' utf-8 ' ))
15 if ll[0]==
md.hexdigest():
16 return True
17 else :
18 return False
19
20 class TokenAuth():
21 def authenticate(self, request):
22 token = request.GET.get(
' token ' )
23 success=
check_token(token)
24 if success:
25 return
26 else :
27 raise AuthenticationFailed(
' 認證失敗 ' )
28 def authenticate_header(self,request):
29 pass
30 class Login(APIView):
31 def post(self,reuquest):
32 back_msg={
' status ' :1001,
' msg ' :None}
33 try :
34 name=reuquest.data.get(
' name ' )
35 pwd=reuquest.data.get(
' pwd ' )
36 user=models.User.objects.filter(username=name,password=
pwd).first()
37 if user:
38 token=
get_token(user.pk)
39 # models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
40 back_msg[
' status ' ]=
' 1000 '
41 back_msg[
' msg ' ]=
' 登錄成功 '
42 back_msg[
' token ' ]=
token
43 else :
44 back_msg[
' msg ' ] =
' 用戶名或密碼錯誤 '
45 except Exception as e:
46 back_msg[
' msg ' ]=
str(e)
47 return Response(back_msg)
48 from rest_framework.authentication
import BaseAuthentication
49 class TokenAuth():
50 def authenticate(self, request):
51 token = request.GET.get(
' token ' )
52 token_obj = models.UserToken.objects.filter(token=
token).first()
53 if token_obj:
54 return
55 else :
56 raise AuthenticationFailed(
' 認證失敗 ' )
57 def authenticate_header(self,request):
58 pass
59
60 class Course(APIView):
61 authentication_classes =
[TokenAuth, ]
62
63 def get(self, request):
64 return HttpResponse(
' get ' )
65
66 def post(self, request):
67 return HttpResponse(
' post ' )
不存數據庫的token驗證 總結:局部使用,只需要在視圖類里加入:
? authentication_classes = [TokenAuth, ] ?
REST_FRAMEWORK=
{ " DEFAULT_AUTHENTICATION_CLASSES " :[
" app01.service.auth.Authentication " ,]
} 1 # Request對象的user方法
2 @property
3 def user(self):
4 the authentication classes provided to the request.
5 if not hasattr(self,
' _user ' ):
6 with wrap_attributeerrors():
7 self._authenticate()
8 return self._user
9
10 def _authenticate(self):
11 for authenticator
in self.authenticators:
12 try :
13 user_auth_tuple =
authenticator.authenticate(self)
14 except exceptions.APIException:
15 self._not_authenticated()
16 raise
17 # 認證成功,可以返回一個元組,但必須是最后一個驗證類才能返回
18 if user_auth_tuple
is not None:
19 self._authenticator =
authenticator
20 self.user, self.auth =
user_auth_tuple
21 return
22
23 self._not_authenticated()
View Code self.authenticators
def get_authenticators(self): return [auth()
for auth
in self.authentication_classes]
認證類使用順序:先用視圖類中的驗證類,再用settings里配置的驗證類,最后用默認的驗證類
?
五、權限組件 只用超級用戶才能訪問指定的數據,普通用戶不能訪問,所以就要有權限組件對其限制
1 from rest_framework.permissions
import BasePermission
2 class UserPermission(BasePermission):
3 message =
' 不是超級用戶,查看不了 '
4 def has_permission(self, request, view):
5 # user_type = request.user.get_user_type_display()
6 # if user_type == '超級用戶':
7 user_type =
request.user.user_type
8 print (user_type)
9 if user_type == 1
:
10 return True
11 else :
12 return False
13 class Course(APIView):
14 authentication_classes =
[TokenAuth, ]
15 permission_classes =
[UserPermission,]
16
17 def get(self, request):
18 return HttpResponse(
' get ' )
19
20 def post(self, request):
21 return HttpResponse(
' post ' )
View Code 局部使用只需要在視圖類里加入:
? permission_classes = [UserPermission,] ?
REST_FRAMEWORK =
{ " DEFAULT_AUTHENTICATION_CLASSES " :[
" app01.service.auth.Authentication " ,], " DEFAULT_PERMISSION_CLASSES " :[
" app01.service.permissions.SVIPPermission " ,]
} 1 def check_permissions(self, request):
2 for permission
in self.get_permissions():
3 if not permission.has_permission(request, self):
4 self.permission_denied(
5 request, message=getattr(permission,
' message ' , None)
6 )
View Code self.get_permissions()
def get_permissions(self): return [permission()
for permission
in self.permission_classes]
權限類使用順序:先用視圖類中的權限類 ,再用settings里配置的權限類 ,最后用默認的權限類
?
六、頻率組件 為了控制用戶對某個url請求的頻率,比如,一分鐘以內,只能訪問三次
?
自定義的邏輯:
# (1)取出訪問者ip
# (2)判斷當前ip不在訪問字典里,添加進去,并且直接返回True,表示第一次訪問,在字典里,繼續往下走
# (3)循環判斷當前ip的列表,有值,并且當前時間減去列表的最后一個時間大于60s,把這種數據pop掉,這樣列表中只有60s以內的訪問時間,
# (4)判斷,當列表小于3,說明一分鐘以內訪問不足三次,把當前時間插入到列表第一個位置,返回True,順利通過
# (5)當大于等于3,說明一分鐘內訪問超過三次,返回False驗證失敗 代碼實現:
1 class MyThrottles():
2 VISIT_RECORD =
{}
3 def __init__ (self):
4 self.history=
None
5 def allow_request(self,request, view):
6 # (1)取出訪問者ip
7 # print(request.META)
8 ip=request.META.get(
' REMOTE_ADDR ' )
9 import time
10 ctime=
time.time()
11 # (2)判斷當前ip不在訪問字典里,添加進去,并且直接返回True,表示第一次訪問
12 if ip
not in self.VISIT_RECORD:
13 self.VISIT_RECORD[ip]=
[ctime,]
14 return True
15 self.history=
self.VISIT_RECORD.get(ip)
16 # (3)循環判斷當前ip的列表,有值,并且當前時間減去列表的最后一個時間大于60s,把這種數據pop掉,這樣列表中只有60s以內的訪問時間,
17 while self.history
and ctime-self.history[-1]>60
:
18 self.history.pop()
19 # (4)判斷,當列表小于3,說明一分鐘以內訪問不足三次,把當前時間插入到列表第一個位置,返回True,順利通過
20 # (5)當大于等于3,說明一分鐘內訪問超過三次,返回False驗證失敗
21 if len(self.history)<3
:
22 self.history.insert(0,ctime)
23 return True
24 else :
25 return False
26 def wait(self):
27 import time
28 ctime=
time.time()
29 return 60-(ctime-self.history[-1])
View Code ?
寫一個類,繼承自SimpleRateThrottle,(根據ip限制)問:要根據用戶現在怎么寫
1 from rest_framework.throttling
import SimpleRateThrottle
2 class VisitThrottle(SimpleRateThrottle):
3 scope =
' luffy '
4 def get_cache_key(self, request, view):
5 return self.get_ident(request)
在setting里配置:(一分鐘訪問三次)
1 REST_FRAMEWORK =
{
2 ' DEFAULT_THROTTLE_RATES ' :{
3 ' luffy ' :
' 3/m '
4 }
5 }
在視圖類里使用
throttle_classes = [MyThrottles,]
錯誤信息的中文提示:
1 class Course(APIView):
2 authentication_classes =
[TokenAuth, ]
3 permission_classes =
[UserPermission, ]
4 throttle_classes =
[MyThrottles,]
5
6 def get(self, request):
7 return HttpResponse(
' get ' )
8
9 def post(self, request):
10 return HttpResponse(
' post ' )
11 def throttled(self, request, wait):
12 from rest_framework.exceptions
import Throttled
13 class MyThrottled(Throttled):
14 default_detail =
' 傻逼啊 '
15 extra_detail_singular =
' 還有 {wait} second. '
16 extra_detail_plural =
' 出了 {wait} seconds. '
17 raise MyThrottled(wait)
View Code 內置頻率限制類:
BaseThrottle是所有類的基類:方法:def get_ident(self, request)獲取標識,其實就是獲取ip,自定義的需要繼承它
AnonRateThrottle:未登錄用戶ip限制,需要配合auth模塊用
SimpleRateThrottle:重寫此方法,可以實現頻率現在,不需要咱們手寫上面自定義的邏輯
UserRateThrottle:登錄用戶頻率限制,這個得配合auth模塊來用
ScopedRateThrottle:應用在局部視圖上的(忽略)
?
REST_FRAMEWORK =
{ ' DEFAULT_THROTTLE_CLASSES ' :[
' app01.utils.VisitThrottle ' ,], ' DEFAULT_THROTTLE_RATES ' :{ ' luffy ' :
' 3/m ' }
} ?
省略。。。。。。
?
?
七、分頁器組件 1 from rest_framework.pagination
import PageNumberPagination
2 # 一 基本使用:url=url=http://127.0.0.1:8000/pager/?page=2&size=3,size無效
3 class Pager(APIView):
4 def get(self,request,*args,**
kwargs):
5 # 獲取所有數據
6 ret=
models.Book.objects.all()
7 # 創建分頁對象
8 page=
PageNumberPagination()
9 # 在數據庫中獲取分頁的數據
10 page_list=page.paginate_queryset(ret,request,view=
self)
11 # 對分頁進行序列化
12 ser=BookSerializer1(instance=page_list,many=
True)
13 return Response(ser.data)
14 # 二 自定制 url=http://127.0.0.1:8000/pager/?page=2&size=3
15 # size=30,無效,最多5條
16 class Mypage(PageNumberPagination):
17 page_size = 2
18 page_query_param =
' page '
19 # 定制傳參
20 page_size_query_param =
' size '
21 # 最大一頁的數據
22 max_page_size = 5
23 class Pager(APIView):
24 def get(self,request,*args,**
kwargs):
25 # 獲取所有數據
26 ret=
models.Book.objects.all()
27 # 創建分頁對象
28 page=
Mypage()
29 # 在數據庫中獲取分頁的數據
30 page_list=page.paginate_queryset(ret,request,view=
self)
31 # 對分頁進行序列化
32 ser=BookSerializer1(instance=page_list,many=
True)
33 # return Response(ser.data)
34 # 這個也是返回Response對象,但是比基本的多了上一頁,下一頁,和總數據條數(了解即可)
35 return page.get_paginated_response(ser.data)
setting中配置:
REST_FRAMEWORK =
{ # 每頁顯示兩條 ' PAGE_SIZE ' :2
} ?路由:?url(r' ^pager/$ ' , views.Pager.as_view()), ?
新建類:?Serializers
1 class BookSerializer1(serializers.ModelSerializer):
2 class Meta:
3 model=
models.Book
4 # fields="__all__"
5 exclude=(
' authors ' ,)
1 # http://127.0.0.1:8000/pager/?offset=4&limit=3
2 from rest_framework.pagination
import LimitOffsetPagination
3 # 也可以自定制,同簡單分頁
4 class Pager(APIView):
5 def get(self,request,*args,**
kwargs):
6 # 獲取所有數據
7 ret=
models.Book.objects.all()
8 # 創建分頁對象
9 page=
LimitOffsetPagination()
10 # 在數據庫中獲取分頁的數據
11 page_list=page.paginate_queryset(ret,request,view=
self)
12 # 對分頁進行序列化
13 ser=BookSerializer1(instance=page_list,many=
True)
14 # return page.get_paginated_response(ser.data)
15 return Response(ser.data)
CursorPagination(加密分頁,只能看上一頁和下一頁,速度快) 1 from rest_framework.pagination
import CursorPagination
2 # 看源碼,是通過sql查詢,大于id和小于id
3 class Pager(APIView):
4 def get(self,request,*args,**
kwargs):
5 # 獲取所有數據
6 ret=
models.Book.objects.all()
7 # 創建分頁對象
8 page=
CursorPagination()
9 page.ordering=
' nid '
10 # 在數據庫中獲取分頁的數據
11 page_list=page.paginate_queryset(ret,request,view=
self)
12 # 對分頁進行序列化
13 ser=BookSerializer1(instance=page_list,many=
True)
14 # 可以避免頁碼被猜到
15 return page.get_paginated_response(ser.data)
?
?
轉載于:https://www.cnblogs.com/child-king/p/10456641.html
總結
以上是生活随笔 為你收集整理的Rest Framework 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。