Django-restframework

今日內容概要:javascript

  1.restful api html

  2.django rest frame work框架java

   - 認證python

     - 權限jquery

   - 訪問頻率限制web

         - 版本ajax

         - 解析器redis

         - 序列化數據庫

         - 分頁django

         - 視圖

         - 路由

         - 渲染器(通常不用)

以前知識點複習:

  1.在django中若是header裏的content-type = application/json,django的視圖裏經過request.post獲取不到值,須要經過request.body獲取json傳過來的值

  2.關於跨域問題:

    1.同源策略:

只有當協議、端口、和域名都相同的頁面,則兩個頁面具備相同的源。只要網站的 協議名protocol、 主機host、 端口號port 這三個中的任意一個不一樣,網站間的數據請求與傳輸便構成了跨域調用,會受到同源策略的限制。 
​ 同源策略限制從一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。瀏覽器的同源策略,出於防範跨站腳本的攻擊,禁止客戶端腳本(如 JavaScript)對不一樣域的服務進行跨站調用(一般指使用XMLHttpRequest請求)。

     解決辦法

    1.使用ajax的jsonp 只能應用get請求

    2.使用jquery的jsonp插件 請求方式不僅侷限於get請求,還能夠是post請求,但從服務器從獲取的數據依然是jsonp格式

    3.cors

    瀏覽器將CORS請求分紅兩類:簡單請求(simple request)和非簡單請求(not-so-simple request).

    知足簡單請求的方法:

(1) 請求方法是如下三種方法之一:
    HEAD
    GET
    POST
(2)HTTP的頭信息不超出如下幾種字段:
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

 

對於簡單請求,瀏覽器直接發出CORS請求。具體來講,就是在頭信息之中,增長一個Origin字段。
下面是一個例子,瀏覽器發現此次跨源AJAX請求是簡單請求,就自動在頭信息之中,添加一個Origin字段。
#簡單請求,通過cors後效果
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

  非簡單請求:

非簡單請求是那種對服務器有特殊要求的請求,好比請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。

非簡單請求的CORS請求,會在正式通訊以前,增長一次HTTP查詢請求,稱爲"預檢"請求(preflight)。

瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可使用哪些HTTP動詞和頭信息字段。只有獲得確定答覆,瀏覽器纔會發出正式的XMLHttpRequest請求,不然就報錯。

  下面是一波javascript代碼:

var url = 'http://api.test.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

  上面代碼中,HTTP請求的方法是PUT,而且發送一個自定義頭信息X-Custom-Header。瀏覽器發現,這是一個非簡單請求,就自動發出一個"預檢"請求,要求服務器確承認以這樣請求。下面是這個"預檢"請求的HTTP頭信息。

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。頭信息裏面,關鍵字段是Origin,表示請求來自哪一個源。

  除了Origin字段,"預檢"請求的頭信息包括兩個特殊字段。

  (1)Access-Control-Request-Method

  該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT

  (2)Access-Control-Request-Headers

  該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,上例是X-Custom-Header

預檢請求迴應:

  服務器收到"預檢"請求之後,檢查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段之後,確認容許跨源請求,就能夠作出迴應。

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

  上面的HTTP迴應中,關鍵的是Access-Control-Allow-Origin字段,表示http://api.bob.com能夠請求數據。該字段也能夠設爲星號,表示贊成任意跨源請求。

Access-Control-Allow-Origin: *

   若是瀏覽器否認了"預檢"請求,會返回一個正常的HTTP迴應,可是沒有任何CORS相關的頭信息字段。這時,瀏覽器就會認定,服務器不一樣意預檢請求,所以觸發一個錯誤,被XMLHttpRequest對象的onerror回調函數捕獲。控制檯會打印出以下的報錯信息。

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

   服務器迴應的其餘CORS相關字段以下。

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000

  cors請求原理圖:

    瀏覽器和服務器

    

  這樣cors遇到複雜請求,就會向後段發送兩次請求

  3.django設置model表時候的設置choice字段,

    food_choices = ((1,"蘋果"),(2,"鴨梨"))

    food_type = models.IntegerField(choices=food_choices,default=1)

  在查詢的時候,默認food_type是一個id,如何從foot_type獲取值?

    model.Food.first().get_food_type_display 返回choices的內容

  4.複習面向對象的封裝

