django rest framework

官網(wǎng)鏈接:http://www.django-rest-framework.org/

一获搏、概念:什么是rest framework

rest 是representational state transfer的縮寫肛根,意為‘表征狀態(tài)轉(zhuǎn)移’

REST從資源的角度類審視整個網(wǎng)絡(luò),它將分布在網(wǎng)絡(luò)中某個節(jié)點的資源通過URL進行標識钦睡,客戶端應(yīng)用通過URL來獲取資源的表征,獲得這些表征致使這些應(yīng)用轉(zhuǎn)變狀態(tài)。
所以 django rest framework 又可稱為‘面向資源編程’

二谎亩、Restful API設(shè)計

1、API與用戶的通信協(xié)議宇姚,總是使用HTTPs協(xié)議匈庭。
2、域名

https://api.example.com   #盡量將API 部署在專用域名(會存在跨域問題)

3浑劳、版本

https://api.example.com/v1/  ##在URL后面添加上版本

4阱持、路徑

https://api.example.com/v1/Humans
https://api.example.com/v1/Chinese
##路徑命名要使用名詞(可復(fù)數(shù))

5、請求方法(method):

GET      :從服務(wù)器取出資源(一項或多項)
POST    :在服務(wù)器新建一個資源
PUT      :在服務(wù)器更新資源(客戶端提供改變后的完整資源)
PATCH  :在服務(wù)器更新資源(客戶端提供改變的屬性)
DELETE :從服務(wù)器刪除資源

6魔熏、狀態(tài)碼

200 OK - [GET]:服務(wù)器成功返回用戶請求的數(shù)據(jù)衷咽,該操作是冪等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數(shù)據(jù)成功蒜绽。
202 Accepted - [*]:表示一個請求已經(jīng)進入后臺排隊(異步任務(wù))
204 NO CONTENT - [DELETE]:用戶刪除數(shù)據(jù)成功镶骗。
400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發(fā)出的請求有錯誤,服務(wù)器沒有進行新建或修改數(shù)據(jù)的操作躲雅,該操作是冪等的鼎姊。
401 Unauthorized - [*]:表示用戶沒有權(quán)限(令牌、用戶名吏夯、密碼錯誤)此蜈。
403 Forbidden - [*] 表示用戶得到授權(quán)(與401錯誤相對)摊欠,但是訪問是被禁止的移斩。
404 NOT FOUND - [*]:用戶發(fā)出的請求針對的是不存在的記錄润歉,服務(wù)器沒有進行操作逗堵,該操作是冪等的棺聊。
406 Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式拉一,但是只有XML格式)囤攀。
410 Gone -[GET]:用戶請求的資源被永久刪除背稼,且不會再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 當創(chuàng)建一個對象時植兰,發(fā)生一個驗證錯誤份帐。
500 INTERNAL SERVER ERROR - [*]:服務(wù)器發(fā)生錯誤,用戶將無法判斷發(fā)出的請求是否成功楣导。

詳細鏈接:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

Rest framework 在django源碼中的執(zhí)行流程

執(zhí)行流程

urls.py

from django.conf.urls import url, include
from web.views.s1_api import TestView
 
urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

views.py

from rest_framework.views import APIView
from rest_framework.response import Response
 
 
class TestView(APIView):
    def dispatch(self, request, *args, **kwargs):
        """
        請求到來之后废境,都要執(zhí)行dispatch方法,dispatch方法根據(jù)請求方式不同觸發(fā) get/post/put等方法
         
        注意:APIView中的dispatch方法有好多好多的功能
        """
        return super().dispatch(request, *args, **kwargs)
 
    def get(self, request, *args, **kwargs):
        return Response('GET請求筒繁,響應(yīng)內(nèi)容')
 
    def post(self, request, *args, **kwargs):
        return Response('POST請求噩凹,響應(yīng)內(nèi)容')
 
    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

對django的request進行加工毡咏,形成新的request


image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png

Django Rest Framework 框架實現(xiàn)

1驮宴、基本流程

urls.py

from django.conf.urls import url, include
from web.views.s1_api import TestView
 
urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

views.py

from rest_framework.views import APIView
from rest_framework.response import Response
 
 
class TestView(APIView):
    def dispatch(self, request, *args, **kwargs):
        """
        請求到來之后,都要執(zhí)行dispatch方法呕缭,dispatch方法根據(jù)請求方式不同觸發(fā) get/post/put等方法
         
        注意:APIView中的dispatch方法有好多好多的功能
        """
        return super().dispatch(request, *args, **kwargs)
 
    def get(self, request, *args, **kwargs):
        return Response('GET請求堵泽,響應(yīng)內(nèi)容')
 
    def post(self, request, *args, **kwargs):
        return Response('POST請求,響應(yīng)內(nèi)容')
 
    def put(self, request, *args, **kwargs):
        return Response('PUT請求恢总,響應(yīng)內(nèi)容')
總結(jié):

請求到達后迎罗,首先在APIView的 dispatch方法觸發(fā)。

2离熏、認證和授權(quán):

a 用戶URL傳入的token認證:
urls.py

from django.conf.urls import url, include
from web.viewsimport TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions

token_list = [
    'sfsfss123kuf3j123',
    'asijnfowerkkf9812',
]


class TestAuthentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用戶認證佳谦,如果驗證成功后返回元組: (用戶,用戶Token)
        :param request: 
        :return: 
            None,表示跳過該驗證;
                如果跳過了所有認證滋戳,默認用戶和Token和使用配置文件進行設(shè)置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER()
                else:
                    self.user = None
        
                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                else:
                    self.auth = None
            (user,token)表示驗證通過并設(shè)置用戶名和Token钻蔑;
            AuthenticationFailed異常
        """
        val = request.query_params.get('token')
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用戶認證失敗")

        return ('登錄用戶', '用戶token')

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        # 驗證失敗時,返回的響應(yīng)頭WWW-Authenticate對應(yīng)的值
        pass


class TestView(APIView):
    authentication_classes = [TestAuthentication, ]
    permission_classes = []

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response('GET請求奸鸯,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求咪笑,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

b娄涩、請求頭認證

from django.conf.urls import url, include
from web.viewsimport TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions

token_list = [
    'sfsfss123kuf3j123',
    'asijnfowerkkf9812',
]


class TestAuthentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用戶認證窗怒,如果驗證成功后返回元組: (用戶,用戶Token)
        :param request: 
        :return: 
            None,表示跳過該驗證;
                如果跳過了所有認證蓄拣,默認用戶和Token和使用配置文件進行設(shè)置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER()
                else:
                    self.user = None
        
                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                else:
                    self.auth = None
            (user,token)表示驗證通過并設(shè)置用戶名和Token扬虚;
            AuthenticationFailed異常
        """
        import base64
        auth = request.META.get('HTTP_AUTHORIZATION', b'')
        if auth:
            auth = auth.encode('utf-8')
        auth = auth.split()
        if not auth or auth[0].lower() != b'basic':
            raise exceptions.AuthenticationFailed('驗證失敗')
        if len(auth) != 2:
            raise exceptions.AuthenticationFailed('驗證失敗')
        username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
        if username == 'alex' and password == '123':
            return ('登錄用戶', '用戶token')
        else:
            raise exceptions.AuthenticationFailed('用戶名或密碼錯誤')

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        return 'Basic realm=api'


class TestView(APIView):
    authentication_classes = [TestAuthentication, ]
    permission_classes = []

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response('GET請求,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求球恤,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求辜昵,響應(yīng)內(nèi)容')

views.py

c. 多個認證規(guī)則
urls.py

from django.conf.urls import url, include
from web.views.s2_auth import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework import exceptions

token_list = [
    'sfsfss123kuf3j123',
    'asijnfowerkkf9812',
]


class Test1Authentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用戶認證,如果驗證成功后返回元組: (用戶,用戶Token)
        :param request: 
        :return: 
            None,表示跳過該驗證咽斧;
                如果跳過了所有認證堪置,默認用戶和Token和使用配置文件進行設(shè)置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER() # 默認值為:匿名用戶
                else:
                    self.user = None

                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默認值為:None
                else:
                    self.auth = None
            (user,token)表示驗證通過并設(shè)置用戶名和Token躬存;
            AuthenticationFailed異常
        """
        import base64
        auth = request.META.get('HTTP_AUTHORIZATION', b'')
        if auth:
            auth = auth.encode('utf-8')
        else:
            return None
        print(auth,'xxxx')
        auth = auth.split()
        if not auth or auth[0].lower() != b'basic':
            raise exceptions.AuthenticationFailed('驗證失敗')
        if len(auth) != 2:
            raise exceptions.AuthenticationFailed('驗證失敗')
        username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
        if username == 'alex' and password == '123':
            return ('登錄用戶', '用戶token')
        else:
            raise exceptions.AuthenticationFailed('用戶名或密碼錯誤')

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        # return 'Basic realm=api'
        pass

class Test2Authentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用戶認證,如果驗證成功后返回元組: (用戶,用戶Token)
        :param request: 
        :return: 
            None,表示跳過該驗證舀锨;
                如果跳過了所有認證岭洲,默認用戶和Token和使用配置文件進行設(shè)置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER() # 默認值為:匿名用戶
                else:
                    self.user = None
        
                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默認值為:None
                else:
                    self.auth = None
            (user,token)表示驗證通過并設(shè)置用戶名和Token;
            AuthenticationFailed異常
        """
        val = request.query_params.get('token')
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用戶認證失敗")

        return ('登錄用戶', '用戶token')

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        pass


