REST framework 提供了一些身份驗證方案,而且還容許實現自定義方案。前端
class UserInfo(models.Model): username = models.CharField(max_length=16) password = models.CharField(max_length=32) type = models.SmallIntegerField( choices=((1, '普通用戶'), (2, 'VIP用戶')), default=1 ) class Token(models.Model): user = models.OneToOneField(to='UserInfo') token_code = models.CharField(max_length=128)
def get_random_token(username): """ 根據用戶名和時間戳生成隨機token :param username: :return: """ import hashlib, time timestamp = str(time.time()) m = hashlib.md5(bytes(username, encoding="utf8")) m.update(bytes(timestamp, encoding="utf8")) return m.hexdigest() class LoginView(APIView): """ 校驗用戶名密碼是否正確從而生成token的視圖 """ def post(self, request): res = {"code": 0} print(request.data) username = request.data.get("username") password = request.data.get("password") user = models.UserInfo.objects.filter(username=username, password=password).first() if user: # 若是用戶名密碼正確 token = get_random_token(username) models.Token.objects.update_or_create(defaults={"token_code": token}, user=user) res["token"] = token else: res["code"] = 1 res["error"] = "用戶名或密碼錯誤" return Response(res)
from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed class MyAuth(BaseAuthentication): def authenticate(self, request): if request.method in ["POST", "PUT", "DELETE"]: request_token = request.data.get("token", None) if not request_token: raise AuthenticationFailed('缺乏token') token_obj = models.Token.objects.filter(token_code=request_token).first() if not token_obj: raise AuthenticationFailed('無效的token') return token_obj.user.username, None else: return None, None
class CommentViewSet(ModelViewSet): queryset = models.Comment.objects.all() serializer_class = app01_serializers.CommentSerializer authentication_classes = [MyAuth, ]
# 在settings.py中配置 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ] }
只有VIP用戶才能看的內容。web
# 自定義權限 class MyPermission(BasePermission): message = 'VIP用戶才能訪問' def has_permission(self, request, view): """ 自定義權限只有VIP用戶才能訪問 """ # 由於在進行權限判斷以前已經作了認證判斷,因此這裏能夠直接拿到request.user if request.user and request.user.type == 2: # 若是是VIP用戶 return True else: return False
class CommentViewSet(ModelViewSet): queryset = models.Comment.objects.all() serializer_class = app01_serializers.CommentSerializer authentication_classes = [MyAuth, ] permission_classes = [MyPermission, ]
# 在settings.py中設置rest framework相關配置項 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ], "DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ] }
DRF內置了基本的限制類,首先咱們本身動手寫一個限制類,熟悉下限制組件的執行過程。json
VISIT_RECORD = {} # 自定義限制 class MyThrottle(object): def __init__(self): self.history = None def allow_request(self, request, view): """ 自定義頻率限制60秒內只能訪問三次 """ # 獲取用戶IP ip = request.META.get("REMOTE_ADDR") timestamp = time.time() if ip not in VISIT_RECORD: VISIT_RECORD[ip] = [timestamp, ] return True history = VISIT_RECORD[ip] self.history = history history.insert(0, timestamp) while history and history[-1] < timestamp - 60: history.pop() if len(history) > 3: return False else: return True def wait(self): """ 限制時間還剩多少 """ timestamp = time.time() return 60 - (timestamp - self.history[-1])
class CommentViewSet(ModelViewSet): queryset = models.Comment.objects.all() serializer_class = app01_serializers.CommentSerializer throttle_classes = [MyThrottle, ]
# 在settings.py中設置rest framework相關配置項 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ], "DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ] "DEFAULT_THROTTLE_CLASSES": ["app01.utils.MyThrottle", ] }
from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope = "xxx" def get_cache_key(self, request, view): return self.get_ident(request)
# 在settings.py中設置rest framework相關配置項 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ], # "DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ] "DEFAULT_THROTTLE_CLASSES": ["app01.utils.VisitThrottle", ], "DEFAULT_THROTTLE_RATES": { "xxx": "5/m", } }
class MyPageNumber(PageNumberPagination): page_size = 2 # 每頁顯示多少條 page_size_query_param = 'size' # URL中每頁顯示條數的參數 page_query_param = 'page' # URL中頁碼的參數 max_page_size = None # 最大頁碼數限制
class ArticleList(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = models.Article.objects.all().order_by("id") # 分頁 page_obj = MyPageNumber() page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self) ser_obj = ArticleSerializer(page_article, many=True) res["data"] = ser_obj.data return Response(res)
class ArticleList(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = models.Article.objects.all().order_by("id") # 分頁 page_obj = MyPageNumber() page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self) ser_obj = ArticleSerializer(page_article, many=True) res["data"] = ser_obj.data return page_obj.get_paginated_response(res)
# offset分頁 class MyLimitOffset(LimitOffsetPagination): default_limit = 1 limit_query_param = 'limit' offset_query_param = 'offset' max_limit = 999
class ArticleList(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = models.Article.objects.all().order_by("id") # 分頁 page_obj = MyLimitOffset() page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self) ser_obj = ArticleSerializer(page_article, many=True) res["data"] = ser_obj.data return page_obj.get_paginated_response(res)
# 加密分頁 class MyCursorPagination(CursorPagination): cursor_query_param = 'cursor' page_size = 1 ordering = '-id' # 重寫要排序的字段
class ArticleList(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = models.Article.objects.all().order_by("id") # 分頁 page_obj = MyCursorPagination() page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self) ser_obj = ArticleSerializer(page_article, many=True) res["data"] = ser_obj.data # return Response(res) return page_obj.get_paginated_response(res)
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 100 }
能夠在視圖類中進行局部設置後端
class PublisherViewSet(ModelViewSet):
queryset = models.Publisher.objects.all()
serializer_class = PublisherModelSerializer
pagination_class = PageNumberPagination # 注意不是列表(只能有一個分頁模式)
class BookViewSet(ModelViewSet): queryset = models.Book.objects.all() serializer_class = BookModelSerializer parser_classes = [JSONParser, ]
REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', ) }
'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ),
這樣設置後就只能返回JSON格式的數據了,並不會像以前同樣提供一個閱讀友好的web頁面。api
class PublisherViewSet(ModelViewSet): queryset = models.Publisher.objects.all() serializer_class = PublisherModelSerializer renderer_classes = [JSONRenderer, ]
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', ), }
DRF提供了五種版本控制方案,以下圖:瀏覽器
這裏以URLPathVersioning 爲例,仍是在項目的settings.py中REST_FRAMEWORK配置項下配置:app
REST_FRAMEWORK = { ... 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', 'DEFAULT_VERSION': 'v1', # 默認的版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 有效的版本 'VERSION_PARAM': 'version', # 版本的參數名與URL conf中一致 }
urlpatterns = [ ... url(r'^(?P<version>[v1|v2]+)/publishers/$', views.PublisherViewSet.as_view({'get': 'list', 'post': 'create'})), url(r'^(?P<version>[v1|v2]+)/publishers/(?P<pk>\d+)/$', views.PublisherViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})), ]
class PublisherViewSet(ModelViewSet): def get_serializer_class(self): """不一樣的版本使用不一樣的序列化類""" if self.request.version == 'v1': return PublisherModelSerializerVersion1 else: return PublisherModelSerializer queryset = models.Publisher.objects.all()
注意,一般是不會單獨給某個視圖設置版本控制的,若是你確實須要給單獨的視圖設置版本控制,能夠在視圖中設置versioning_class屬性,以下:框架
class PublisherViewSet(ModelViewSet): ... versioning_class = URLPathVersioning