request = 請求相關全部的數據
#在原有request對象的基礎上封裝了更多方法和屬性
class NewRequest(object):
    def __init__(self, req, parser, auth):
        self._request = req
        self.parser = parser
        self.auth = auth


obj = NewRequest(request, 'x1', 'x2')
obj.parser
obj.auth
obj._request
obj._request.POST

  5.列表生成式 

    v = [ i for i in range(10)]

示例:
class Auth1:
    pass


class Auth2:
    pass

class Foo(object):
    cls_list = [Auth1, Auth2]

    def get_cls_list_obj(self):
        # return [Auth1(),Auth2() ]
        return [cl() for cl in self.cls_list] #返回的是一堆對象的列表

obj = Foo()
ret = obj.get_cls_list_obj()
print(ret)

   6.給你一個字符串 "utils.auth.Auth",幫我找到Auth類,並實例化。

    經過importlib(import_module(xxx)) --> 反射,執行

   7.django能夠鏈接memcache和redis作緩存,鏈接redis須要安裝一個django-redis

 

1、什麼是接口?

  基於cbv實現接口

urls裏:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'user/$',views.UserView.as_view())
]

views裏:

from django.shortcuts import render,redirect,HttpResponse
from django.views import View
from .models import UserInfo


class UserView(View):
    def get(self,request,*args,**kwargs):
        return HttpResponse("get.method")

    def post(self,request,*args,**kwargs):
        return HttpResponse("post.method")

 

遇到返回json格式出現中文,如何顯示正常結果?

class UserView(View):
    def get(self,request,*args,**kwargs):
        result = {
            "status":True,
            "data":"我是中文",
        }
        # return JsonResponse(result,json_dumps_params={'ensure_ascii':False}) #django自帶的jsonResponse原理一樣是json.dumps
        return HttpResponse(json.dumps(result,ensure_ascii=False))

 

經過源碼查看jsonResponse的實現

class JsonResponse(HttpResponse):
    def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
                 json_dumps_params=None, **kwargs):
        if safe and not isinstance(data, dict):
            raise TypeError(
                'In order to allow non-dict objects to be serialized set the '
                'safe parameter to False.'
            )
        if json_dumps_params is None:
            json_dumps_params = {}
            
        設置了個content_type爲json
        kwargs.setdefault('content_type', 'application/json')

        #json.dumps序列化
        data = json.dumps(data, cls=encoder, **json_dumps_params) 
        super(JsonResponse, self).__init__(content=data, **kwargs)

  

經過源碼分析view裏調用dispatch方法

#執行過程: url --> as_view() --> View類裏找as_view方法 

@classonlymethod
def as_view(cls, **initkwargs):
    """
    Main entry point for a request-response process.
    """
    for key in initkwargs:
        if key in cls.http_method_names:
            raise TypeError("You tried to pass in the %s method name as a "
                            "keyword argument to %s(). Don't do that."
                            % (key, cls.__name__))
        if not hasattr(cls, key):
            raise TypeError("%s() received an invalid keyword %r. as_view "
                            "only accepts arguments that are already "
                            "attributes of the class." % (cls.__name__, key))

    #裝飾器最終返回self.dispatch 進行執行,這個self爲cbv裏面類的名字
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
        return self.dispatch(request, *args, **kwargs)
    view.view_class = cls
    view.view_initkwargs = initkwargs

    # take name and docstring from class
    update_wrapper(view, cls, updated=())

    # and possible attributes set by decorators
    # like csrf_exempt from dispatch
    update_wrapper(view, cls.dispatch, assigned=())
    return view

  

分析原生dispatch裏的流程

#view裏面支持以下8種方法
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

def dispatch(self, request, *args, **kwargs):
	#這裏的原理是獲取請求method,與當前類的方法進行反射,最後執行該反射結果
    if request.method.lower() in self.http_method_names:
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)



def http_method_not_allowed(self, request, *args, **kwargs):
    #若是不在上述8種方法裏,返回405,method error
    logger.warning(
        'Method Not Allowed (%s): %s', request.method, request.path,
        extra={'status_code': 405, 'request': request}
    )
    return http.HttpResponseNotAllowed(self._allowed_methods())

  

 2、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:指定篩選條件

