今日內容概要: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
。
預檢請求迴應:
服務器收到"預檢"請求之後,檢查了Origin
、Access-Control-Request-Method
和Access-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
基於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())
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" }}
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 }
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' }
示例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'}
a.用戶傳入token認證
from django.conf.urls import url, include from web.viewsimport TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ]
在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)
""" 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')
認證總體流程大體流程圖
從源碼查看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)
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
@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
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)
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) )
#根據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
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
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請求,響應內容')
全局使用:
REST_FRAMEWORK = { 'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_TOKEN': None, "DEFAULT_AUTHENTICATION_CLASSES": [ "web.utils.TestAuthentication", ], "DEFAULT_PERMISSION_CLASSES": [ "web.utils.TestPermission", ], }
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請求,響應內容')
權限不經過返回結果
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緩存)
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', }, }
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請求,響應內容')
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', }, }
限速類定義:
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)
視圖裏註冊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')
經過源碼看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,]
返回結果以下:
序列化用於對用戶請求數據進行驗證和數據進行序列化。
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.基礎定義字段
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)
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'), ]
#!/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請求,響應內容')
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)
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'), ]
#!/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請求,響應內容')
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'), ]
#!/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請求,響應內容')
根據請求頭 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>
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 get_content_negotiator會進行篩選
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請求,響應內容')
f.全局使用
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 來進行獲取
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('...')
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()), ]
#!/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
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()), ]
#!/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
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()), ]
#!/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
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)
防止爬蟲
間接引出來一個問題.
數據庫有幾千萬條數據,讓你作分頁,若是在數據庫查詢當前頁id,where page > id 作篩選作limit_offset,對頁碼進行加密,當前頁的最大值和最小值加密
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
獲取所有和獲取部分的例子
url(r'^sa/$', views.SaView.as_view()),
url(r'^sa/(?P<pk>\d+)/$', views.SaView.as_view()),
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
queryset = Group.objects.all() #查詢使用queryset serializer_class = GroupSerializer #序列化
根據 用戶請求URL 或 用戶可接受的類型,篩選出合適的 渲染組件。
用戶請求URL:
用戶請求頭:
a. json
訪問URL:
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. 表格
訪問URL:
#!/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表單
訪問URL:
#!/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. 自定義顯示模板
訪問URL:
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>
e. 瀏覽器格式API+JSON
訪問URL:
#!/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後綴來選擇渲染器。
補充:
序列化組件 當修改返回字段的時候,能夠設置 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)