class TestView(APIView):
    authentication_classes = [Test1Authentication, Test2Authentication]
    permission_classes = []

    def get(self, request, *args, **kwargs):
        print(request.user)
        print(request.auth)
        return Response('GET請求坎匿,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求盾剩,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

d. 認證和權(quán)限
urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import BasePermission

from rest_framework.request import Request
from rest_framework import exceptions

token_list = [
    'sfsfss123kuf3j123',
    'asijnfowerkkf9812',
]


class TestAuthentication(BaseAuthentication):
    def authenticate(self, request):
        """
        用戶認證替蔬,如果驗證成功后返回元組: (用戶,用戶Token)
        :param request: 
        :return: 
            None,表示跳過該驗證彪腔;
                如果跳過了所有認證,默認用戶和Token和使用配置文件進行設(shè)置
                self._authenticator = None
                if api_settings.UNAUTHENTICATED_USER:
                    self.user = api_settings.UNAUTHENTICATED_USER() # 默認值為:匿名用戶
                else:
                    self.user = None
        
                if api_settings.UNAUTHENTICATED_TOKEN:
                    self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默認值為:None
                else:
                    self.auth = None
            (user,token)表示驗證通過并設(shè)置用戶名和Token进栽;
            AuthenticationFailed異常
        """
        val = request.query_params.get('token')
        if val not in token_list:
            raise exceptions.AuthenticationFailed("用戶認證失敗")

        return ('登錄用戶', '用戶token')

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        pass


class TestPermission(BasePermission):
    message = "權(quán)限驗證失敗"

    def has_permission(self, request, view):
        """
        判斷是否有權(quán)限訪問當前請求
        Return `True` if permission is granted, `False` otherwise.
        :param request: 
        :param view: 
        :return: True有權(quán)限;False無權(quán)限
        """
        if request.user == "管理員":
            return True

    # GenericAPIView中g(shù)et_object時調(diào)用
    def has_object_permission(self, request, view, obj):
        """
        視圖繼承GenericAPIView恭垦,并在其中使用get_object時獲取對象時快毛,觸發(fā)單獨對象權(quán)限驗證
        Return `True` if permission is granted, `False` otherwise.
        :param request: 
        :param view: 
        :param obj: 
        :return: True有權(quán)限;False無權(quán)限
        """
        if request.user == "管理員":
            return True


class TestView(APIView):
    # 認證的動作是由request.user觸發(fā)
    authentication_classes = [TestAuthentication, ]

    # 權(quán)限
    # 循環(huán)執(zhí)行所有的權(quán)限
    permission_classes = [TestPermission, ]

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET請求番挺,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求唠帝,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

e. 全局使用
上述操作中均是對單獨視圖進行特殊配置玄柏,如果想要對全局進行配置襟衰,則需要再配置文件中寫入即可。
settings.py

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "web.utils.TestAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "web.utils.TestPermission",
    ],
}

settings.py

urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response

class TestView(APIView):

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET請求粪摘,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求瀑晒,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py
3. 用戶訪問次數(shù)/頻率限制

a徘意、基于用戶IP限制訪問頻率

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import time
from rest_framework.views import APIView
from rest_framework.response import Response

from rest_framework import exceptions
from rest_framework.throttling import BaseThrottle
from rest_framework.settings import api_settings

# 保存訪問記錄
RECORD = {
    '用戶IP': [12312139, 12312135, 12312133, ]
}


class TestThrottle(BaseThrottle):
    ctime = time.time

    def get_ident(self, request):
        """
        根據(jù)用戶IP和代理IP苔悦,當做請求者的唯一IP
        Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR
        if present and number of proxies is > 0. If not use all of
        HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR.
        """
        xff = request.META.get('HTTP_X_FORWARDED_FOR')
        remote_addr = request.META.get('REMOTE_ADDR')
        num_proxies = api_settings.NUM_PROXIES

        if num_proxies is not None:
            if num_proxies == 0 or xff is None:
                return remote_addr
            addrs = xff.split(',')
            client_addr = addrs[-min(num_proxies, len(addrs))]
            return client_addr.strip()

        return ''.join(xff.split()) if xff else remote_addr

    def allow_request(self, request, view):
        """
        是否仍然在允許范圍內(nèi)
        Return `True` if the request should be allowed, `False` otherwise.
        :param request: 
        :param view: 
        :return: True,表示可以通過椎咧;False表示已超過限制玖详,不允許訪問
        """
        # 獲取用戶唯一標識(如:IP)

        # 允許一分鐘訪問10次
        num_request = 10
        time_request = 60

        now = self.ctime()
        ident = self.get_ident(request)
        self.ident = ident
        if ident not in RECORD:
            RECORD[ident] = [now, ]
            return True
        history = RECORD[ident]
        while history and history[-1] <= now - time_request:
            history.pop()
        if len(history) < num_request:
            history.insert(0, now)
            return True

    def wait(self):
        """
        多少秒后可以允許繼續(xù)訪問
        Optionally, return a recommended number of seconds to wait before
        the next request.
        """
        last_time = RECORD[self.ident][0]
        now = self.ctime()
        return int(60 + last_time - now)


class TestView(APIView):
    throttle_classes = [TestThrottle, ]

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET請求,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求勤讽,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求蟋座,響應(yīng)內(nèi)容')

    def throttled(self, request, wait):
        """
        訪問次數(shù)被限制時,定制錯誤信息
        """

        class Throttled(exceptions.Throttled):
            default_detail = '請求被限制.'
            extra_detail_singular = '請 {wait} 秒之后再重試.'
            extra_detail_plural = '請 {wait} 秒之后再重試.'

        raise Throttled(wait)

views.py

b脚牍、基于用戶IP顯示訪問頻率(利用Django緩存)
settings.py

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'test_scope': '10/m',
    },
}

urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response

from rest_framework import exceptions
from rest_framework.throttling import SimpleRateThrottle


class TestThrottle(SimpleRateThrottle):

    # 配置文件定義的顯示頻率的Key
    scope = "test_scope"

    def get_cache_key(self, request, view):
        """
        Should return a unique cache-key which can be used for throttling.
        Must be overridden.

        May return `None` if the request should not be throttled.
        """
        if not request.user:
            ident = self.get_ident(request)
        else:
            ident = request.user

        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }


class TestView(APIView):
    throttle_classes = [TestThrottle, ]

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET請求向臀,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求莫矗,響應(yīng)內(nèi)容')

    def throttled(self, request, wait):
        """
        訪問次數(shù)被限制時飒硅,定制錯誤信息
        """

        class Throttled(exceptions.Throttled):
            default_detail = '請求被限制.'
            extra_detail_singular = '請 {wait} 秒之后再重試.'
            extra_detail_plural = '請 {wait} 秒之后再重試.'

        raise Throttled(wait)

views.py

c砂缩、view中限制請求頻率
settings.py

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'xxxxxx': '10/m',
    },
}

urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response