狀態碼
	OK - [GET]:服務器成功返回用戶請求的數據,該操做是冪等的(Idempotent)。
	CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。
	Accepted - [*]:表示一個請求已經進入後臺排隊(異步任務)
	NO CONTENT - [DELETE]:用戶刪除數據成功。
	INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操做,該操做是冪等的。
	Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。
	Forbidden - [*] 表示用戶獲得受權(與401錯誤相對),可是訪問是被禁止的。
	NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操做,該操做是冪等的。
	Not Acceptable - [GET]:用戶請求的格式不可得(好比用戶請求JSON格式,可是隻有XML格式)。
	Gone -[GET]:用戶請求的資源被永久刪除,且不會再獲得的。
	Unprocesable entity - [POST/PUT/PATCH] 當建立一個對象時,發生一個驗證錯誤。
	INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將沒法判斷髮出的請求是否成功。

	更多看這裏:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

錯誤處理,狀態碼是4xx時,應返回錯誤信息,error當作key。
	{
	    error: "Invalid API key"
	}
返回結果,針對不一樣操做,服務器向用戶返回的結果應該符合如下規範。
	GET /collection:返回資源對象的列表(數組)
	GET /collection/resource:返回單個資源對象
	POST /collection:返回新生成的資源對象
	PUT /collection/resource:返回完整的資源對象
	PATCH /collection/resource:返回完整的資源對象
	DELETE /collection/resource:返回一個空文檔
	
Hypermedia API,RESTful API最好作到Hypermedia,即返回結果中提供連接,連向其餘API方法,使得用戶不查文檔,也知道下一步應該作什麼。
	{"link": {
	  "rel":   "collection https://www.example.com/zoos",
	  "href":  "https://api.example.com/zoos",
	  "title": "List of zoos",
	  "type":  "application/vnd.yourformat+json"
	}}

3、Django rest frame work 實現

url.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):
        """
        請求到來以後,都要執行dispatch方法,dispatch方法根據請求方式不一樣觸發 get/post/put等方法
         
        注意:APIView中的dispatch方法有好多好多的功能
        """
        return super().dispatch(request, *args, **kwargs)
 
    def get(self, request, *args, **kwargs):
        return Response('GET請求,響應內容')
 
    def post(self, request, *args, **kwargs):
        return Response('POST請求,響應內容')
 
    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')

上述爲rest_frame的主要功能 更多方法在dispatch裏 

 

from rest_framework.response import Response  這個返回的是一個友好頁面,前提得在app裏註冊rest_framework

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'rest_framework',
]

rest_framework ,csrf中間件不影響緣由源碼剖析

rest_framework/views.py

請求進來執行視圖as_view方法 --> APIVIEW --> as_view最終返回view將view裏封裝了一些參數 --> dispatch方法
@classmethod
def as_view(cls, **initkwargs):
    if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
        def force_evaluation():
            raise RuntimeError(
                'Do not evaluate the `.queryset` attribute directly, '
                'as the result will be cached and reused between requests. '
                'Use `.all()` or call `.get_queryset()` instead.'
            )
        cls.queryset._fetch_all = force_evaluation
  #繼承mro父類的as_view方法,view = self.dispatch(request,*args,**kwargs)
    view = super(APIView, cls).as_view(**initkwargs)
    view.cls = cls
    view.initkwargs = initkwargs

    # all other authentication is CSRF exempt.
    return csrf_exempt(view) #這個地方解決了rest不會受csrf_token影響

 

request._request是老的request 從源碼裏 Request類的__init__方法能夠查看到 

class Request(object):
	def __init__(self, request, parsers=None, authenticators=None,
	             negotiator=None, parser_context=None)

	    self._request = request #這裏聲明瞭self._request = request
	    self.parsers = parsers or ()
	    self.authenticators = authenticators or ()
	    self.negotiator = negotiator or self._default_negotiator()
	    self.parser_context = parser_context

APIVIEW的dispatch 

