Rest Framework

1、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

 

 

2、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方法(內部調用認證,權限和頻率)

 

 

3、序列化組件

  • 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

    注意:html

      source 若是是字段,會顯示字段,若是是方法,會執行方法,不用加括號(authors=serializers.CharField(source='authors.all'))如在模型中定義一個方法,直接能夠在在source指定執行python

        

      
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

 

  • 生成hypermedialink(極少數)

 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=Book
        fields="__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方法

   經過源碼查看留的校驗字段的鉤子函數:sql

 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

 

 4、認證組件

  • 認證簡介

      只有認證經過的用戶才能訪問指定的url地址,好比:查詢課程信息,須要登陸以後才能查看,沒有登陸,就不能查看,這時候須要用到認證組件django

  • 局部使用

    
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驗證

    總結:局部使用,只須要在視圖類里加入:json

     authentication_classes = [TokenAuth, ] api

  • 全局使用

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裏配置的驗證類,最後用默認的驗證類數組

 

5、權限組件

  • 權限簡介

      只用超級用戶才能訪問指定的數據,普通用戶不能訪問,因此就要有權限組件對其限制服務器

  • 局部使用

    
 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裏配置的權限類,最後用默認的權限類

 

6、頻率組件

  • 頻率簡介

      爲了控制用戶對某個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'
    }
}

 

  • 源碼分析

    省略。。。。。。

 

 

7、分頁器組件

  • 簡單分頁(查看第n頁,每頁顯示n條)

 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',)
  • 偏移分頁(在第n個位置,向後查看n條數據)

 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)
相關文章
相關標籤/搜索