from rest_framework import exceptions
from rest_framework.throttling import ScopedRateThrottle


# 繼承 ScopedRateThrottle
class TestThrottle(ScopedRateThrottle):

    def get_cache_key(self, request, view):
        """
        Should return a unique cache-key which can be used for throttling.
        Must be overridden.

        May return `None` if the request should not be throttled.
        """
        if not request.user:
            ident = self.get_ident(request)
        else:
            ident = request.user

        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }


class TestView(APIView):
    throttle_classes = [TestThrottle, ]

    # 在settings中獲取 xxxxxx 對應(yīng)的頻率限制值
    throttle_scope = "xxxxxx"

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET請求,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求三娩,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求庵芭,響應(yīng)內(nèi)容')

    def throttled(self, request, wait):
        """
        訪問次數(shù)被限制時,定制錯誤信息
        """

        class Throttled(exceptions.Throttled):
            default_detail = '請求被限制.'
            extra_detail_singular = '請 {wait} 秒之后再重試.'
            extra_detail_plural = '請 {wait} 秒之后再重試.'

        raise Throttled(wait)

views.py

d. 匿名時用IP限制+登錄時用Token限制
settings.py

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    'DEFAULT_THROTTLE_RATES': {
        'luffy_anon': '10/m',
        'luffy_user': '20/m',
    },
}

settings.py

urls.py

from django.conf.urls import url, include
from web.views.s3_throttling import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response

from rest_framework.throttling import SimpleRateThrottle


class LuffyAnonRateThrottle(SimpleRateThrottle):
    """
    匿名用戶雀监,根據(jù)IP進行限制
    """
    scope = "luffy_anon"

    def get_cache_key(self, request, view):
        # 用戶已登錄双吆,則跳過 匿名頻率限制
        if request.user:
            return None

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }


class LuffyUserRateThrottle(SimpleRateThrottle):
    """
    登錄用戶,根據(jù)用戶token限制
    """
    scope = "luffy_user"

    def get_ident(self, request):
        """
        認證成功時:request.user是用戶對象会前;request.auth是token對象
        :param request: 
        :return: 
        """
        # return request.auth.token
        return "user_token"

    def get_cache_key(self, request, view):
        """
        獲取緩存key
        :param request: 
        :param view: 
        :return: 
        """
        # 未登錄用戶好乐,則跳過 Token限制
        if not request.user:
            return None

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }


class TestView(APIView):
    throttle_classes = [LuffyUserRateThrottle, LuffyAnonRateThrottle, ]

    def get(self, request, *args, **kwargs):
        # self.dispatch
        print(request.user)
        print(request.auth)
        return Response('GET請求,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求瓦宜,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求蔚万,響應(yīng)內(nèi)容')

views.py

e. 全局使用
settings.py

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'api.utils.throttles.throttles.LuffyAnonRateThrottle',
        'api.utils.throttles.throttles.LuffyUserRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '10/day',
        'user': '10/day',
        'luffy_anon': '10/m',
        'luffy_user': '20/m',
    },
}

settings
4、版本

a. 基于url的get傳參方式
如:/users?version=v1

REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允許的版本
    'VERSION_PARAM': 'version'          # URL中獲取值的key
}

settings.py

urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view(),name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning


class TestView(APIView):
    versioning_class = QueryParameterVersioning

    def get(self, request, *args, **kwargs):

        # 獲取版本
        print(request.version)
        # 獲取版本管理的類
        print(request.versioning_scheme)

        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)

        return Response('GET請求临庇,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求反璃,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

b. 基于url的正則方式
如:/v1/users/
settings.py

REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允許的版本
    'VERSION_PARAM': 'version'          # URL中獲取值的key
}

settings.py

urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning


class TestView(APIView):
    versioning_class = URLPathVersioning

    def get(self, request, *args, **kwargs):
        # 獲取版本
        print(request.version)
        # 獲取版本管理的類
        print(request.versioning_scheme)

        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)

        return Response('GET請求假夺,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求淮蜈,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

c. 基于 accept 請求頭方式
如:Accept: application/json; version=1.0
settings.py

REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允許的版本
    'VERSION_PARAM': 'version'          # URL中獲取值的key
}

settings.py

urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view(), name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import AcceptHeaderVersioning


class TestView(APIView):
    versioning_class = AcceptHeaderVersioning

    def get(self, request, *args, **kwargs):
        # 獲取版本 HTTP_ACCEPT頭
        print(request.version)
        # 獲取版本管理的類
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)

        return Response('GET請求已卷,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求梧田,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

d. 基于主機名方法
如:v1.example.com
settings.py

ALLOWED_HOSTS = ['*']
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',  # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 允許的版本
    'VERSION_PARAM': 'version'  # URL中獲取值的key
}

settings.py

urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view(), name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import HostNameVersioning


class TestView(APIView):
    versioning_class = HostNameVersioning

    def get(self, request, *args, **kwargs):
        # 獲取版本
        print(request.version)
        # 獲取版本管理的類
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)

        return Response('GET請求侧蘸,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求裁眯,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

e. 基于django路由系統(tǒng)的namespace
如:example.com/v1/users/
settings.py

REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',  # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 允許的版本
    'VERSION_PARAM': 'version'  # URL中獲取值的key
}

urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^v1/', ([
                      url(r'test/', TestView.as_view(), name='test'),
                  ], None, 'v1')),
    url(r'^v2/', ([
                      url(r'test/', TestView.as_view(), name='test'),
                  ], None, 'v2')),

]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import NamespaceVersioning