def dispatch(self, request, *args, **kwargs):
    """
    `.dispatch()` is pretty much the same as Django's regular dispatch,
    but with extra hooks for startup, finalize, and exception handling.
    """
    self.args = args
    self.kwargs = kwargs
    #request返回Request對象(包含原有request對象,封裝了新的功能
    	1.認證類對象的列表
    	2.parser類對象的列表
    )
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    self.headers = self.default_response_headers  # deprecate?

    try:
    	#執行視圖以前又執行了initial
        self.initial(request, *args, **kwargs)

        # Get the appropriate handler method
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),
                              self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed

        response = handler(request, *args, **kwargs)

    except Exception as exc:
        response = self.handle_exception(exc)

    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response


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

    self.format_kwarg = self.get_format_suffix(**kwargs)
    neg = self.perform_content_negotiation(request)
    request.accepted_renderer, request.accepted_media_type = neg

    version, scheme = self.determine_version(request, *args, **kwargs) 
    #DEFAULT_VERSIONING_CLASS的類determine_version(request, *args, **kwargs)方法,version_class
    request.version, request.versioning_scheme = version, scheme #返回是一個元組

    # Ensure that the incoming request is permitted
   
    self.perform_authentication(request) #認證相關類
    self.check_permissions(request) #權限相關類
    self.check_throttles(request)  #限速相關類 

 

一.版本

   a.基於url get傳參 

  如:/users?version=v1

REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 容許的版本
    'VERSION_PARAM': 'version'          # URL中獲取值的key
}
settings.py
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view(),name='test'),
]
urls.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請求,響應內容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')
views.py

  b.基於url正則方式

  如:/v1/users/

REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 容許的版本
    'VERSION_PARAM': 'version'          # URL中獲取值的key
}
settings.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
#!/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請求,響應內容')
views.py

  c.基於accept頭方式

  如:Accept: application/json; version=1.0

REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',            # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 容許的版本
    'VERSION_PARAM': 'version'          # URL中獲取值的key
}
settings.py
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view(), name='test'),
]
urls.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請求,響應內容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')
views.py

 

    d.基於主機名方法

  如:v1.example.com 

ALLOWED_HOSTS = ['*']
REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',  # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 容許的版本
    'VERSION_PARAM': 'version'  # URL中獲取值的key
}
settings.py
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view(), name='test'),
]
urls.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請求,響應內容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')
views.py

 

    e.基於django路由系統的namespace

  如:example.com/v1/users/  

REST_FRAMEWORK = {
    'DEFAULT_VERSION': 'v1',  # 默認版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 容許的版本
    'VERSION_PARAM': 'version'  # URL中獲取值的key
}
settings.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
#!/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請求,響應內容')
views.py

 

   f.全局使用

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

 

示例url_version獲取源碼

class URLPathVersioning(BaseVersioning):
    """
    To the client this is the same style as `NamespaceVersioning`.
    The difference is in the backend - this implementation uses
    Django's URL keyword arguments to determine the version.

    An example URL conf for two views that accept two different versions.

    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
        url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
    ]

    GET /1.0/something/ HTTP/1.1
    Host: example.com
    Accept: application/json
    """
    invalid_version_message = _('Invalid version in URL path.')

    def determine_version(self, request, *args, **kwargs):
        # self.default_version = settings裏的DEFAULT_VERSION
        version = kwargs.get(self.version_param, self.default_version)
        if not self.is_allowed_version(version):
            raise exceptions.NotFound(self.invalid_version_message)
        return version

    def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
        if request.version is not None:
            kwargs = {} if (kwargs is None) else kwargs
            kwargs[self.version_param] = request.version

        return super(URLPathVersioning, self).reverse(
            viewname, args, kwargs, request, format, **extra
        )

  

注意點:

   路由裏寫: 

  url(r'^test/(?P<go>\w+)/', views.order),
  默認傳到**kwarg裏
    from kwargs {'go': 'go'}

2、認證與權限 

     a.用戶傳入token認證

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

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

 在utils設置全局token認證

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from app01 import models

class TokenAuthtication(BaseAuthentication):
    def authenticate(self, request):
        """

        :param request:
        :return:
            (user,auth) 表示認證成功,並將元組分別複製給request.user/request.auth
             raise AuthenticationFailed('認證失敗') 表示認證失敗
             None, 表示匿名用戶
        """
        token = request.query_params.get('token')
        if not token:
            raise AuthenticationFailed('用戶Token未攜帶')

        token_obj = models.UserToken.objects.filter(token=token).first()
        if not token_obj:
            raise AuthenticationFailed('token已失效或錯誤')

        return (token_obj.user.username,token_obj)
