Django REST framework,是1個基於Django搭建 REST風格API的框架;html
一、什麼是API呢?前端
API就是訪問便可獲取數據的url地址,下面是一個最簡單的 Django API,訪問http://127.0.0.1:8000/,返回用戶列表;python
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^user/', views.user), ]
from django.shortcuts import render,HttpResponse from django.http import JsonResponse users=['大波','張開','人間' ] def user(request): return JsonResponse(users,safe=False)
二、什麼是restfunAPI呢?linux
若是新增增長用戶功能,再向用戶暴露1個API接口 http://127.0.0.1:8000/useradd/?new_user='A'web
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^user/', views.user), url(r'^useradd/', views.user_add), #增長用戶功能 ]
from django.shortcuts import render,HttpResponse from django.http import JsonResponse users=['大波','張開','人間' ] def user(request): return JsonResponse(users,safe=False) def user_add(request): #增長用戶功能 new_user=request.GET.get('new_user') users.append(new_user) return JsonResponse(users,safe=False)
在增長用戶功能上再添加刪除、編輯用戶功能 向用戶暴露 4個api接口sql
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^user/', views.user), #顯示用戶列表 url(r'^useradd/$', views.user_add), #增長用戶功能 url(r'^userdelete/\d+/$', views.user_delete), # 刪除用戶功能 url(r'^useredit/\d+/$', views.user_edit), #編輯用戶信息 ]
若是按照這種開發模式,隨着項目功能的完善,弊端也會暴露出來,維護的API接口也會愈來愈多,在協做開發中出現種種問題;數據庫
因而一種API編寫規範出現了,他就是RESTful規範;django
在1個視圖中經過判斷用戶的請求method不一樣(get/post/put/delete),後臺作不一樣的操做;這樣就能夠只暴露1個API給用戶而且維護了代碼的整潔,這就是restful 規範之一;json
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^user/', views.user), #顯示用戶列表 url(r'^user/(?P<pk>\d+)/$',views.user), #編輯和刪除須要一個PK 在URL中 ]
from django.shortcuts import render,HttpResponse from django.http import JsonResponse users=['大波','張開','人間' ] def user(request,*args,**kwargs): if request.method=='GET': #若是請求方法爲 GET 獲取數據列表 return JsonResponse(users,safe=False) elif request.method=='POST': # 若是請求方法爲 POST 添加列表數據 return JsonResponse(users, safe=False) elif request.method=='PUT': # 若是請求方法爲 PUT,更新操做 pk=kwargs.get('pk') return JsonResponse(users, safe=False) elif request.method=='DELETE': pk=kwargs.get('pk') # 若是請求方法爲 DELETE,刪除操做 return JsonResponse(users, safe=False)
總結:後端
restful 風格API有不少規範,接下來咱們介紹一下它都有哪些規範?
RESTful API的設計須要遵循不少規範,可是隻是建議,若是不遵照這種規範也是能夠的,如下是 它部分規範介紹;
一、面向資源
面向資源指得是把網絡中能夠訪問到的任何url都想象成1個資源,這個資源能夠是1個文件、 1張圖片、1個視頻....。
例如:
http://www.le.com/videos/
http://www.le.com/files/
http://www.le.com/pictures/
注意 1級域名後邊(videos、files、pictures)都是名詞
二、版本號
咱們的API不是一成不變的,若是版本迭代也須要體如今url上,以供用戶選擇不一樣版本;
例如:
http://www.le.com/v1/files/ 訪問版本1API資源
http://www.le.com/v2/files/ 訪問版本2API資源
三、返回值
API在放回數據的同時也返回請求的狀態碼;
例如:
return HttpRespose(josn.dumps(ret),status=200) 訪問成功
return HttpRespose(josn.dumps(ret),status=3XX) 權限錯誤
return HttpRespose(josn.dumps(ret),status=4XX) 資源存在
return HttpRespose(josn.dumps(ret),status=5XX) 服務器錯誤
詳細:
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 經常使用狀態碼列表
四、API與用戶的通訊協議,老是使用https協議
五、域名規範
例如:
前端Vue使用:https://www.le.com
後端Django:https://api.le.com 注意會出現跨域問題
六、過濾,經過在url上傳參的形式傳遞搜索條件
七、返回結果: 根據用戶不一樣的操做,服務器放過的結果應該符合如下規範
GET /collection:返回資源對象的列表(數組) GET /collection/resource:返回單個資源對象 POST /collection:返回新生成的資源對象 PUT /collection/resource:返回完整的資源對象 PATCH /collection/resource:返回完整的資源對象 DELETE /collection/resource:返回一個空文檔
八、Hypermedia API,RESTful API最好作到Hypermedia,即返回結果中提供連接,連向其餘API方法,使得用戶不查文檔,也知道下一步應該作什麼。
[ { 'name':'alex', 'age':'18', 'user_type': 'http://www.le.com/api/v1/usergroups/1/' }, { 'name':'alex', 'age':'18', 'user_type': 'DS' }, { 'name':'alex1', 'age':'18', 'user_type': 'DS' }, { 'name':'alex2', 'age':'18', 'user_type': 'DS' } ]
9.總結
restful 規範本質就是1個約束 web 接口,增、刪、改、查方法的規範,方便調用方維護、記憶url;
若是本身去實現1個restful api須要遵循以上不少restful API規則,實現起來比較困難,
因而Django的 Rest framework框架出現了,經過它就能夠無需關注衆多restful規範, 快速實現restful API;
0、安裝 djangorestframwork pip install djangorestframework -i https://pypi.douban.com/simple/ 1、安裝 virtual env(虛擬環境,至關於作了環境隔離) pip install virtualenv 2、建立1個存放虛擬環境的目錄 mkdir virtualenv cd virtualenv 3、建立1個乾淨的虛擬環境(--no-site-packaes) virtualenv --no-site-package Using base prefix 'd:\\python3.6.1' New python executable in D:\virtualenv\ven Installing setuptools, pip, wheel...done. 4、激活虛擬環境 linux source venv/bin/activate DOS cdvirtualenv\venv1\Scripts activate 安裝Django >>> exit() (venv1) D:\virtualenv\venv1\Scripts>pip install django 5、退出虛擬環境 deactivate
Django framework源碼分析
想要深刻了解 Rest framework框架是如何實現restful API的就要分析它的源碼;
Django framework源碼入口
因爲全部CBV首先要執行dispatch方法,全部它就是Django framework源碼的入口
from django.shortcuts import render,HttpResponse from rest_framework.views import APIView #導入 APIView #類繼承了APIView就是 restfulAPI了,可是APIView繼承了Django的view本質是對Django view的一種二次封裝; class UsersView(APIView): 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 #1.二次封裝request(原request,parsers=解析用戶請求的數據,authenticators=認證相關, negotiator=選擇相關, parser_context='封裝的self和參數') request = self.initialize_request(request, *args, **kwargs) self.request = request #二、request被更換爲封裝完畢的request self.headers = self.default_response_headers # deprecate? #三、執行initial 獲取版本信息、 認證、權限、節流 try: 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 get(self,request,*args,**kwargs): self.dispatch() # 執行APIView的 dispatch 也是 framwork的源碼入口 return HttpResponse('...') #
Django framework分析結果
通過對Django framework分析得知 Django restframework的生命週期
1、中間件 2、路由系統 三、CBV/FBV(視圖) 4、執行dispath方法 二次封裝request(原request,解析用戶請求的數據,authenticators=認證相關, negotiator=選擇相關, parser_context='封裝的self和參數') try 獲取版本信息 認證 權限 節流 respose=執行 GET/POST/PUT/DELETE方法 except respose = 異常 finally 處理響應內容
咱們的API不是一成不變的,若是版本迭代就須要以版本號加以區別,讓用戶選擇不一樣的版本;
1.1 版本控制源碼分析
1.2 配置使用
視圖配置
方式1: http://127.0.0.1:8000/users/?version=v1
from django.shortcuts import render,HttpResponse from rest_framework.views import APIView #導入 APIView from rest_framework.versioning import QueryParameterVersioning from rest_framework.versioning import URLPathVersioning class UsersView(APIView): versioning_class=QueryParameterVersioning #http://127.0.0.1:8000/users/?version=v1 def get(self,request,*args,**kwargs): print(request.version) return HttpResponse('...')
方式2:http://127.0.0.1:8000/v3/users/
from rest_framework.views import APIView #導入 APIView from rest_framework.versioning import QueryParameterVersioning from rest_framework.versioning import URLPathVersioning class UsersView(APIView): versioning_class = URLPathVersioning #http://127.0.0.1:8000/v3/users/ def get(self,request,*args,**kwargs): print(request.version) return HttpResponse('...')
全局配置
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 APP ] REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#設置使用的類 "VERSION_PARAM":"version", #設置默認參數名稱 "DEFAULT_VERSION":'v1', #設置默認版本 "ALLOWED_VERSIONS":['v1','v2'] #設置容許的版本 }
獲取和反向生成url
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^(?P<version>\w+)/users/$', views.UsersView.as_view(),name='xxxxx'), # url(r'^(?P<version>\w+)/users/$', views.UsersView.as_view()), ]
from django.shortcuts import render,HttpResponse from rest_framework.views import APIView #導入 APIView # from rest_framework.versioning import QueryParameterVersioning # from rest_framework.versioning import URLPathVersioning class UsersView(APIView): # versioning_class=QueryParameterVersioning #http://127.0.0.1:8000/users/?version=v1 # versioning_class = URLPathVersioning #http://127.0.0.1:8000/v3/users/ def get(self,request,*args,**kwargs): print(request.versioning_scheme.reverse(viewname='xxxxx',request=request)) #反向生成URL return HttpResponse('...')
根據用戶請求(tocken)作用戶身份認證,成功request.user能獲取到用戶名,不然AnonymousUser /none;
2.1 認證源碼分析
APIView 設置認證相關功能
Request類調用認證功能
class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch() print(self.authentication_classes ) return HttpResponse('...')
class APIView(View): '''1.3 去系統配置文件,獲取認證相關的類s [BasicAuthentication(),SessionAuthentication()] ''' authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES def dispatch(self, request, *args, **kwargs): self.args = args self.kwargs = kwargs #1 二次封裝 request(self.initialize_request) request = self.initialize_request(request, *args, **kwargs) self.request = request #替換成新的request self.headers = self.default_response_headers # deprecate? try: #二、執行認證功能 self.initial(request, *args, **kwargs) except Exception as exc: pass return self.response def initialize_request(self, request, *args, **kwargs): '''1.1 執行initialize_reques,執行authenticators=self.get_authenticators() 調用get_authenticators()獲取[auth對象,auth對象] ''' return Request( request, authenticators=self.get_authenticators(), ) def get_authenticators(self): '''1.2 get_authenticators 去系統配置文件中獲取 認證相關的類, 返回列表''' return [auth() for auth in self.authentication_classes] #2.1 認證功能調用perform_authentication方法 def initial(self, request, *args, **kwargs): self.perform_authentication(request) #2.2 perform_authentication調用了 request類的user方法 def perform_authentication(self, request): request.user
#二、調用request的user方法,執行認證功能 class Request(): def __init__(request,authenticators=None): self._request = request self.authenticators = authenticators or () #or 元祖 #2.1 request.user方法調用了 _authenticat方法 @property def user(self): if not hasattr(self, '_user'): self._authenticate() return self._user def _authenticate(self): # 2.2遍歷 self.authenticators,request初始環節封裝的[認證對象,認證對象 ],並執行認證對象的authenticate方法 for authenticator in self.authenticators: ''' 異常終止整個循環 返回元祖,整個循環終止 返回None,進入下一次循環 ''' try: user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return self._not_authenticated() #2.3 若是循環出現 異常,則設置 request.user = 匿名用戶 def _not_authenticated(self): 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
2.2 使用配置
經過對 APIView 認證環節的源碼分析,得知認證環節本質就是執行 BasicAuthentication、SessionAuthentication類中的authenticate方法;
若是authenticate方法執行成功 return (username,None ) ,則設置 request.user屬性爲username,不然設置爲AnonymousUser匿名用戶;
若是authenticate方法執行成功 return None ,則繼續執行 下一個認證類的authenticate方法;
若是authenticate方法 raise exceptions.AuthenticationFailed('認證失敗')拋出異常,則整個認證環節終止;
局部視圖使用認證功能
# 源碼中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK 裏的東西 REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#設置使用的類 "VERSION_PARAM":"version", #設置默認參數名稱 "DEFAULT_VERSION":'v1', #設置默認版本 "ALLOWED_VERSIONS":['v1','v2'] , #設置容許的版本 # 設置爲 None,沒有經過認證的用戶,request.user匿名用戶 =None "UNAUTHENTICATED_USER":None, # 設置爲UNAUTHENTICATED_TOKEN tocken =None 'UNAUTHENTICATED_TOKEN':None }
from django.shortcuts import render,HttpResponse from rest_framework.views import APIView #導入 APIView from rest_framework.authentication import BasicAuthentication from rest_framework.authentication import SessionAuthentication from rest_framework.request import Request from rest_framework import exceptions class CustomAuthentication(BasicAuthentication): def authenticate(self, request): return ('egon',None ) #authenticate 返回哪一個用戶,request.user=哪一個用戶 def authenticate_header(self, request): pass class UsersView(APIView): authentication_classes =[CustomAuthentication ] def get(self,request,*args,**kwargs): print(request.user) return HttpResponse('...')
自定製認證失敗錯誤信息
from rest_framework.authentication import SessionAuthentication from rest_framework.request import Request from rest_framework import exceptions class CustomAuthentication(BasicAuthentication): def authenticate(self, request): #自定義認證失敗異常信息 raise exceptions.AuthenticationFailed('認證失敗') def authenticate_header(self, request): pass class UsersView(APIView): authentication_classes =[CustomAuthentication ] def get(self,request,*args,**kwargs): print(request.user) return HttpResponse('...')
全局視圖使用認證功能
from rest_framework.authentication import BasicAuthentication from rest_framework.authentication import SessionAuthentication class CustomAuthentication(BasicAuthentication): def authenticate(self, request): return ('alex',None) def authenticate_header(self, request): pass
# 源碼中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK 裏的東西 REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#設置使用的類 "VERSION_PARAM":"version", #設置默認參數名稱 "DEFAULT_VERSION":'v1', #設置默認版本 "ALLOWED_VERSIONS":['v1','v2'] , #設置容許的版本 # 設置爲 None,沒有經過認證的用戶,request.user匿名用戶 =None "UNAUTHENTICATED_USER":None, # 設置爲UNAUTHENTICATED_TOKEN tocken =None 'UNAUTHENTICATED_TOKEN':None, "DEFAULT_AUTHENTICATION_CLASSES":( "app01.utils.CustomAuthentication", ), }
from rest_framework.authentication import SessionAuthentication from rest_framework import exceptions class UsersView(APIView): def get(self,request,*args,**kwargs): print(request.user) # print(self.dispatch()) return HttpResponse('...')
2.3 擴展
基於token的用戶認證
token:服務端動態生成的1串用來檢驗用戶身份的字符串,能夠放在header、cokies、url參數(安全性較差)、請求體(CSRF token);
token和session相似,不一樣於 session的是token比較靈活,不只僅能夠cokies裏;
from django.shortcuts import render,HttpResponse from rest_framework.views import APIView #導入 APIView from rest_framework.authentication import BasicAuthentication from rest_framework.authentication import SessionAuthentication from rest_framework import exceptions from rest_framework.request import Request from rest_framework.response import Response from app01 import models from django.http import JsonResponse def gen_tcoken(username): import random,hashlib,time ctime=str(time.time()) hash=hashlib.md5(username.encode('utf-8')) hash.update(ctime.encode('utf-8')) return hash.hexdigest() class CustomAuthentication(BasicAuthentication): #認證類 def authenticate(self, request): #從 url的參數中獲取客戶端攜帶的tocken tocken=request.query_params.get('tocken') #去數據庫中檢查tocken是否存在? tocken_obj=models.Token.objects.filter(token=tocken).first() #若是存在 if tocken_obj: #注意還能夠返回對象 return (tocken_obj.user,tocken_obj) #不然 自定義錯誤信息 認證失敗 raise exceptions.AuthenticationFailed('認證失敗') def authenticate_header(self, request): pass class ZhanggenAuthentication(): ''' 局部使用認證功能的狀況下,優先繼承該類 ''' authentication_classes = [CustomAuthentication] class LoginView(APIView): #容許匿名用戶訪問 def post(self, request, *args, **kwargs): ret = {'coede': 1000, 'msg': ''} username = request.data.get('username') pwd = request.data.get('password') user = models.Userinfo.objects.filter(user=username, pwd=pwd) if user: # 根據user=user去查找,若是找到更新 若是沒有找到建立defaults={} 中的數據 tk = gen_tcoken(username) models.Token.objects.update_or_create(user=user, defaults={'token': tk}) ret['coede'] = 1001 ret['token'] = tk else: ret['msg'] = '用戶名或者密碼錯誤' return JsonResponse(ret) class IndexView(ZhanggenAuthentication,APIView): #不容許匿名用戶訪問 def get(self,request,*args,**kwargs): print(request.user) print(request.user.user) return Response('經過認證!')
from django.db import models class Userinfo(models.Model): user=models.CharField(max_length=32) pwd=models.CharField(max_length=64) email=models.CharField(max_length=64) user_type_choices=( (1,'普通用戶'), (2,'文藝用戶'), (3,'其餘用戶'), ) user_type_id=models.IntegerField(choices=user_type_choices,default=1) #設置 one to one 1個用戶不能在不一樣設備上登陸 #設置 Freikey 支持1個用戶 在不一樣設備上同時登陸 class Token(models.Model): user=models.OneToOneField(Userinfo) token=models.CharField(max_length=64)
def gen_tcoken(username): import random,hashlib,time ctime=str(time.time()) hash=hashlib.md5(username.encode('utf-8')) hash.update(ctime.encode('utf-8')) return hash.hexdigest()
class LoginView(APIView): #容許匿名用戶訪問 def post(self, request, *args, **kwargs): ret = {'coede': 1000, 'msg': ''} username = request.data.get('username') pwd = request.data.get('password') user = models.Userinfo.objects.filter(user=username, pwd=pwd) if user: # 根據user=user去查找,若是找到更新 若是沒有找到建立defaults={} 中的數據 tk = gen_tcoken(username) models.Token.objects.update_or_create(user=user, defaults={'token': tk}) ret['coede'] = 1001 ret['token'] = tk else: ret['msg'] = '用戶名或者密碼錯誤' return JsonResponse(ret)
class CustomAuthentication(BasicAuthentication): #認證類 def authenticate(self, request): #從 url的參數中獲取客戶端攜帶的tocken tocken=request.query_params.get('tocken') #去數據庫中檢查tocken是否存在? tocken_obj=models.Token.objects.filter(token=tocken).first() #若是存在 if tocken_obj: #注意還能夠返回對象 return (tocken_obj.user,tocken_obj) #不然 自定義錯誤信息 認證失敗 raise exceptions.AuthenticationFailed('認證失敗') def authenticate_header(self, request): pass
class ZhanggenAuthentication(): ''' 局部使用認證功能的狀況下,優先繼承該類 ''' authentication_classes = [CustomAuthentication] class IndexView(ZhanggenAuthentication,APIView): #不容許匿名用戶訪問 def get(self,request,*args,**kwargs): print(request.user) print(request.user.user) return Response('經過認證!')
認證失敗後定製響應頭
def authenticate_header(self, request): return 'Basic realm=api' #設置響應頭,認證失敗後,瀏覽器彈窗,再向server發送請求
使用RestAPI認證功能小結
一、建立2張表userinfo 和token表
二、認證類的authenticate方法去請求頭中獲取token信息,而後去token表中查詢token是否存在;
三、查詢到token 是正經常使用戶(返回 用戶名)不然爲匿名用戶(raise異常終止認證、或者 return none進行下一個認證)
四、局部應用
方式1::哪一個CBV須要認證在類中定義authentication_classes =[CustomAuthentication ]
方式2:額外定義1個類,CBV多繼承
方式3:全局配置使用認證功能,那個CBV不使用authentication_classes =[ ]
五、全局使用 在配置文件中配置 ,注意從新建立一個模塊,把認證類放裏面;
社會三、六、9,如何實現對API接口的訪問權限設置呢?基於IP ?和用戶名?
需求:普通用戶對UserGroupView視圖無訪問權限
局部使用:
from django.db import models class Userinfo(models.Model): user=models.CharField(max_length=32) pwd=models.CharField(max_length=64) email=models.CharField(max_length=64) user_type_choices=( (1,'普通用戶'), (2,'文藝用戶'), (3,'其餘用戶'), ) user_type_id=models.IntegerField(choices=user_type_choices,default=1) #設置 one to one 1個用戶不能在不一樣設備上登陸 #設置 Freikey 支持1個用戶 在不一樣設備上同時登陸 class Token(models.Model): user=models.OneToOneField(Userinfo) token=models.CharField(max_length=64)
#權限相關 from rest_framework.permissions import BasePermission,AllowAny class CustomPermission(BasePermission): def has_permission(self, request, view): #view代指那個視圖 #若是用戶爲普通用戶,訪問的視圖是UserGroupView,沒有權限拒絕訪問 if request.user.user_type_id==1 and isinstance(view,UserGroupView) : return False #返回False 表明沒有權限訪問 return True # return True #返回True 表明都有權限訪問
class UserGroupView(APIView): permission_classes=[CustomPermission] def get(self, request, *args, **kwargs): # print(request.user) print(self.permission_classes) print(request.user.user) return Response('經過認證!')
全局使用:
REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#設置使用的類 "VERSION_PARAM":"version", #設置默認參數名稱 "DEFAULT_VERSION":'v1', #設置默認版本 "ALLOWED_VERSIONS":['v1','v2'] , #設置容許的版本 # 設置爲 None,沒有經過認證的用戶,request.user匿名用戶 =None "UNAUTHENTICATED_USER":None, # 設置爲UNAUTHENTICATED_TOKEN tocken =None 'UNAUTHENTICATED_TOKEN':None, "DEFAULT_AUTHENTICATION_CLASSES":( "utils.auth.auth1.CustomAuthentication", ), 'DEFAULT_PERMISSION_CLASSES':[ "utils.permission.per1.CustomPermission",], }
#權限相關 from rest_framework.permissions import BasePermission,AllowAny from app01 import views class CustomPermission(BasePermission): message='無權限訪問' #定製錯誤信息 def has_permission(self, request, view): #view代指那個視圖 #若是用戶爲普通用戶,訪問的視圖是UserGroupView,沒有權限拒絕訪問 if request.user.user_type_id==1 and isinstance(view,views.UserGroupView) : return False #返回False 表明沒有權限訪問 return True # return True #返回True 表明都有權限訪問
class UserGroupView(APIView): def get(self, request, *args, **kwargs): # print(request.user) print(self.permission_classes) print(request.user.user) return Response('經過認證!')
RestApi訪問權限使用小結:
建立權限的類,類中has_permission(self, request, view) return flase表明沒有權限訪問,return True表明有權限訪問,request代指請求信息、view參數代指視圖
RestApi的訪問權限控制工做在視圖層 dispatch方法,RBAC工做在中間件;
Django Rest Framework 對用戶的訪問頻率是經過在Django緩存中記錄每位用戶訪問時間,進行訪問頻率限制的;
用戶訪問時間記錄:以用戶惟一標識(IP/token)作鍵,訪問時間戳列表作值,列表的長度爲用戶訪問次數;
{
用戶A:[ 訪問時間戳1,訪問時間戳2],
用戶B:[ 訪問時間戳1,訪問時間戳2]
}
當用戶A請求到來:
根據用戶的惟一標識(IP/Token/用戶名)獲取用戶A的全部訪問記錄時間戳列表;
獲取當前時間,根據當前時間戳和訪問記錄列表中的時間戳、列表中時間戳個數,作對比來決定用戶A是否能夠繼續訪問;
#根據用戶的惟一標識 獲取當我用戶的全部訪問記錄 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()#控制用戶訪問記錄列表的中的訪問記錄,最後1個在配置時間範圍內 #超出合理長度 返回False,不能繼續訪問 if len(self.history) >= self.num_requests: return self.throttle_failure() #訪問記錄列表在合理長度,return Ture可繼續訪問 return self.throttle_success()
4.一、局部應用(基於用戶對單個視圖進行訪問頻率限制self.key = request.user.user+view.__class__.__name__)
{
用戶A視圖1:[ 訪問時間戳1,訪問時間戳2],
用戶A視圖2:[ 訪問時間戳1,訪問時間戳2]
}
# 限制訪問次數相關 from rest_framework.throttling import BaseThrottle,AnonRateThrottle,SimpleRateThrottle #根據IP class CustomAnonThrottle(SimpleRateThrottle): # 代指配置文件中定義的訪問頻率的類 {'anon':'5/m'} scope = 'anon' def allow_request(self, request, view): # 已經登陸管不着 if request.user: return True # 獲取當前訪問用戶的惟一標識 get_cache_key是抽象方法 ip+字符串格式化 self.key = self.get_cache_key(request, view) # 根據用戶的惟一標識 獲取當我用戶的全部訪問記錄 self.history = self.cache.get(self.key, []) # 獲取當前時間 [最近訪問,最先訪問 ] self.now = self.timer() # 作限制請求頻率操做 while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() # 根據配置文件 次數/時間,中的時間控制用戶訪問記錄列表在合理的時間區間內 # 根據根據配置文件次數/時間中的次數,和列表長度決定用戶是否可繼續訪問 # 超出合理長度 返回False,不能繼續訪問 if len(self.history) >= self.num_requests: return self.throttle_failure() # 訪問記錄列表在合理長度,return Ture可繼續訪問 return self.throttle_success() def get_cache_key(self, request, view): return self.cache_format % { 'scope': self.scope, 'ident': self.get_ident(request) } #根據用戶名 class CustomUserThrottle(SimpleRateThrottle): #代指配置文件中定義的訪問頻率的類 {'anon':'5/m'} scope = 'user' def allow_request(self, request, view): #已經登陸管不着 if not request.user: return True #獲取當前訪問用戶的惟一標識 用戶名 #self.key = request.user.user#用戶對所頁面進行訪問頻率限制 #用戶對單個視圖 進行訪問頻率限制 self.key = request.user.user+view.__class__.__name__ #根據用戶的惟一標識 獲取當我用戶的全部訪問記錄 self.history = self.cache.get(self.key, []) #獲取當前時間 [最近訪問,最先訪問 ] self.now = self.timer() #作限制請求頻率操做 while self.history and self.history[-1] <= self.now - self.duration: self.history.pop()#根據配置文件 次數/時間,中的時間控制用戶訪問記錄列表在合理的時間區間內 #根據根據配置文件次數/時間中的次數,和列表長度決定用戶是否可繼續訪問 # 超出合理長度 返回False,不能繼續訪問 if len(self.history) >= self.num_requests: return self.throttle_failure() #訪問記錄列表在合理長度,return Ture可繼續訪問 return self.throttle_success()
4.一、全局應用(基於用戶對每一個視圖進行訪問頻率限制self.key = request.user.user)
{
用戶A:[ 訪問時間戳1,訪問時間戳2],
}
url(r'^(?P<version>\w+)/usergroup/$', views.UserGroupView.as_view(), name='xxxxx'), #登陸用戶 4/m url(r'^(?P<version>\w+)/test/$', views.TestView.as_view(), name='xxxxx'), #匿名用戶5/m
# 源碼中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK 裏的東西 REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#設置使用的類 "VERSION_PARAM":"version", #設置默認參數名稱 "DEFAULT_VERSION":'v1', #設置默認版本 "ALLOWED_VERSIONS":['v1','v2'] , #設置容許的版本 # 設置爲 None,沒有經過認證的用戶,request.user匿名用戶 =None "UNAUTHENTICATED_USER":None, # 設置爲UNAUTHENTICATED_TOKEN tocken =None 'UNAUTHENTICATED_TOKEN':None, #認證相關配置 "DEFAULT_AUTHENTICATION_CLASSES":( "utils.auth.auth1.CustomAuthentication", ), #權限相關配置 'DEFAULT_PERMISSION_CLASSES':[ "utils.permission.per1.CustomPermission"], #限制訪問次數 'DEFAULT_THROTTLE_RATES':{ 'anon':'5/m', #匿名用戶5次 'user':'4/m', #登陸用戶6次 } }
# 限制訪問次數相關 from rest_framework.throttling import BaseThrottle,AnonRateThrottle,SimpleRateThrottle #根據IP class CustomAnonThrottle(SimpleRateThrottle): # 代指配置文件中定義的訪問頻率的類 {'anon':'5/m'} scope = 'anon' def allow_request(self, request, view): # 已經登陸管不着 if request.user: return True # 獲取當前訪問用戶的惟一標識 get_cache_key是抽象方法 ip+字符串格式化 self.key = self.get_cache_key(request, view) # 根據用戶的惟一標識 獲取當我用戶的全部訪問記錄 self.history = self.cache.get(self.key, []) # 獲取當前時間 [最近訪問,最先訪問 ] self.now = self.timer() # 作限制請求頻率操做 while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() # 根據配置文件 次數/時間,中的時間控制用戶訪問記錄列表在合理的時間區間內 # 根據根據配置文件次數/時間中的次數,和列表長度決定用戶是否可繼續訪問 # 超出合理長度 返回False,不能繼續訪問 if len(self.history) >= self.num_requests: return self.throttle_failure() # 訪問記錄列表在合理長度,return Ture可繼續訪問 return self.throttle_success() def get_cache_key(self, request, view): return self.cache_format % { 'scope': self.scope, 'ident': self.get_ident(request) } #根據用戶名 class CustomUserThrottle(SimpleRateThrottle): #代指配置文件中定義的訪問頻率的類 {'anon':'5/m'} scope = 'user' def allow_request(self, request, view): #已經登陸管不着 if not request.user: return True #獲取當前訪問用戶的惟一標識 用戶名 self.key = request.user.user #根據用戶的惟一標識 獲取當我用戶的全部訪問記錄 self.history = self.cache.get(self.key, []) #獲取當前時間 [最近訪問,最先訪問 ] self.now = self.timer() #作限制請求頻率操做 while self.history and self.history[-1] <= self.now - self.duration: self.history.pop()#根據配置文件 次數/時間,中的時間控制用戶訪問記錄列表在合理的時間區間內 #根據根據配置文件次數/時間中的次數,和列表長度決定用戶是否可繼續訪問 # 超出合理長度 返回False,不能繼續訪問 if len(self.history) >= self.num_requests: return self.throttle_failure() #訪問記錄列表在合理長度,return Ture可繼續訪問 return self.throttle_success()
class UserGroupView(APIView): # permission_classes = [] throttle_classes=[CustomAnonThrottle,CustomUserThrottle] def get(self, request, *args, **kwargs): # print(self.dispatch()) print(request.user) # self.dispatch() # print(self.throttle_classes) # print(self.permission_classes) # print(request.user.user) # print(request.META.get('REMOTE_ADDR')) # print(request.META.get('HTTP_X_FORWARDED_FOR')) return Response('經過認證!') class TestView(APIView): permission_classes = [] authentication_classes = [] throttle_classes = [CustomAnonThrottle, CustomUserThrottle] def get(self, request, *args, **kwargs): return Response('test經過認證!')
匿名用戶使用IP做爲用戶惟一標識
class CustomThrottle(SimpleRateThrottle): scope = 'anon' def get_cache_key(self, request, view): # if request.user.is_authenticated: # return None # Only throttle unauthenticated requests. return self.cache_format % { 'scope': self.scope, 'ident': self.get_ident(request) }
#限制訪問次數 'DEFAULT_THROTTLE_RATES':{'anon':'5/m'}
class UserGroupView(APIView): # permission_classes = [] throttle_classes=[CustomThrottle] def get(self, request, *args, **kwargs): # print(request.user) # self.dispatch() # print(self.throttle_classes) # print(self.permission_classes) # print(request.user.user) return Response('經過認證!')
匿名用戶5/m
url(r'^(?P<version>\w+)/test/$', views.TestView.as_view(), name='xxxxx'), #匿名用戶5/m
#限制訪問次數 'DEFAULT_THROTTLE_RATES':{ 'anon':'5/m', #匿名用戶5次 'user':'4/m', #登陸用戶6次 }
#根據IP class CustomAnonThrottle(SimpleRateThrottle): # 代指配置文件中定義的訪問頻率的類 {'anon':'5/m'} scope = 'anon' def allow_request(self, request, view): # 已經登陸管不着 if request.user: return True # 獲取當前訪問用戶的惟一標識 get_cache_key是抽象方法 ip+字符串格式化 self.key = self.get_cache_key(request, view) # 根據用戶的惟一標識 獲取當我用戶的全部訪問記錄 self.history = self.cache.get(self.key, []) # 獲取當前時間 [最近訪問,最先訪問 ] self.now = self.timer() # 作限制請求頻率操做 while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() # 根據配置文件 次數/時間,中的時間控制用戶訪問記錄列表在合理的時間區間內 # 根據根據配置文件次數/時間中的次數,和列表長度決定用戶是否可繼續訪問 # 超出合理長度 返回False,不能繼續訪問 if len(self.history) >= self.num_requests: return self.throttle_failure() # 訪問記錄列表在合理長度,return Ture可繼續訪問 return self.throttle_success() def get_cache_key(self, request, view): return self.cache_format % { 'scope': self.scope, 'ident': self.get_ident(request) }
class TestView(APIView): permission_classes = [] authentication_classes = [] throttle_classes = [CustomAnonThrottle, CustomUserThrottle] def get(self, request, *args, **kwargs): return Response('test經過認證!')
登陸用戶4/m
url(r'^(?P<version>\w+)/usergroup/$', views.UserGroupView.as_view(), name='xxxxx'), #登陸用戶 4/m
#限制訪問次數 'DEFAULT_THROTTLE_RATES':{ 'anon':'5/m', #匿名用戶5次 'user':'4/m', #登陸用戶6次 }
#根據用戶名 class CustomUserThrottle(SimpleRateThrottle): #代指配置文件中定義的訪問頻率的類 {'anon':'5/m'} scope = 'user' def allow_request(self, request, view): #已經登陸管不着 if not request.user: return True #獲取當前訪問用戶的惟一標識 用戶名 self.key = request.user.user #根據用戶的惟一標識 獲取當我用戶的全部訪問記錄 self.history = self.cache.get(self.key, []) #獲取當前時間 [最近訪問,最先訪問 ] self.now = self.timer() #作限制請求頻率操做 while self.history and self.history[-1] <= self.now - self.duration: self.history.pop()#根據配置文件 次數/時間,中的時間控制用戶訪問記錄列表在合理的時間區間內 #根據根據配置文件次數/時間中的次數,和列表長度決定用戶是否可繼續訪問 # 超出合理長度 返回False,不能繼續訪問 if len(self.history) >= self.num_requests: return self.throttle_failure() #訪問記錄列表在合理長度,return Ture可繼續訪問 return self.throttle_success()
class UserGroupView(APIView): # permission_classes = [] throttle_classes=[CustomAnonThrottle,CustomUserThrottle] def get(self, request, *args, **kwargs): # print(self.dispatch()) print(request.user) # self.dispatch() # print(self.throttle_classes) # print(self.permission_classes) # print(request.user.user) # print(request.META.get('REMOTE_ADDR')) # print(request.META.get('HTTP_X_FORWARDED_FOR')) return Response('經過認證!')
4.三、練習題
要求:
登陸URL: 無權限限制,要求用戶登陸並返回token;
首頁URL:無權限限制,匿名用戶限制 2/m, 登陸用戶 4/m ;
訂單URL: 匿名用戶沒法查看(權限) ,登陸用戶 4/m;
from django.db import models from django.db import models class Userinfo(models.Model): user=models.CharField(max_length=32) pwd=models.CharField(max_length=64) email=models.CharField(max_length=64) user_type_choices=( (1,'普通用戶'), (2,'文藝用戶'), (3,'其餘用戶'), ) user_type_id=models.IntegerField(choices=user_type_choices,default=1) #設置 one to one 1個用戶不能在不一樣設備上登陸 #設置 Freikey 支持1個用戶 在不一樣設備上同時登陸 class Token(models.Model): user=models.OneToOneField(Userinfo) token=models.CharField(max_length=64)
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^login/',views.LoginView.as_view()), url(r'^index/',views.IndexView.as_view()), url(r'^order/',views.OrderView.as_view()), ]
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 APP ] # 源碼中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK 裏的東西 REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#設置使用的類 "VERSION_PARAM":"version", #設置默認參數名稱 "DEFAULT_VERSION":'v1', #設置默認版本 "ALLOWED_VERSIONS":['v1','v2'] , #設置容許的版本 # 設置爲 None,沒有經過認證的用戶,request.user匿名用戶 =None "UNAUTHENTICATED_USER":None, # 設置爲UNAUTHENTICATED_TOKEN tocken =None 'UNAUTHENTICATED_TOKEN':None, # 權限相關配置 'DEFAULT_PERMISSION_CLASSES': ["utils.permission.permission1.CustomPermission"], #認證相關配置 "DEFAULT_AUTHENTICATION_CLASSES":( "utils.auth.auth1.CustomAuthentication", ), #限制訪問次數 'DEFAULT_THROTTLE_RATES':{ 'anon':'2/m', #匿名用戶5次 'user':'4/m', #登陸用戶6次 } }
from rest_framework import exceptions from rest_framework.authentication import BasicAuthentication from app01 import models class CustomAuthentication(BasicAuthentication): #認證類 def authenticate(self, request): #從 url的參數中獲取客戶端攜帶的tocken tocken=request.query_params.get('tocken') #若是是匿名用戶 url中沒有攜帶tocken返回None不拋出異常,表示運行匿名用戶訪問; if not tocken: return (None,None) #raise exceptions.AuthenticationFailed('認證失敗') 拋出異常表示不容許匿名用戶登陸 #去數據庫中檢查tocken是否存在? tocken_obj=models.Token.objects.filter(token=tocken).first() #若是沒有在數據中查詢到用戶攜帶tocken也容許訪問 if not tocken_obj: return (None,None) #若是存在 return (tocken_obj.user,tocken_obj) #若是自定義認證類的authenticate方法return了(None,None),沒有raise異常表明容許匿名用戶訪問
#權限相關 from rest_framework.permissions import BasePermission,AllowAny from app01 import views class CustomPermission(BasePermission): message='匿名用戶無權限訪問,訂單頁面。' #定製錯誤信息 def has_permission(self, request, view): #view代指那個視圖 #若是用戶爲普通用戶,訪問的視圖是UserGroupView,沒有權限拒絕訪問 if not request.user and isinstance(view,views.OrderView) : return False #返回False 表明沒有權限訪問 return True # return True #返回True 表明都有權限訪問
from django.shortcuts import render from utils.throttle.throttle1 import CustomAnonThrottle,CustomUserThrottle from rest_framework.views import APIView #導入 APIView from rest_framework.authentication import BasicAuthentication from rest_framework.authentication import SessionAuthentication from app01 import models from rest_framework.request import Request from rest_framework.response import Response from django.http import JsonResponse #生成動態Tocken def gen_tcoken(username): import random,hashlib,time ctime=str(time.time()) hash=hashlib.md5(username.encode('utf-8')) hash.update(ctime.encode('utf-8')) return hash.hexdigest() ##認證相關 class LoginView(APIView): authentication_classes = [] def get(self,request,*args,**kwargs): ret={'code':666,'token':None,'msg':None} username=request.GET.get('username') # 因爲API對request進行二次封裝GET請獲取參數可使用request.query_params.get('pwd') #因爲API對request進行二次封裝 POST獲取參數可使用request.data.get('pwd') pwd=request.query_params.get('pwd') user_obj=models.Userinfo.objects.filter(user=username,pwd=pwd).first() if user_obj: tk=gen_tcoken(username) models.Token.objects.update_or_create(user=user_obj, defaults={'token': tk}) ret['token']=tk else: ret['code']=444 ret['msg']='用戶名或者密碼錯誤' return JsonResponse(ret) class IndexView(APIView): # 若是在全局配置了權限認證,在單個authentication_classes = []代指本視圖不作認證 throttle_classes = [CustomAnonThrottle, CustomUserThrottle] def get(self,request,*args,**kwargs): return Response('經過主頁認證') class OrderView(APIView): ''' 訂單頁面:用戶登陸以後才能訪問,每分鐘訪問4次 認證環節:容許匿名用戶 權限:不容許匿名用戶對 訪問 OrderView視圖 節流:4/m ''' throttle_classes = [CustomUserThrottle] def get(self,request): return Response('經過訂單頁面認證')
REST framework的解析器:根據客戶端發送的content-type請求頭, 選擇對應的解析器, 對請求體內容進行處理。
5.1源碼邏輯
a、首先把self.get_parsers(姑娘)和get_content_negotiator(勞保)都封裝到request對象中;
return Request( request, parsers=self.get_parsers(), negotiator=self.get_content_negotiator(), )
b、執行request.data調用解析功能negotiator根據 content_type挑選對應的parsers作解析工做
5.2:限制API接口僅處理請求頭中設置content-type/json 和content-type/form的請求體
from rest_framework.parsers import JSONParser from rest_framework.parsers import FormParser from rest_framework.parsers import MultiPartParser from rest_framework.negotiation import DefaultContentNegotiation class ParserView(APIView): parser_classes=[JSONParser,FormParser] #只處理請求頭中包含:Content - Type:application/jison和Content - Type:application/form def get(self,request,*args,**kwargs): return Response ('Parsing success') def post(self,request,*args,**kwargs): print(request.data) # self.dispatch() return Response('Parsing success')
接口測試:PostmanAPI接口測試工具
後臺經過Django ORM查詢到數據都是QuerySet數據類型,這種數據類型不能夠直接json.dumps()序列化響應給客戶端,REST framework自帶序列化功能,能夠將數據庫查詢到的Foreign Key、Many to Many、Choice字段序列化爲字典響應給客戶端,還能夠對用戶提交的數據進行的驗證(功能相似Form驗證);
序列化
6.1:單表查詢結果序列化
#自定義序列化類 from rest_framework import serializers class UserSerializers(serializers.Serializer): user=serializers.CharField() pwd=serializers.CharField() email=serializers.CharField() user_type_id=serializers.IntegerField() #應用 class SerializeView(APIView): def get(self,request,*args,**kwargs): user_list=models.Userinfo.objects.all() #同時想返回序列化數據 many=True,若是是models.Userinfo.objects.all().first()使用many=Flase ser=UserSerializers(instance=user_list,many=True) return Response(ser.data)
6.2:source連表查詢結果序列化(外鍵 1對多關係)
#自定義序列化類 from rest_framework import serializers class UserSerializers(serializers.Serializer): user=serializers.CharField() pwd=serializers.CharField() email=serializers.CharField() user_type_id=serializers.IntegerField() #source代指連表操做獲取數據 ug=serializers.CharField(source='ug.title') #應用 class SerializeView(APIView): def get(self,request,*args,**kwargs): user_list=models.Userinfo.objects.all() #同時想返回序列化數據 many=True,若是是models.Userinfo.objects.all().first()使用many=Flase ser=UserSerializers(instance=user_list,many=True) return Response(ser.data)
正向連表 外鍵字段.xx
class TestSerializer(serializers.ModelSerializer): #1對多字段 house=serializers.CharField(source='House.addr') #1對多字段 car=serializers.CharField(source='car.titile') #多對多字段 madam=serializers.CharField(source='madam.all') car_level=serializers.CharField(source='car.get_car_level_display') class Meta: model=models.Person fields=['name','house','car','madam','car_level']
source還支持反向連表查詢
person=serializers.CharField(source='person_set.all')
depth 連表深度:自動連表,連表深度最大爲10
class UserSerializers(serializers.ModelSerializer): pwd=serializers.HyperlinkedIdentityField(view_name='xxxx') class Meta: model=models.Userinfo fields='__all__' depth=2 #自動聯表
若是是數據庫中普通字段在自定製serializers類中定義fileds=[‘字段名稱’ ]屬性便可,可是遇到choices(中文)、Many to Many字段怎麼處理呢?
獲取choice字段中文: get_choice字段_display
獲取多對多字段數據
方法1:
CharField()中的source參數之因此支持 外鍵跨表、choice字段獲取中文名、多對多字段獲取,原理是由於CharField類中的2個方法:
一、get_attribute 去數據庫中獲取值
二、to_representation 在頁面上顯示值
若是本身實現這2種方法,也能夠自定製獲取、顯示外鍵、多對多、choice字段數據;
class MyFielf(serializers.CharField): def get_attribute(self,instance): #instance 表明每一行 madam_list=instance.madam.all() return madam_list def to_representation(self,value): #value 就是get_attribute return的結果 ret=[] for row in value: ret.append({'name':row.name}) return ret class TestSerializer(serializers.ModelSerializer): madams=MyFielf() class Meta: model=models.Person fields=['name','madams']
方法2:
SerializerMethodField字段結合method_name=' 自定製方法'參數
class ModelCourseDetaiSerializer(serializers.ModelSerializer): course_name=serializers.CharField(source='course.name') #外鍵 price_policy=serializers.SerializerMethodField(method_name='get_price_policyss')# 多對多 recommend_courses=serializers.SerializerMethodField(method_name='get_recommend_courses_list') # ContentType teacher_list = serializers.SerializerMethodField(method_name='get_teacher_lists') class Meta: model=models.CourseDetail fields=['id','course_name','price_policy','recommend_courses','teacher_list'] #推薦課程 def get_recommend_courses_list(self,obj): ret=[] courses_list=obj.recommend_courses.all() for i in courses_list: ret.append({'id':i.id,'name':i.name}) return ret #價格策略 def get_price_policyss(self,obj): ret = [] price=obj.course.price_policy.all() for i in price: ret.append({'id':i.id,'name':i.valid_period,'price':i.price}) return ret #課程講師 def get_teacher_lists(self,obj): ret = [] teachers=obj.teachers.all() for t in teachers: ret.append({'name':t.name,'brief ':t.brief,'role':t.get_role_display()}) return ret class Course_DetailView(APIView): def get(self,*args,**kwargs): pk=kwargs.get('pk',None) detail=models.CourseDetail.objects.get(course_id=pk) ser = ModelCourseDetaiSerializer(instance=detail,many=False) return Response(ser.data)
對提交到API接口的數據作校驗
serializers不只能夠把後臺ORM獲取到的數據序列化後響應客戶端,還能夠對客戶端提交的數據進行驗證(相似Form驗證功能);
#自定義序列化類 from rest_framework import serializers #複雜驗證規則 class PasswordValidator(object): def __init__(self, base): self.base = base def __call__(self, value): if value != self.base: message = '密碼必須是 %s.' % self.base raise serializers.ValidationError(message) class UserSerializers(serializers.Serializer): user=serializers.CharField(error_messages={"required":"用戶名不能爲空"}) #PasswordValidator驗證類 pwd=serializers.CharField(validators=[PasswordValidator(base='666')]) email=serializers.CharField() user_type_id=serializers.IntegerField() #source代指連表操做獲取數據 ug=serializers.CharField(source='ug.title') #應用 class SerializeView(APIView): def get(self,request,*args,**kwargs): user_list=models.Userinfo.objects.all() #同時想返回序列化數據 many=True,若是是models.Userinfo.objects.all().first()使用many=Flase ser=UserSerializers(instance=user_list,many=True) return Response(ser.data) def post(self, request, *args, **kwargs): #對用戶提交數據進行驗證 data=request.dat ser = UserSerializers(data=request.data) #驗證成功返回 正確數據 if ser.is_valid(): return Response(ser.validated_data) else: #驗證失敗放回錯誤信息 print(ser.errors) return Response(ser.errors)
serializers的數據驗證功能相似Form驗證,Form驗證能夠結合Models作ModelForm驗證,serializers也是能夠的;
class UserSerializers(serializers.ModelSerializer): class Meta: model=models.Userinfo fields="__all__" #應用 class SerializeView(APIView): def get(self,request,*args,**kwargs): user_list=models.Userinfo.objects.all() #同時想返回序列化數據 many=True,若是是models.Userinfo.objects.all().first()使用many=Flase ser=UserSerializers(instance=user_list,many=True) return Response(ser.data)
HyperlinkedIdentityField字段,(根據PK反向生成URL)
""" Django settings for RestFrameWor練習 project. Generated by 'django-admin startproject' using Django 1.11.4. For more information on this file, see https://docs.djangoproject.com/en/1.11/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/1.11/ref/settings/ """ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '#zh*tb-b3=2w^lka#47nztp=-shjy0a1j^8&pqypcaug3d%6oq' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition 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 APP ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'RestFrameWor練習.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates'),], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'RestFrameWor練習.wsgi.application' # Database # https://docs.djangoproject.com/en/1.11/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # Password validation # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/1.11/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.11/howto/static-files/ STATIC_URL = '/static/' # 源碼中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK 裏的東西 REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#設置使用的類 "VERSION_PARAM":"version", #設置默認參數名稱 "DEFAULT_VERSION":'v1', #設置默認版本 "ALLOWED_VERSIONS":['v1','v2'] , #設置容許的版本 # 設置爲 None,沒有經過認證的用戶,request.user匿名用戶 =None "UNAUTHENTICATED_USER":None, # 設置爲UNAUTHENTICATED_TOKEN tocken =None 'UNAUTHENTICATED_TOKEN':None, # 權限相關配置 'DEFAULT_PERMISSION_CLASSES': ["utils.permission.permission1.CustomPermission"], #認證相關配置 "DEFAULT_AUTHENTICATION_CLASSES":( "utils.auth.auth1.CustomAuthentication", ), #限制訪問次數 'DEFAULT_THROTTLE_RATES':{ 'anon':'2/m', #匿名用戶2次 'user':'4/m', #登陸用戶4次 } }
注意若是配置文件中配置了版本控制功能,在反向生成URL的時候也要體現;
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^login/',views.LoginView.as_view()), url(r'^index/',views.IndexView.as_view()), url(r'^order/',views.OrderView.as_view()), url(r'^parser/', views.ParserView.as_view()), url(r'^serialize/',views.SerializeView.as_view()), url(r'^serialize\.(?P<format>\w+)',views.SerializeView.as_view()), url(r'test/(?P<pk>\d+)/(?P<version>[v1|v2]+)', views.SerializeView.as_view(),name='xxxx'), ]
class UserSerializers(serializers.ModelSerializer): pwd=serializers.HyperlinkedIdentityField(view_name='xxxx') class Meta: model=models.Userinfo fields='__all__' #應用 class SerializeView(APIView): def get(self,request,*args,**kwargs): user_list=models.Userinfo.objects.all() #同時想返回序列化數據 many=True,若是是models.Userinfo.objects.all().first()使用many=Flase ser=UserSerializers(instance=user_list,many=True,context={'request':request}) return Response(ser.data)
自動生成url字段
class UserSerializers(serializers.HyperlinkedModelSerializer): class Meta: model=models.Userinfo fields=['id','user','pwd','email','url'] #應用 class SerializeView(APIView): def get(self,request,*args,**kwargs): user_list=models.Userinfo.objects.all() #同時想返回序列化數據 many=True,若是是models.Userinfo.objects.all().first()使用many=Flase ser=UserSerializers(instance=user_list,many=True,context={'request':request}) return Response(ser.data) def post(self, request, *args, **kwargs): #對用戶提交數據進行驗證 data=request.dat ser = UserSerializers(data=request.data) #驗證成功返回 正確數據 if ser.is_valid(): return Response(ser.validated_data) else: #驗證失敗放回錯誤信息 print(ser.errors) return Response(ser.errors)
Serializer小結:
Serializer雖然叫序列化,卻有2大功能 (序列化、Form驗證)3大父類(Serializer,ModelSerializer,HyperlinkedModelSerializer);
自定義的serializers類,有3中可繼承的父類;
一、serializers.Serializer:手動指定須要序列化和驗證的字段
二、serializers.ModelSerializer:自動獲取須要序列化和驗證的字段(相似ModelFrom功能)
三、serializers.HyperlinkedModelSerializer 自動生成超連接
當用戶向咱們的REST framework提交查詢請求時候,怎麼能把數據庫裏的所有數據list出來響應給用戶呢?這就涉及到數據分頁了,不只要分頁還要考慮分頁性能;
a.根據 url參數攜帶的頁碼,進行分頁;
url(r'^pager/$', views.PagerView.as_view()),
#分頁相關配置 from rest_framework.pagination import PageNumberPagination class ZhanggenPagination(PageNumberPagination): #默認一頁顯示多少條數據 page_size=1 #http://127.0.0.1:8008/pager/?page=1 url參數名稱 page_query_param='page' #http://127.0.0.1:8008/pager/?page=1&page_size=2,經過url傳參定製一頁顯示多少條數據; page_size_query_param = 'page_size' #序列化相關配置 class PagerSerializers(serializers.ModelSerializer): #新增一個字段 zhanggen=serializers.CharField(source='ug.title') class Meta: model=models.Userinfo fields='__all__' #CBV應用分頁和序列化功能 class PagerView(APIView): def get(self,request,*args,**kwargs): user_list=models.Userinfo.objects.all() #實例化 自定製分頁類 obj=ZhanggenPagination() #調用分頁類的paginate_queryset方法,根據url的參數 獲取分頁數據; page_user_list=obj.paginate_queryset(user_list,request,self ) #序列化 ser=PagerSerializers(instance=page_user_list,many=True) #返回結果包頁碼"next": "http://127.0.0.1:8008/pager/?page=2",點擊跳轉到下一頁; # respose=obj.get_paginated_response(ser.data) # return respose #返回結果不包含頁碼 return Response(ser.data)
b. 基於某位置偏移進行分頁 offset limit
urlpatterns = [url(r'^pager/$', views.PagerView.as_view()), ]
#分頁相關配置 from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import LimitOffsetPagination from rest_framework.pagination import CursorPagination #http://127.0.0.1:8008/pager/?offset=2&limit=5 獲取3-8條(移動到2,基於2偏移獲取5條數據) class Zhanggen1Pagination(LimitOffsetPagination): # 默認每頁顯示的數據條數 default_limit = 1 # URL中傳入的數據位置的參數(基於這個位置偏移) offset_query_param = 'offset' # 偏移量參數 limit_query_param = 'limit' # 最大每頁顯得條數 max_limit = None #序列化相關配置 class PagerSerializers(serializers.ModelSerializer): #新增一個字段 zhanggen=serializers.CharField(source='ug.title') class Meta: model=models.Userinfo fields='__all__' #CBV應用分頁和序列化功能 class PagerView(APIView): def get(self,request,*args,**kwargs): user_list=models.Userinfo.objects.all() #實例化 自定製分頁類 obj=Zhanggen1Pagination() #調用分頁類的paginate_queryset方法,根據url的參數 獲取分頁數據; page_user_list=obj.paginate_queryset(user_list,request,view=self ) #序列化 ser=PagerSerializers(instance=page_user_list,many=True) #返回結果包頁碼"next": "http://127.0.0.1:8008/pager/?page=2",點擊跳轉到下一頁; respose=obj.get_paginated_response(ser.data) return respose #返回結果不包含頁碼 # return Response(ser.data)
c. 遊標分頁
urlpatterns = [ url(r'^pager/$', views.PagerView.as_view()), ]
#分頁相關配置 from rest_framework.pagination import CursorPagination #http://127.0.0.1:8008/pager/?cursor=cD0x class Zhanggen2Pagination(CursorPagination): # URL傳入的遊標參數 cursor_query_param = 'cursor' # 默認每頁顯示的數據條數 page_size = 1 # URL傳入的每頁顯示條數的參數 page_size_query_param = 'page_size' # 每頁顯示數據最大條數 max_page_size = 1000 # 根據ID從大到小排列 ordering = "id" #序列化相關配置 class PagerSerializers(serializers.ModelSerializer): #新增一個字段 zhanggen=serializers.CharField(source='ug.title') class Meta: model=models.Userinfo fields='__all__' #CBV應用分頁和序列化功能 class PagerView(APIView): def get(self,request,*args,**kwargs): user_list=models.Userinfo.objects.all() #實例化 自定製分頁類 obj=Zhanggen2Pagination() #調用分頁類的paginate_queryset方法,根據url的參數 獲取分頁數據; page_user_list=obj.paginate_queryset(user_list,request,view=self ) #序列化 ser=PagerSerializers(instance=page_user_list,many=True) #返回結果包頁碼"next": "http://127.0.0.1:8008/pager/?page=2",點擊跳轉到下一頁; respose=obj.get_paginated_response(ser.data) return respose #返回結果不包含頁碼 # return Response(ser.data)
分頁功能小結:
a. REST framework一共有3中分頁方式
一、根據頁碼和每次能查看的條目數:http://127.0.0.1:8008/pager/?page=1&page_size=2 獲取1-2條
二、基於offset偏移位置作limit獲取:http://127.0.0.1:8008/pager/?offset=2&limit=5 獲取3-8條(移動到2,基於2偏移獲取5條數據)
三、遊標分頁(加密頁面,1頁1頁得翻頁)
b. 淺談分頁性能
分頁方式1和方式2的缺陷:
以上分頁方式最終轉化成SQL語句本質是 offset和 limit
第1頁:select * from 表 offset 0 limit 5
第2頁:select * from 表 offset 5 limit 5
第3頁:select * from 表 offset 10 limit 5
.................................
第N頁:select * from 表 offset X limit 5
越日後翻 offset的值就會越大,已經翻閱過的數據量越多,須要掃描的數據就會越多,每次翻頁數據庫查詢會把已經翻閱的數據的從頭開始再掃描一遍,速度就會越慢;
解決方案:
遊標分頁之因此會把頁碼加密,只讓用戶點擊上一頁和下一頁,進行1頁1頁的翻頁。。。
緣由:普通翻頁和直接跳轉到某頁會涉及全表掃描,除非 查詢數據庫SQL語句中包含 id < n , id >n
翻頁時記住當前頁最大ID和最小ID,想要到上一頁 id>min, 想要到下一頁 id>max select * from 表 where id >n offset 0 limit 5
缺點:沒法直接跳轉到某一頁
REST framework根據配置自動生成 增、刪、改、list的url而且自動生成視圖中的list、add、delete、put方法;解放你的雙手!
自建路由
urlpatterns = [ #路由相關 #http://127.0.0.1:8008/router/ # 支持:GET 查詢顯示list頁面 、 POST:增長 url(r'^router/$', views.RouterView.as_view()), #http://127.0.0.1:8008/router.json #支持:攜帶.json後綴 url(r'^router\.(?P<format>\w+)$', views.RouterView.as_view()), #http://127.0.0.1:8008/router/1/ # 支持:put修改、delete 刪除 url(r'^router/(?P<pk>\d+)/$', views.RouterView.as_view()), #http://127.0.0.1:8008/router/1.json #支持 get 獲取單條數據 url(r'^router/(?P<pk>\d+)\.(?P<format>\w+)$', views.RouterView.as_view()), ]
#路由相關 #路由序列化相關 class RouteSerializers(serializers.ModelSerializer): class Meta: model=models.Userinfo fields='__all__' #應用 class RouterView(APIView): def get(self,*args,**kwargs): pk=kwargs.get('pk') if pk: user_obj= models.Userinfo.objects.get(pk=pk) ser=RouteSerializers(instance=user_obj,many=False) else: user_list=models.Userinfo.objects.all() ser = RouteSerializers(instance=user_list, many=True) return Response(ser.data)
GenericAPIView內置了一些方法 完成 分頁、序列化功能,無需本身實例化對象了,路由沒做任何改變;
from app01 import views urlpatterns = [ # 半自動建立路由 #http://127.0.0.1:8008/router/ # 支持:GET 查詢顯示list頁面 、 POST:增長 url(r'^router/$', views.RouterView.as_view()), #http://127.0.0.1:8008/router.json #支持:攜帶.json後綴 url(r'^router\.(?P<format>\w+)$', views.RouterView.as_view()), #http://127.0.0.1:8008/router/1/ # 支持:put修改、delete 刪除 url(r'^router/(?P<pk>\d+)/$', views.RouterView.as_view()), #http://127.0.0.1:8008/router/1.json #支持 get 獲取單條數據 url(r'^router/(?P<pk>\d+)\.(?P<format>\w+)$', views.RouterView.as_view()), ]
#路由序列化相關 from rest_framework import serializers class RouteSerializers(serializers.ModelSerializer): class Meta: model=models.Userinfo fields='__all__' #路由分頁相關類 from rest_framework.pagination import PageNumberPagination class ZhanggenPagination(PageNumberPagination): #默認一頁顯示多少條數據 page_size=1 #http://127.0.0.1:8008/pager/?page=1 url參數名稱 page_query_param='page' #http://127.0.0.1:8008/pager/?page=1&page_size=2,經過url傳參定製一頁顯示多少條數據; page_size_query_param = 'page_size' #半自動路由 from rest_framework.generics import GenericAPIView class RouterView(GenericAPIView): #數據庫數據 queryset = models.Userinfo.objects.all() #序列化類 serializer_class = RouteSerializers #分頁類 pagination_class = ZhanggenPagination def get(self,request,*args,**kwargs): #獲取數據庫數據 user_list=self.get_queryset() #獲取分頁相關,對數獲取到的數據進行分頁 page_user_list=self.paginate_queryset(user_list) #獲取序列化相關,對已經分頁得數據庫數據進行序列化 ser=self.get_serializer(instance=page_user_list,many=True) respose=self.get_paginated_response(ser.data) return respose
半自動路由: as_view({'get': "retrieve", "put": 'update','delete':'destroy'})) 更加as.view方法中的參數,自動觸發視圖執行
from app01 import views urlpatterns = [ #半自動路由:根據不一樣的請求自動觸發 不一樣的視圖作不一樣的操做 url(r'^router/$',views.RouterView.as_view({'get':"list","post":'create' } )), #http://127.0.0.1:8008/router/1/ #get請求獲取某條數據 put請求某條數據 delete請求刪除單條數據 url(r'^router/(?P<pk>\d+)/$', views.RouterView.as_view({'get': "retrieve", "put": 'update','delete':'destroy'})), ]
#路由序列化相關 from rest_framework import serializers class RouteSerializers(serializers.ModelSerializer): class Meta: model=models.Userinfo fields='__all__' from rest_framework.viewsets import ModelViewSet class RouterView(ModelViewSet): #數據庫數據 queryset = models.Userinfo.objects.all() #序列化類 serializer_class = RouteSerializers
全自動路由
from django.conf.urls import url,include from app01 import views from rest_framework.routers import DefaultRouter router=DefaultRouter() #http://127.0.0.1:8008/zhanggen/2/ 設置url前綴對應的視圖 router.register('zhanggen',views.RouterView) urlpatterns = [ url(r'^',include(router.urls)), ]
#路由序列化相關 class RouteSerializers(serializers.ModelSerializer): class Meta: model=models.Userinfo fields='__all__' from rest_framework.viewsets import ModelViewSet from rest_framework import mixins class RouterView(ModelViewSet): #數據庫數據 queryset = models.Userinfo.objects.all() #序列化類 serializer_class = RouteSerializers
framework視圖類總結
繼承最底層APIView:功能少,可定製性強
繼承ModelViewSet:功能豐富,FBV裏list、add、delet..方法可定製性查;
REST framework根據用戶請求url後綴(http://127.0.0.1:8008/render.json,http://127.0.0.1:8008/render/form/)的不一樣,渲染出不一樣的數據格式響應給客戶端;
注意:後端渲染功能是結合路由系統中設置format傳入的參數來作渲染的,全部不要忘了在路由系統中設置format來接收url傳來的參數;
urlpatterns = [ #渲染相關 url(r'^render/$', views.RenderView.as_view()), #http://127.0.0.1:8008/render.json url(r'^render\.(?P<format>\w+)$', views.RenderView.as_view()), #http://127.0.0.1:8008/render/json/ url(r'^render/(?P<format>\w+)/$', views.RenderView.as_view()), ]
#渲染相關 from rest_framework.renderers import JSONRenderer,AdminRenderer,BrowsableAPIRenderer,HTMLFormRenderer #序列化 class RenderSerializers(serializers.ModelSerializer): class Meta: model=models.Userinfo fields='__all__' # class RenderView(APIView): # #支持響應 json、admin 格式的數據 # renderer_classes =[JSONRenderer,AdminRenderer,BrowsableAPIRenderer] # def get(self,request,*args,**kwargs): # user_list=models.Userinfo.objects.all() # ser=RenderSerializers(instance=user_list,many=True) # return Response(ser.data) class RenderView(APIView): #支持響應form格式的數據 renderer_classes =[HTMLFormRenderer] def get(self,request,*args,**kwargs): #注意返回form格式的數據instance必須爲單個對象 user_list=models.Userinfo.objects.all().first() ser=RenderSerializers(instance=user_list,many=False) return Response(ser.data)
0、RET framework 內置了 版本、用戶認證、訪問權限、限制訪問頻率公共功能;(全局使用在配置文件配置,在全局使用的前提下,局部視圖使用 xxclass=[ ]表明局部不使用)
一、雖然使用全自動url結合ModelViewSet視圖能夠快速搭建一個簡單的API,可是慎用由於後期項目擴展和更新很難修改;
class Zhanggen(APIView): #繼承APIView可擴展性強 def get(self, request, *args, **kwargs): pass def post(self, request, *args, **kwargs): pass def put(self, request, *args, **kwargs): pass def delete(self, request, *args, **kwargs): pass
二、1個視圖對應4個標準url
#http://127.0.0.1:8008/router/ # 支持:GET 查詢顯示list頁面 、 POST:增長 url(r'^router/$',views.RouterView.as_view()), #http://127.0.0.1:8008/router.json #支持:攜帶.json後綴 url(r'^router\.(?P<format>\w+)$', views.RouterView.as_view()), #http://127.0.0.1:8008/router/1/ # 支持:put修改、delete 刪除 url(r'^router/(?P<pk>\d+)/$', views.RouterView.as_view()), #http://127.0.0.1:8008/router/1.json #支持 get 獲取單條數據 url(r'^router/(?P<pk>\d+)\.(?P<format>\w+)$', views.RouterView.as_view()),
三、版本控制
url(r'test/(?P<pk>\d+)/(?P<version>[v1|v2]+)$', views.SerializeView.as_view(),name='xxxx'),