class TestView(APIView):
    versioning_class = NamespaceVersioning

    def get(self, request, *args, **kwargs):
        # 獲取版本
        print(request.version)
        # 獲取版本管理的類
        print(request.versioning_scheme)
        # 反向生成URL
        reverse_url = request.versioning_scheme.reverse('test', request=request)
        print(reverse_url)

        return Response('GET請求闺魏,響應(yīng)內(nèi)容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求未状,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

f. 全局使用
settings.py

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",
    'DEFAULT_VERSION': 'v1',
    'ALLOWED_VERSIONS': ['v1', 'v2'],
    'VERSION_PARAM': 'version' 
}

settings.py
5. 解析器

根據(jù)請求頭 content-type 選擇對應(yīng)的解析器就請求體內(nèi)容進行處理析桥。
a. 僅處理請求頭content-type為application/json的請求體
urls.py

from django.conf.urls import url, include
from web.views.s5_parser import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]

urls.py

views.py

from django.conf.urls import url, include
from web.views.s5_parser import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]

urls.py

b. 僅處理請求頭content-type為application/x-www-form-urlencoded 的請求體
urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FormParser


class TestView(APIView):
    parser_classes = [FormParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 獲取請求的值司草,并使用對應(yīng)的JSONParser進行處理
        print(request.data)

        # application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
        print(request.POST)
        print(request.FILES)

        return Response('POST請求泡仗,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求埋虹,響應(yīng)內(nèi)容')

views.py

c. 僅處理請求頭content-type為multipart/form-data的請求體
urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import MultiPartParser


class TestView(APIView):
    parser_classes = [MultiPartParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 獲取請求的值,并使用對應(yīng)的JSONParser進行處理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data時娩怎,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST請求搔课,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/" method="post" enctype="multipart/form-data">
    <input type="text" name="user" />
    <input type="file" name="img">

    <input type="submit" value="提交">

</form>
</body>
</html>

upload.html

d. 僅上傳文件
urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/(?P<filename>[^/]+)', TestView.as_view(), name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import FileUploadParser


class TestView(APIView):
    parser_classes = [FileUploadParser, ]

    def post(self, request, filename, *args, **kwargs):
        print(filename)
        print(request.content_type)

        # 獲取請求的值截亦,并使用對應(yīng)的JSONParser進行處理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data時爬泥,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST請求柬讨,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/test/f1.numbers" method="post" enctype="multipart/form-data">
    <input type="text" name="user" />
    <input type="file" name="img">

    <input type="submit" value="提交">

</form>
</body>
</html>

upload.html

e. 同時多個Parser
當同時使用多個parser時袍啡,rest framework會根據(jù)請求頭content-type自動進行比對踩官,并使用對應(yīng)parser
urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser


class TestView(APIView):
    parser_classes = [JSONParser, FormParser, MultiPartParser, ]

    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 獲取請求的值,并使用對應(yīng)的JSONParser進行處理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data時境输,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST請求蔗牡,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應(yīng)內(nèi)容')

views.py

f. 全局使用
settings.py

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser'
        'rest_framework.parsers.FormParser'
        'rest_framework.parsers.MultiPartParser'
    ]

}

settings.py

urls.py

from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response


class TestView(APIView):
    def post(self, request, *args, **kwargs):
        print(request.content_type)

        # 獲取請求的值嗅剖,并使用對應(yīng)的JSONParser進行處理
        print(request.data)
        # application/x-www-form-urlencoded 或 multipart/form-data時辩越,request.POST中才有值
        print(request.POST)
        print(request.FILES)
        return Response('POST請求,響應(yīng)內(nèi)容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求信粮,響應(yīng)內(nèi)容')

views.py

注意:個別特殊的值可以通過Django的request對象 request._request 來進行獲取

6黔攒、序列化

official link:http://www.django-rest-framework.org/api-guide/serializers/
Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can ten be easily rendered into Json,XML or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

Attention:

The serializers in REST framework work very simliarly to Django's Form and ModelForm classes.

序列化用于對用戶請求數(shù)據(jù)進行驗證和數(shù)據(jù)進行序列化
a、自定義字段
urls.py

from django.conf.urls import url, include
from web.views.s6_serializers import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models


class PasswordValidator(object):
    def __init__(self, base):
        self.base = base

    def __call__(self, value):
        if value != self.base:
            message = 'This field must be %s.' % self.base
            raise serializers.ValidationError(message)

    def set_context(self, serializer_field):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # 執(zhí)行驗證之前調(diào)用,serializer_fields是當前字段對象
        pass


class UserSerializer(serializers.Serializer):
    ut_title = serializers.CharField(source='ut.title')
    user = serializers.CharField(min_length=6)
    pwd = serializers.CharField(error_messages={'required': '密碼不能為空'}, validators=[PasswordValidator('666')])


class TestView(APIView):
    def get(self, request, *args, **kwargs):

        # 序列化强缘,將數(shù)據(jù)庫查詢字段序列化為字典
        data_list = models.UserInfo.objects.all()
        ser = UserSerializer(instance=data_list, many=True)
        # 或
        # obj = models.UserInfo.objects.all().first()
        # ser = UserSerializer(instance=obj, many=False)
        return Response(ser.data)

    def post(self, request, *args, **kwargs):
        # 驗證亏钩,對請求發(fā)來的數(shù)據(jù)進行驗證
        ser = UserSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data)
        else:
            print(ser.errors)

        return Response('POST請求,響應(yīng)內(nèi)容')

views.py

b. 基于Model自動生成字段