utils/auth.py
"""
post 請求 http://127.0.0.1:8000/api/v1/auth   body爲
{
        'username':'liujiliang',
        'password':'123123'
}

http://127.0.0.1:8000/api/v1/user?token=c84cfc0a-33dc-4d05-be59-368570594a77
"""
class AuthView(APIView):
    authentication_classes = [] #認證不須要token
    def post(self,request,*args,**kwargs):
        response = {'code':1000}
        user = request.data.get('username')
        print(user)
        pwd = request.data.get('password')
        print(pwd)
        obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
        if not obj:
            response['code'] = 1001
            response['msg'] = '用戶或密碼錯誤'
            return JsonResponse(response,json_dumps_params={'ensure_ascii':False})

        token = str(uuid.uuid4())
        models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
        response['token'] = token

        return JsonResponse(response,json_dumps_params={'ensure_ascii':False})

class UserView(APIView):
    def get(self,request,*args,**kwargs):
        print(request.user)
        print(request.auth)
        return HttpResponse('user.get')

    def post(self,request,*args,**kwargs):
        return HttpResponse('user.post')
views.py

 

認證總體流程大體流程圖

從源碼查看token認證:

  

def initial(self, request, *args, **kwargs):
    """
    Runs anything that needs to occur prior to calling the method handler.
    """
    self.format_kwarg = self.get_format_suffix(**kwargs)

    # Perform content negotiation and store the accepted info on the request
    neg = self.perform_content_negotiation(request)
    request.accepted_renderer, request.accepted_media_type = neg

    # Determine the API version, if versioning is in use.
    version, scheme = self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme

     #dispatch的initial執行認證相關認證
    self.perform_authentication(request)
    self.check_permissions(request)
    self.check_throttles(request)
dispatch_initial
def perform_authentication(self, request):
    """
    Perform authentication on the incoming request.

    Note that if you override this and simply 'pass', then authentication
    will instead be performed lazily, the first time either
    `request.user` or `request.auth` is accessed.
    """
    #此request爲封裝後的大Request
    request.user
perform_authentication
@property
def user(self):
    """
    Returns the user associated with the current request, as authenticated
    by the authentication classes provided to the request.
    """
    if not hasattr(self, '_user'):
        with wrap_attributeerrors(): 
            self._authenticate()#執行這裏執行Request._authenticate
    return self._user
user
def _authenticate(self):
    """
    Attempt to authenticate the request using each authentication instance
    in turn.
    """
    for authenticator in self.authenticators: #此authenticators在Request初始化的時候已經定義
        try:
            user_auth_tuple = authenticator.authenticate(self) #從這裏能夠看出返回值是一個元組
        except exceptions.APIException:
            self._not_authenticated() #若是拋出異常捕獲到 就返回一個(None,None)
            raise

        if user_auth_tuple is not None:
            self._authenticator = authenticator
            self.user, self.auth = user_auth_tuple 
            return

    self._not_authenticated() #若是沒有認證執行 默認就是(None,None)
_authenticate
authenticators=APIVIEW.get_authenticators() #返回一個對象列表

    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        #配置裏的DEFAULT_AUTHENTICATION_CLASSES
        return [auth() for auth in self.authentication_classes]


    self.authenticators = authenticators or ()
    user_auth_tuple = authenticator.authenticate(self) #執行認證類的authenticate方法
獲取對象的方式

 

從源碼看權限控制:

dispatch 下的initial 方法裏的check_permissions,

    def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions():
            #執行[obj,obj]裏的has_permission返回值是一個布爾值
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
check_permissions
#根據user_id來判斷用戶具備的權限,能夠任意擴展
from rest_framework.permissions import BasePermission

class UserPermission(BasePermission):

    def has_permission(self,request,view):
        user_type_id = request.auth.user.user_type
        if user_type_id > 0:
            return True
        return False

class ManagerPermission(BasePermission):

    def has_permission(self,request,view):
        user_type_id = request.auth.user.user_type
        if user_type_id > 1:
            return True
        return False
