一. 什麼是RESTful
- REST與技術無關,代表的是一種軟體架構風格,REST是Representational State Transfer的簡稱,中文翻譯為“表征狀态轉移”
- REST從資源的角度類審視整個網絡,它将分布在網絡中某個節點的資源通過URL進行辨別,用戶端應用通過URL來擷取資源的表征,獲得這些表征緻使這些應用轉變狀态
- 所有的資料,不過是通過網絡擷取的還是操作(增删改查)的資料,都是資源,将一切資料視為資源是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:指定篩選條件
- 狀态碼
Django Rest Framework Django Rest Framework
常用狀态碼清單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
-
錯誤處理,狀态碼是4xx時,應傳回錯誤資訊,error當做key。
?
1
2
3
{
error:
"Invalid API key"
}
- 傳回結果,針對不同操作,伺服器向使用者傳回的結果應該符合以下規範。
4
5
6
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"
}}
摘自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html
三. 基于Django實作
路由系統:
|
CBV視圖:
7 8 9 10 11 12 13 14 15 16 17 | |
四. 基于Django Rest Framework架構實作
1. 基本流程
url.py
|
views.py
18 19 20 21 | |
上述是rest framework架構基本流程,重要的功能是在APIView的dispatch中觸發。
2. 認證和授權
a. 使用者url傳入的token認證

from django.conf.urls import url, include
from web.viewsimport TestView
urlpatterns = [
url(r'^test/', TestView.as_view()),
]
urls.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和使用配置檔案進行設定
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)表示驗證通過并設定使用者名和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.
"""
# 驗證失敗時,傳回的響應頭WWW-Authenticate對應的值
pass
class TestView(APIView):
authentication_classes = [TestAuthentication, ]
permission_classes = []
def get(self, request, *args, **kwargs):
print(request.user)
print(request.auth)
return Response('GET請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
b. 請求頭認證

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

#!/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和使用配置檔案進行設定
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)表示驗證通過并設定使用者名和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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
c. 多個認證規則

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

#!/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和使用配置檔案進行設定
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)表示驗證通過并設定使用者名和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和使用配置檔案進行設定
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)表示驗證通過并設定使用者名和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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
d. 認證和權限

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

#!/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和使用配置檔案進行設定
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)表示驗證通過并設定使用者名和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 = "權限驗證失敗"
def has_permission(self, request, view):
"""
判斷是否有權限通路目前請求
Return `True` if permission is granted, `False` otherwise.
:param request:
:param view:
:return: True有權限;False無權限
"""
if request.user == "管理者":
return True
# GenericAPIView中get_object時調用
def has_object_permission(self, request, view, obj):
"""
視圖繼承GenericAPIView,并在其中使用get_object時擷取對象時,觸發單獨對象權限驗證
Return `True` if permission is granted, `False` otherwise.
:param request:
:param view:
:param obj:
:return: True有權限;False無權限
"""
if request.user == "管理者":
return True
class TestView(APIView):
# 認證的動作是由request.user觸發
authentication_classes = [TestAuthentication, ]
# 權限
# 循環執行所有的權限
permission_classes = [TestPermission, ]
def get(self, request, *args, **kwargs):
# self.dispatch
print(request.user)
print(request.auth)
return Response('GET請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
e. 全局使用
上述操作中均是對單獨視圖進行特殊配置,如果想要對全局進行配置,則需要再配置檔案中寫入即可。

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

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

#!/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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
3. 使用者通路次數/頻率限制
a. 基于使用者IP限制通路頻率

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

#!/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):
"""
根據使用者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):
"""
是否仍然在允許範圍内
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):
"""
多少秒後可以允許繼續通路
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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
def throttled(self, request, wait):
"""
通路次數被限制時,定制錯誤資訊
"""
class Throttled(exceptions.Throttled):
default_detail = '請求被限制.'
extra_detail_singular = '請 {wait} 秒之後再重試.'
extra_detail_plural = '請 {wait} 秒之後再重試.'
raise Throttled(wait)
b. 基于使用者IP顯示通路頻率(利于Django緩存)

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

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

#!/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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
def throttled(self, request, wait):
"""
通路次數被限制時,定制錯誤資訊
"""
class Throttled(exceptions.Throttled):
default_detail = '請求被限制.'
extra_detail_singular = '請 {wait} 秒之後再重試.'
extra_detail_plural = '請 {wait} 秒之後再重試.'
raise Throttled(wait)
c. view中限制請求頻率

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

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

#!/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 對應的頻率限制值
throttle_scope = "xxxxxx"
def get(self, request, *args, **kwargs):
# self.dispatch
print(request.user)
print(request.auth)
return Response('GET請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
def throttled(self, request, wait):
"""
通路次數被限制時,定制錯誤資訊
"""
class Throttled(exceptions.Throttled):
default_detail = '請求被限制.'
extra_detail_singular = '請 {wait} 秒之後再重試.'
extra_detail_plural = '請 {wait} 秒之後再重試.'
raise Throttled(wait)
d. 匿名時用IP限制+登入時用Token限制

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

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

#!/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):
"""
匿名使用者,根據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):
"""
登入使用者,根據使用者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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')

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
}

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

#!/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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
b. 基于url的正則方式
如:/v1/users/

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

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'),
]

#!/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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
c. 基于 accept 請求頭方式
如:Accept: application/json; version=1.0

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

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

#!/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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
d. 基于主機名方法
如:v1.example.com

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

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

#!/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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
e. 基于django路由系統的namespace
如:example.com/v1/users/

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

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')),
]

#!/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請求,響應内容')
def post(self, request, *args, **kwargs):
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
f. 全局使用

REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': ['v1', 'v2'],
'VERSION_PARAM': 'version'
}
5. 解析器(parser)
根據請求頭 content-type 選擇對應的解析器就請求體内容進行處理。
a. 僅處理請求頭content-type為application/json的請求體

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

#!/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
class TestView(APIView):
parser_classes = [JSONParser, ]
def post(self, request, *args, **kwargs):
print(request.content_type)
# 擷取請求的值,并使用對應的JSONParser進行處理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
b. 僅處理請求頭content-type為application/x-www-form-urlencoded 的請求體

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

#!/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)
# 擷取請求的值,并使用對應的JSONParser進行處理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
c. 僅處理請求頭content-type為multipart/form-data的請求體

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

#!/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)
# 擷取請求的值,并使用對應的JSONParser進行處理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')

<!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. 僅上傳檔案

from django.conf.urls import url, include
from web.views import TestView
urlpatterns = [
url(r'test/(?P<filename>[^/]+)', TestView.as_view(), name='test'),
]

#!/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)
# 擷取請求的值,并使用對應的JSONParser進行處理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')

<!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>
e. 同時多個Parser
當同時使用多個parser時,rest framework會根據請求頭content-type自動進行比對,并使用對應parser

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

#!/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)
# 擷取請求的值,并使用對應的JSONParser進行處理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')

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

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

#!/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)
# 擷取請求的值,并使用對應的JSONParser進行處理
print(request.data)
# application/x-www-form-urlencoded 或 multipart/form-data時,request.POST中才有值
print(request.POST)
print(request.FILES)
return Response('POST請求,響應内容')
def put(self, request, *args, **kwargs):
return Response('PUT請求,響應内容')
注意:個别特殊的值可以通過Django的request對象 request._request 來進行擷取
6. 序列化
序列化用于對使用者請求資料進行驗證和資料進行序列化。
a. 自定義字段

from django.conf.urls import url, include
from web.views.s6_serializers import TestView
urlpatterns = [
url(r'test/', TestView.as_view(), name='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 .. 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.
"""
# 執行驗證之前調用,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):
# 序列化,将資料庫查詢字段序列化為字典
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):
# 驗證,對請求發來的資料進行驗證
ser = UserSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data)
else:
print(ser.errors)
return Response('POST請求,響應内容')
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'),
]

#!/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.
"""
# 執行驗證之前調用,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):
# 序列化,将資料庫查詢字段序列化為字典
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):
# 驗證,對請求發來的資料進行驗證
print(request.data)
ser = ModelUserSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data)
else:
print(ser.errors)
return Response('POST請求,響應内容')
c. 生成URL

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'),
]

#!/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.
"""
# 執行驗證之前調用,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):
# 序列化,将資料庫查詢字段序列化為字典
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):
# 驗證,對請求發來的資料進行驗證
print(request.data)
ser = ModelUserSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data)
else:
print(ser.errors)
return Response('POST請求,響應内容')
7. 分頁
a. 根據頁碼進行分頁

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

#!/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):
# 預設每頁顯示的資料條數
page_size = 1
# 擷取URL參數中設定的每頁顯示資料條數
page_size_query_param = 'page_size'
# 擷取URL參數中傳入的頁碼key
page_query_param = 'page'
# 最大支援的每頁顯示的資料條數
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')
# 執行個體化分頁對象,擷取資料庫中的分頁資料
paginator = StandardResultsSetPagination()
page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
# 序列化對象
serializer = UserSerializer(page_user_list, many=True)
# 生成分頁和資料
response = paginator.get_paginated_response(serializer.data)
return response
b. 位置和個數進行分頁

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

#!/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):
# 預設每頁顯示的資料條數
default_limit = 10
# URL中傳入的顯示資料條數的參數
limit_query_param = 'limit'
# URL中傳入的資料位置的參數
offset_query_param = 'offset'
# 最大每頁顯得條數
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')
# 執行個體化分頁對象,擷取資料庫中的分頁資料
paginator = StandardResultsSetPagination()
page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
# 序列化對象
serializer = UserSerializer(page_user_list, many=True)
# 生成分頁和資料
response = paginator.get_paginated_response(serializer.data)
return response
c. 遊标分頁

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

#!/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傳入的遊标參數
cursor_query_param = 'cursor'
# 預設每頁顯示的資料條數
page_size = 2
# URL傳入的每頁顯示條數的參數
page_size_query_param = 'page_size'
# 每頁顯示資料最大條數
max_page_size = 1000
# 根據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')
# 執行個體化分頁對象,擷取資料庫中的分頁資料
paginator = StandardResultsSetPagination()
page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
# 序列化對象
serializer = UserSerializer(page_user_list, many=True)
# 生成分頁和資料
response = paginator.get_paginated_response(serializer.data)
return response
8. 路由系統
a. 自定義路由

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())
]

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('...')
b. 半自動路由

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'})),
]

#!/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
c. 全自動路由

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)),
]

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
9. 視圖
a. GenericViewSet

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'),
]

#!/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
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'})),
]

#!/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
c. ModelViewSet(rest framework路由)

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)),
]

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
10. 渲染器
根據 使用者請求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/

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()),
]

#!/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)
b. 表格
- http://127.0.0.1:8000/test/?format=admin
- http://127.0.0.1:8000/test.admin

#!/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)
c. Form表單
- http://127.0.0.1:8000/test/?format=form
- http://127.0.0.1:8000/test.form

#!/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)
d. 自定義顯示模闆
- http://127.0.0.1:8000/test/?format=html
- http://127.0.0.1:8000/test.html

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()),
]

#!/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')

<!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
- http://127.0.0.1:8000/test/?format=api
- http://127.0.0.1:8000/test.api

#!/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')
注意:如果同時多個存在時,自動根據URL字尾來選擇渲染器。