from django.conf.urls import url, include
from web.views.s6_serializers import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models


class PasswordValidator(object):
    def __init__(self, base):
        self.base = str(base)

    def __call__(self, value):
        if value != self.base:
            message = 'This field must be %s.' % self.base
            raise serializers.ValidationError(message)

    def set_context(self, serializer_field):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # 執(zhí)行驗證之前調(diào)用,serializer_fields是當前字段對象
        pass

class ModelUserSerializer(serializers.ModelSerializer):

    user = serializers.CharField(max_length=32)

    class Meta:
        model = models.UserInfo
        fields = "__all__"
        # fields = ['user', 'pwd', 'ut']
        depth = 2
        extra_kwargs = {'user': {'min_length': 6}, 'pwd': {'validators': [PasswordValidator(666), ]}}
        # read_only_fields = ['user']


class TestView(APIView):
    def get(self, request, *args, **kwargs):

        # 序列化欺旧,將數(shù)據(jù)庫查詢字段序列化為字典
        data_list = models.UserInfo.objects.all()
        ser = ModelUserSerializer(instance=data_list, many=True)
        # 或
        # obj = models.UserInfo.objects.all().first()
        # ser = UserSerializer(instance=obj, many=False)
        return Response(ser.data)

    def post(self, request, *args, **kwargs):
        # 驗證,對請求發(fā)來的數(shù)據(jù)進行驗證
        print(request.data)
        ser = ModelUserSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data)
        else:
            print(ser.errors)

        return Response('POST請求蛤签,響應(yīng)內(nèi)容')

views.py

c. 生成URL
urls.py

from django.conf.urls import url, include
from web.views.s6_serializers import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
    url(r'detail/(?P<pk>\d+)/', TestView.as_view(), name='detail'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models


class PasswordValidator(object):
    def __init__(self, base):
        self.base = str(base)

    def __call__(self, value):
        if value != self.base:
            message = 'This field must be %s.' % self.base
            raise serializers.ValidationError(message)

    def set_context(self, serializer_field):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # 執(zhí)行驗證之前調(diào)用,serializer_fields是當前字段對象
        pass


class ModelUserSerializer(serializers.ModelSerializer):
    ut = serializers.HyperlinkedIdentityField(view_name='detail')
    class Meta:
        model = models.UserInfo
        fields = "__all__"

        extra_kwargs = {
            'user': {'min_length': 6},
            'pwd': {'validators': [PasswordValidator(666),]},
        }



class TestView(APIView):
    def get(self, request, *args, **kwargs):

        # 序列化辞友,將數(shù)據(jù)庫查詢字段序列化為字典
        data_list = models.UserInfo.objects.all()
        ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})
        # 或
        # obj = models.UserInfo.objects.all().first()
        # ser = UserSerializer(instance=obj, many=False)
        return Response(ser.data)

    def post(self, request, *args, **kwargs):
        # 驗證,對請求發(fā)來的數(shù)據(jù)進行驗證
        print(request.data)
        ser = ModelUserSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data)
        else:
            print(ser.errors)

        return Response('POST請求震肮,響應(yīng)內(nèi)容')

views.py

d. 自動生成URL
urls.py

from django.conf.urls import url, include
from web.views.s6_serializers import TestView

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
    url(r'detail/(?P<pk>\d+)/', TestView.as_view(), name='xxxx'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from .. import models


class PasswordValidator(object):
    def __init__(self, base):
        self.base = str(base)

    def __call__(self, value):
        if value != self.base:
            message = 'This field must be %s.' % self.base
            raise serializers.ValidationError(message)

    def set_context(self, serializer_field):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # 執(zhí)行驗證之前調(diào)用,serializer_fields是當前字段對象
        pass


class ModelUserSerializer(serializers.HyperlinkedModelSerializer):
    ll = serializers.HyperlinkedIdentityField(view_name='xxxx')
    tt = serializers.CharField(required=False)

    class Meta:
        model = models.UserInfo
        fields = "__all__"
        list_serializer_class = serializers.ListSerializer

        extra_kwargs = {
            'user': {'min_length': 6},
            'pwd': {'validators': [PasswordValidator(666), ]},
            'url': {'view_name': 'xxxx'},
            'ut': {'view_name': 'xxxx'},
        }


class TestView(APIView):
    def get(self, request, *args, **kwargs):
        # # 序列化称龙,將數(shù)據(jù)庫查詢字段序列化為字典
        data_list = models.UserInfo.objects.all()
        ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})
        # # 如果Many=True
        # # 或
        # # obj = models.UserInfo.objects.all().first()
        # # ser = UserSerializer(instance=obj, many=False)
        return Response(ser.data)

    def post(self, request, *args, **kwargs):
        # 驗證,對請求發(fā)來的數(shù)據(jù)進行驗證
        print(request.data)
        ser = ModelUserSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data)
        else:
            print(ser.errors)

        return Response('POST請求戳晌,響應(yīng)內(nèi)容')

views.py
7鲫尊、分頁

a. 根據(jù)頁碼進行分頁
urls.py

from django.conf.urls import url, include
from rest_framework import routers
from web.views import s9_pagination

urlpatterns = [
    url(r'^test/', s9_pagination.UserViewSet.as_view()),
]

urs.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework import serializers
from .. import models

from rest_framework.pagination import PageNumberPagination