permission程序編寫,繼承BasePermission
class BasePermission(object):
    """
    A base class from which all permission classes should inherit.
    """

    def has_permission(self, request, view):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True

    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True
BasePermission源碼

 

    b. 請求頭認證

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

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]
url.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和使用配置文件進行設置
                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請求,響應內容')
views.py

 

    c.多個認證規則

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

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]
url.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和使用配置文件進行設置
                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請求,響應內容')
views.py

   d.認證和權限

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

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]
url.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和使用配置文件進行設置
                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請求,響應內容')
views.py

全局使用: 

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()),
]
url.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請求,響應內容')

    def post(self, request, *args, **kwargs):
        return Response('POST請求,響應內容')

    def put(self, request, *args, **kwargs):
        return Response('PUT請求,響應內容')
views.py

權限不經過返回結果

3、用戶訪問次數或頻率限制

 a . 基於用戶IP訪問頻率限制

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

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]
urls.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):
        """
        根據用戶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)
views.py

b. 基於用戶IP顯示訪問頻率(利於Django緩存)

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': 'C:\\Users\xxxxx\PycharmProjects\MyCelery\\app01\cache',
    }
}

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'test_scope': '10/m',
    },
}
settings.py
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]
urls.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請求,響應內容')

    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)
views.py

 

c.view限制請求頻率

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'xxxxxx': '10/m',
    },
}
settings.py
from django.conf.urls import url, include
from web.views import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]
urls.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 對應的頻率限制值
    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)
views.py

d. 匿名時用IP限制+登陸時用Token限制

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    'DEFAULT_THROTTLE_RATES': {
        'luffy_anon': '10/m',
        'luffy_user': '20/m',
    },
}
settings.py
from django.conf.urls import url, include
from web.views.s3_throttling import TestView

urlpatterns = [
    url(r'^test/', TestView.as_view()),
]
urls.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):
    """
    匿名用戶,根據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請求,響應內容')
views.py

 

e.全局使用

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.py

 

 

限速類定義:

from rest_framework.throttling import SimpleRateThrottle

class UserRateThrottle(SimpleRateThrottle):
    scope = 'user'
    def get_cache_key(self, request, view):
        if request.user:
            # 若是已經登陸,pk
            ident = request.user
        else:
            # 若是沒有登陸,IP
            ident = self.get_ident(request)
        # 'throttle_%(scope)s_%(ident)s'
        #  throttle_user_fengfeng
        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }

    def allow_request(self, request, view):
        if request.auth.user.user_type == 1:
            #這裏也能夠設置速率更改默認配置參數
            # self.num_requests = 3
            # self.duration = 60
            pass
        else:
            self.num_requests = 6
        return super(UserRateThrottle,self).allow_request(request, view)
throttles.py

 

視圖裏註冊throttle_classes

class UserView(APIView):
    permission_classes = [UserPermission,]
    throttle_classes = [UserRateThrottle,]
    def get(self,request,*args,**kwargs):
        self.dispatch
        print(request.user)
        print(request.auth)
        return HttpResponse('user.get')

    def post(self,request,*args,**kwargs):
        return HttpResponse('user.post')
views.py

經過源碼看simpleRateThrottle限速原理

請求進來執行dispatch

def check_throttles(self, request):
    """
    Check if request should be throttled.
    Raises an appropriate exception if the request is throttled.
    """
    for throttle in self.get_throttles(): #執行throttles的allow_request方法 返回爲布爾值
        if not throttle.allow_request(request, self):
            self.throttled(request, throttle.wait())


SimpleRateThrottle的allow_request

def allow_request(self, request, view):
    """
    Implement the check to see if the request should be throttled.

    On success calls `throttle_success`.
    On failure calls `throttle_failure`.
    """
    if self.rate is None:
        return True

    self.key = self.get_cache_key(request, view)
    if self.key is None:
        return True

    #去緩存裏拿去最新的列表   
    self.history = self.cache.get(self.key, [])
    #用時間戳
    self.now = self.timer()

    # Drop any requests from the history which have now passed the
    # throttle duration

    #若是列表存在而且最後一個時間 小於等於 當前時間與間隔的差值,就刪除最後一個值
    while self.history and self.history[-1] <= self.now - self.duration:
        self.history.pop() 
    if len(self.history) >= self.num_requests: #判斷總體長度是否大於最大值,大於的話就failure
        return self.throttle_failure()
    return self.throttle_success() #不然返回success


 def throttle_success(self):
    """
    Inserts the current request's timestamp along with the key
    into the cache.
    """
    self.history.insert(0, self.now) #成功的話 在列表裏頭部插入
    self.cache.set(self.key, self.history, self.duration)
    return True