class StandardResultsSetPagination(PageNumberPagination):
    # 默認每頁顯示的數(shù)據(jù)條數(shù)
    page_size = 1
    # 獲取URL參數(shù)中設(shè)置的每頁顯示數(shù)據(jù)條數(shù)
    page_size_query_param = 'page_size'

    # 獲取URL參數(shù)中傳入的頁碼key
    page_query_param = 'page'

    # 最大支持的每頁顯示的數(shù)據(jù)條數(shù)
    max_page_size = 1


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class UserViewSet(APIView):
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().order_by('-id')

        # 實例化分頁對象,獲取數(shù)據(jù)庫中的分頁數(shù)據(jù)
        paginator = StandardResultsSetPagination()
        page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)

        # 序列化對象
        serializer = UserSerializer(page_user_list, many=True)

        # 生成分頁和數(shù)據(jù)
        response = paginator.get_paginated_response(serializer.data)
        return response

views.py

b. 位置和個數(shù)進行分頁
url.py

from django.conf.urls import url, include
from web.views import s9_pagination

urlpatterns = [
    url(r'^test/', s9_pagination.UserViewSet.as_view()),
]

urls.py

view.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework import serializers
from .. import models

from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination


class StandardResultsSetPagination(LimitOffsetPagination):
    # 默認每頁顯示的數(shù)據(jù)條數(shù)
    default_limit = 10
    # URL中傳入的顯示數(shù)據(jù)條數(shù)的參數(shù)
    limit_query_param = 'limit'
    # URL中傳入的數(shù)據(jù)位置的參數(shù)
    offset_query_param = 'offset'
    # 最大每頁顯得條數(shù)
    max_limit = None

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class UserViewSet(APIView):
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().order_by('-id')

        # 實例化分頁對象沦偎,獲取數(shù)據(jù)庫中的分頁數(shù)據(jù)
        paginator = StandardResultsSetPagination()
        page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)

        # 序列化對象
        serializer = UserSerializer(page_user_list, many=True)

        # 生成分頁和數(shù)據(jù)
        response = paginator.get_paginated_response(serializer.data)
        return response

views.py

c. 游標分頁

from django.conf.urls import url, include
from web.views import s9_pagination