若是allow_request返回false,執行throttled方法 傳入限制類.wait()方法


def wait(self):
    """
    Returns the recommended next request time in seconds.
    """
    #當前時間和最後一個時間作一個差
    if self.history:
        remaining_duration = self.duration - (self.now - self.history[-1])
    else:
        remaining_duration = self.duration

    available_requests = self.num_requests - len(self.history) + 1
    if available_requests <= 0:
        return None

    return remaining_duration / float(available_requests)
總體代碼關鍵函數

 

簡易圖,示範:

#時間戳
{
	throttle_xiaohua: [1527322520.5065649, 1527322512.5065649,1527322509.5065649,],
}



第一步:去列表中pop已經失效
		
		1527322599.5065649 - 60 = 1527322539.5065649
		
		xiaohua:[]
		
第二步:計算個數:
		xiaohua:[1527322599.5065649,] 

 

返回結果以下:

      

4、序列化和表單驗證

序列化用於對用戶請求數據進行驗證和數據進行序列化。

a . 自定義字段

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
#!/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請求,響應內容')
views.py

 

 

b.基礎定義字段

from rest_framework import serializers
class HostSerializer(serializers.Serializer):
    hostname = serializers.CharField()
    # port = serializers.IntegerField()
    port = serializers.IntegerField()
    sa_id = serializers.IntegerField(source="sa.id")
    sa_name = serializers.CharField(source="sa.name")
    htype_id = serializers.CharField(source="htype")
    htype_title = serializers.CharField(source="get_htype_display")
    departs = serializers.SerializerMethodField() #設置method方法

    #鉤子函數用get_字段
    def get_departs(self,obj):
        return [{'id':row.id,'title':row.title} for row in obj.departs.all()]



class HostView(APIView):
    authentication_classes = []
    #多個對象組成的queryset
    def get(self,request,*args,**kwargs):
        ret  = {"code":2001}
        try:
            queryset = models.Host.objects.all()
            ser = HostSerializer(instance=queryset,many=True)
            ret["data"]  = ser.data
        except Exception as e :
            ret["code"] = 2002
            ret["msg"] = "獲取數據失敗"
        return Response(ret)
   

    #單個對象查詢方式
    def get(self,request,*args,**kwargs):
        ret  = {"code":2001}
        try:
            obj = models.Host.objects.first()
            ser = HostSerializer(instance=obj,many=False)
            ret["data"]  = ser.data
        except Exception as e :
            ret["code"] = 2002
            ret["msg"] = "獲取數據失敗"
        return Response(ret)
views.py

 

c.基於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
#!/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請求,響應內容')
views.py

 

d.弱化版的

class NewHostSerializer(serializers.ModelSerializer):
    #這種方法沒法解決choice字段
    htype_title = serializers.Serializer(source="get_htype_display")
    xxx = serializers.SerializerMethodField()
    #相似於django的ModuleForm
    class Meta:
        model = models.Host
        # fields = '__all__' __all__爲所有
        fields = ['hostname', 'port', 'htype', 'htype_title', 'sa', 'departs', 'xxx']
        depth = 1 #查詢深度能夠增長

    def get_xxx(self,obj):
        return [{'id':row.id,'title':row.title} for row in obj.departs.all()]

class HostView(APIView):
    authentication_classes = []
    #多個對象組成的queryset
    def get(self,request,*args,**kwargs):
        ret  = {"code":2001}
        try:
            queryset = models.Host.objects.all()
            ser = NewHostSerializer(instance=queryset,many=True)
            ret["data"]  = ser.data
        except Exception as e :
            print(e)
            ret["code"] = 2002
            ret["msg"] = "獲取數據失敗"
        return Response(ret)
views.py

 