urlpatterns = [
    url(r'^test/', s9_pagination.UserViewSet.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework import serializers
from .. import models

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination


class StandardResultsSetPagination(CursorPagination):
    # URL傳入的游標參數(shù)
    cursor_query_param = 'cursor'
    # 默認每頁顯示的數(shù)據(jù)條數(shù)
    page_size = 2
    # URL傳入的每頁顯示條數(shù)的參數(shù)
    page_size_query_param = 'page_size'
    # 每頁顯示數(shù)據(jù)最大條數(shù)
    max_page_size = 1000

    # 根據(jù)ID從大到小排列
    ordering = "id"



class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class UserViewSet(APIView):
    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().order_by('-id')

        # 實例化分頁對象疫向,獲取數(shù)據(jù)庫中的分頁數(shù)據(jù)
        paginator = StandardResultsSetPagination()
        page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)

        # 序列化對象
        serializer = UserSerializer(page_user_list, many=True)

        # 生成分頁和數(shù)據(jù)
        response = paginator.get_paginated_response(serializer.data)
        return response

views.py
8、路由系統(tǒng)

a.自定義路由
urls.py

from django.conf.urls import url, include
from web.views import s11_render

urlpatterns = [
    url(r'^test/$', s11_render.TestView.as_view()),
    url(r'^test\.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view()),
    url(r'^test/(?P<pk>[^/.]+)/$', s11_render.TestView.as_view()),
    url(r'^test/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view())
]

urls.py

views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .. import models


class TestView(APIView):
    def get(self, request, *args, **kwargs):
        print(kwargs)
        print(self.renderer_classes)
        return Response('...')

views.py

b. 半自動路由
urls.py

from django.conf.urls import url, include
from web.views import s10_generic

urlpatterns = [
    url(r'^test/$', s10_generic.UserViewSet.as_view({'get': 'list', 'post': 'create'})),
    url(r'^test/(?P<pk>\d+)/$', s10_generic.UserViewSet.as_view(
        {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer

views.py

c. 全自動路由
urls.py

from django.conf.urls import url, include
from rest_framework import routers
from web.views import s10_generic


router = routers.DefaultRouter()
router.register(r'users', s10_generic.UserViewSet)

urlpatterns = [
    url(r'^', include(router.urls)),
]

urls.py

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer

views.py
9. 視圖

a. GenericViewSet
urls.py

from django.conf.urls import url, include
from web.views.s7_viewset import TestView

urlpatterns = [
    url(r'test/', TestView.as_view({'get':'list'}), name='test'),
    url(r'detail/(?P<pk>\d+)/', TestView.as_view({'get':'list'}), name='xxxx'),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework import viewsets
from rest_framework.response import Response


class TestView(viewsets.GenericViewSet):
    def list(self, request, *args, **kwargs):
        return Response('...')

    def add(self, request, *args, **kwargs):
        pass

    def delete(self, request, *args, **kwargs):
        pass

    def edit(self, request, *args, **kwargs):
        pass

views.py

b. ModelViewSet(自定義URL)

from django.conf.urls import url, include
from web.views import s10_generic

urlpatterns = [
    url(r'^test/$', s10_generic.UserViewSet.as_view({'get': 'list', 'post': 'create'})),
    url(r'^test/(?P<pk>\d+)/$', s10_generic.UserViewSet.as_view(
        {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers
from .. import models


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class UserViewSet(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserSerializer

views.py

c. ModelViewSet(rest framework路由)
urls.py

from django.conf.urls import url, include
from rest_framework import routers
from app01 import views

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)

# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
    url(r'^', include(router.urls)),
]

urls.py

views.py

from rest_framework import viewsets
from rest_framework import serializers


class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.User
        fields = ('url', 'username', 'email', 'groups')


class GroupSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Group
        fields = ('url', 'name')
        
class UserViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer


class GroupViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited.
    """
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

views.py
10. 渲染器

根據(jù) 用戶請求URL 或 用戶可接受的類型豪嚎,篩選出合適的 渲染組件搔驼。
用戶請求URL:

http://127.0.0.1:8000/test/?format=json
http://127.0.0.1:8000/test.json
用戶請求頭:

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
a. json

訪問URL:

http://127.0.0.1:8000/test/?format=json
http://127.0.0.1:8000/test.json
http://127.0.0.1:8000/test/
urls.py

from django.conf.urls import url, include
from web.views import s11_render

urlpatterns = [
    url(r'^test/$', s11_render.TestView.as_view()),
    url(r'^test\.(?P<format>[a-z0-9]+)', s11_render.TestView.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers

from rest_framework.renderers import JSONRenderer

from .. import models


class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class TestView(APIView):
    renderer_classes = [JSONRenderer, ]

    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all()
        ser = TestSerializer(instance=user_list, many=True)
        return Response(ser.data)

views.py

b. 表格

訪問URL:

http://127.0.0.1:8000/test/?format=admin
http://127.0.0.1:8000/test.admin
http://127.0.0.1:8000/test/

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers

from rest_framework.renderers import AdminRenderer

from .. import models


class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class TestView(APIView):
    renderer_classes = [AdminRenderer, ]

    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all()
        ser = TestSerializer(instance=user_list, many=True)
        return Response(ser.data)

views.py

c. Form表單

訪問URL:

http://127.0.0.1:8000/test/?format=form
http://127.0.0.1:8000/test.form
http://127.0.0.1:8000/test/


#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers

from rest_framework.renderers import JSONRenderer
from rest_framework.renderers import AdminRenderer
from rest_framework.renderers import HTMLFormRenderer

from .. import models


class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class TestView(APIView):
    renderer_classes = [HTMLFormRenderer, ]

    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().first()
        ser = TestSerializer(instance=user_list, many=False)
        return Response(ser.data)

views.py

d. 自定義顯示模板

訪問URL:

http://127.0.0.1:8000/test/?format=html
http://127.0.0.1:8000/test.html
http://127.0.0.1:8000/test/
urls.py

from django.conf.urls import url, include
from web.views import s11_render

urlpatterns = [
    url(r'^test/$', s11_render.TestView.as_view()),
    url(r'^test\.(?P<format>[a-z0-9]+)', s11_render.TestView.as_view()),
]

urls.py

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.renderers import TemplateHTMLRenderer

from .. import models


class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class TestView(APIView):
    renderer_classes = [TemplateHTMLRenderer, ]

    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().first()
        ser = TestSerializer(instance=user_list, many=False)
        return Response(ser.data, template_name='user_detail.html')

views.py

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ user }}
    {{ pwd }}
    {{ ut }}
</body>
</html>

userdetail.html

e. 瀏覽器格式API+JSON

訪問URL:

http://127.0.0.1:8000/test/?format=api
http://127.0.0.1:8000/test.api
http://127.0.0.1:8000/test/
views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers

from rest_framework.renderers import JSONRenderer
from rest_framework.renderers import BrowsableAPIRenderer

from .. import models


class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"


class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
    def get_default_renderer(self, view):
        return JSONRenderer()


class TestView(APIView):
    renderer_classes = [CustomBrowsableAPIRenderer, ]

    def get(self, request, *args, **kwargs):
        user_list = models.UserInfo.objects.all().first()
        ser = TestSerializer(instance=user_list, many=False)
        return Response(ser.data, template_name='user_detail.html')

views.py
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侈询,隨后出現(xiàn)的幾起案子舌涨,更是在濱河造成了極大的恐慌,老刑警劉巖扔字,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件囊嘉,死亡現(xiàn)場離奇詭異温技,居然都是意外死亡,警方通過查閱死者的電腦和手機扭粱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門舵鳞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人焊刹,你說我怎么就攤上這事系任。” “怎么了虐块?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵俩滥,是天一觀的道長。 經(jīng)常有香客問我贺奠,道長霜旧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任儡率,我火速辦了婚禮挂据,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘儿普。我一直安慰自己崎逃,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布眉孩。 她就那樣靜靜地躺著个绍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浪汪。 梳的紋絲不亂的頭發(fā)上巴柿,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音死遭,去河邊找鬼广恢。 笑死,一個胖子當著我的面吹牛呀潭,可吹牛的內(nèi)容都是我干的钉迷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼钠署,長吁一口氣:“原來是場噩夢啊……” “哼篷牌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起踏幻,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤枷颊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夭苗,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡信卡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了题造。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傍菇。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖界赔,靈堂內(nèi)的尸體忽然破棺而出丢习,到底是詐尸還是另有隱情,我是刑警寧澤淮悼,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布咐低,位于F島的核電站,受9級特大地震影響袜腥,放射性物質(zhì)發(fā)生泄漏见擦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一羹令、第九天 我趴在偏房一處隱蔽的房頂上張望鲤屡。 院中可真熱鬧,春花似錦福侈、人聲如沸酒来。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽役首。三九已至,卻和暖如春显拜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背爹袁。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工远荠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人失息。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓譬淳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親盹兢。 傳聞我的和親對象是個殘疾皇子邻梆,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355