f.生成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'),
]
urls.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.
        """
        # 執行驗證以前調用,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請求,響應內容')
views.py

 

g.自動生成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='xxxx'),
]
urls.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.
        """
        # 執行驗證以前調用,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):
        # # 序列化,將數據庫查詢字段序列化爲字典
        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):
        # 驗證,對請求發來的數據進行驗證
        print(request.data)
        ser = ModelUserSerializer(data=request.data)
        if ser.is_valid():
            print(ser.validated_data)
        else:
            print(ser.errors)

        return Response('POST請求,響應內容')
views.py

 

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


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請求,響應內容')
view.py

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

        # 獲取請求的值,並使用對應的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請求,響應內容')
views.py

 

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

        # 獲取請求的值,並使用對應的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請求,響應內容')
views.py
<!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'),
]
urls.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)

        # 獲取請求的值,並使用對應的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請求,響應內容')
views.py
<!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會根據請求頭content-type自動進行比對,並使用對應parser  get_content_negotiator會進行篩選

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

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
urls.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)

        # 獲取請求的值,並使用對應的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請求,響應內容')
views.py

 

f.全局使用

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

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

urlpatterns = [
    url(r'test/', TestView.as_view(), name='test'),
]
urls.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)

        # 獲取請求的值,並使用對應的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請求,響應內容')
複製代碼
views.py

注意:個別特殊的值能夠經過Django的request對象 request._request 來進行獲取

from rest_framework.parsers import JSONParser,FormParser
class ParserView(APIView):
    authentication_classes = []
    #json返回的是dict,form返回的是querydict,request.data有不一樣的值
    parser_classes = [JSONParser,FormParser]

    def post(self,request,*args,**kwargs):
        print(request.data,type(request.data))
        return Response('...')

6、分頁 

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()),
]
urls.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
views.py

http://127.0.0.1:8000/api/v1/host_list/?page=3

b. 位置和個數進行分頁

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

urlpatterns = [
    url(r'^test/', s9_pagination.UserViewSet.as_view()),
]
urls.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):
    # 默認每頁顯示的數據條數
    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
views.py

 

http://127.0.0.1:8000/api/v1/host_list/?limit=1

http://127.0.0.1:8000/api/v1/host_list/?limit=1&offset=1  offset爲偏移量

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

 

#!/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
views.py

 

http://127.0.0.1:8000/api/v1/host_list/?cursor=cD0y

遊標的另一種使用方法

from rest_framework.pagination import CursorPagination

class MyCursorPagination(CursorPagination):
    page_size = 1
    ordering = 'id'

class PagerSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Host
        fields = ['hostname','port',]

class PagerView(APIView):
    authentication_classes = []
    def get(self,request,*args,**kwargs):
        queryset = models.Host.objects.all().order_by('id')
        pg = MyCursorPagination()
        ret = pg.paginate_queryset(queryset=queryset,request=request,view=self)
        ser = PagerSerializer(instance=ret,many=True)

        response = {
            'code':1000,
            'next':pg.get_next_link(),
            'prev':pg.get_previous_link(),
            'data':ser.data
        }

        return Response(response)
views.py

防止爬蟲

間接引出來一個問題.

數據庫有幾千萬條數據,讓你作分頁,若是在數據庫查詢當前頁id,where page > id 作篩選作limit_offset,對頁碼進行加密,當前頁的最大值和最小值加密

7、路由

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

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

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
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
獲取所有和獲取部分的例子
url(r'^sa/$', views.SaView.as_view()),
url(r'^sa/(?P<pk>\d+)/$', views.SaView.as_view()),

8、視圖

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'),
]
urls.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'})),
]
url.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路由)

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
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
queryset = Group.objects.all() #查詢使用queryset
serializer_class = GroupSerializer #序列化

9、渲染器

根據 用戶請求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/ 
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.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/ 
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
#!/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
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ user }}
    {{ pwd }}
    {{ ut }}
</body>
</html>
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/ 
#!/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

注意:若是同時多個存在時,自動根據URL後綴來選擇渲染器。

 

補充:

  序列化組件 當修改返回字段的時候,能夠設置  read_only=True, write_only=True ,若是是read_only = True 修改的時候, 就不用提交該字段

class HostSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Host
        fields = "__all__"

    course = serializers.SerializerMethodField(read_only=False, write_only=False)
相關文章
相關標籤/搜索