一 內容回顧html
1 開發者模式前端
普通開發方式:先後端放在一塊兒開發python
先後端分離:先後端只經過 JSON 來交流,組件化、工程化不須要依賴後端去實現web
2 後端開發:爲前端提供url接口,也就是API或者接口的開發。永遠返回HttpResponse。
面試
3 django的FBC和CBV
ajax
FBV也就是:function base view的縮寫,也就是視圖函數。方法以下:
數據庫
# def users(request): # user_list=['alex','oldboy','egon'] # return HttpResponse(json.dumps(user_list))
CBV也就是:class base view的縮寫,也就是類的視圖。方法以下:django
注:寫的類須要繼承View內置的類的方法。在url中使用:類名.as_view()json
在views.py文件中:後端
View的導入:from django.views impost View
# class StudentView(View): # # def get(self,request,*args,**kwargs): # return HttpResponse('GET') # # def post(self,request,*args,**kwargs): # return HttpResponse('POST') # # def delete(self,request,*args,**kwargs): # return HttpResponse('DELETE') # # def put(self,request,*args,**kwargs): # return HttpResponse('PUT')
在url.py文件中:
url(r'^student/', views.StudentView.as_view()),
CBV補充:CBV是基於反射實現根據請求方式不一樣,執行不一樣的方法。
原理:也就是執行的順序是url -> view方法 -> dispatch方法(反射執行其餘:GET/POST/DELETE/PUT)
流程以下:
class StudentView(View): def dispatch(self, request, *args, **kwargs): ret=super(StudentView,self).dispatch(request, *args, **kwargs) return ret def get(self,request,*args,**kwargs): return HttpResponse('GET') def post(self,request,*args,**kwargs): return HttpResponse("POST") def put(self,request,*args,**kwargs): return HttpResponse("PUST") def delete(self,request,*args,**kwargs): return HttpResponse('DELETE')
繼承:多個類的共同功能,爲了不重複寫代碼,以下:
class MyBaseView: def dispatch(self,request,*args,**kwargs): print('before') ret=super(MyBaseView,self).dispatch(request,*args,**kwargs) print('after') return ret class StudentView(MyBaseView,View): def get(self,request,*args,**kwargs): print('get方法') return HttpResponse('GET') def put(self, request, *args, **kwargs): print('put方法') return HttpResponse('PUT') def post(self, request, *args, **kwargs): print('post方法') return HttpResponse('POST') def delete(self, request, *args, **kwargs): print('delete方法') return HttpResponse('DELETE')
4 列表生成式:
class Foo: def __init__(self): pass def work(self): print('this is working') class Bar: def __init__(self): pass def ect(self): print('this is ecting') objs=[item() for item in [Foo,Bar]] # 就至關於以下 # objs=[] # for i in [Foo,Bar]: # objs.append(i()) objs[0].work() objs[1].ect()
5 面向對象的封裝:
1將共同的方法和屬性封裝到同一個類下面。
class File: 文件增刪改查方法 Class DB: 數據庫的方法
2 將數據封裝到數據中。實例以下:
class File: def __init__(self,a1,a2): self.a1 = a1 self.xxx = a2 def get:... def delete:... def update:... def add:... obj1 = File(123,666) obj2 = File(456,999)
6 ps擴展:
class Request: def __init__(self,obj): self.obj=obj @property def user(self): return self.obj.authticate() class Auth: def __init__(self,name,age): self.name=name self.age=age def authticate(self): return self.name,self.age class APIView: def dispatch(self): return self.f2() def f2(self): a=Auth('alex',33) ret=Request(a) return ret.user obj=APIView() print(obj.dispatch())
二 中間件和csrf_token
1 django中csrf_token是怎麼實現的:csrf_token是在process_view裏面驗證的。
2 解決csrf_token的方法:
2.1:直接在settings文件中將下面這行代碼給註釋掉
'django.middleware.csrf.CsrfViewMiddleware',
2.2:經過csrf_exempt裝飾器,將不須要csrf_token的試圖函數上加上這個裝飾器。process_view方法就會檢查試圖的cerf_token是否免除。
from django.views.decorators.csrf import csrf_exempt @csrf_exempt # def users(request): user_list=['alex','oldboy','egon'] return HttpResponse(json.dumps(user_list))
2.3:類的視圖若是須要免除csrf_token的驗證,就須要在class類使用裝飾器method_decorator。不過必需要加上dispatch這個方法。加在單獨的方法上面無效。
使用方法一:直接加在類裏面的dispatch方法上面
from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator # method_decorator 裝飾器使用方法一: class StudentView(View): @method_decorator(csrf_exempt) def dispatch(self,request,*args,**kwargs): ret=super(StudentView,self).dispatch(request,*args,**kwargs) return ret def get(self,request,*args,**kwargs): return HttpResponse('GET') def post(self,request,*args,**kwargs): return HttpResponse('POST') def put(self,request,*args,**kwargs): return HttpResponse('PUT') def delete(self,request,*args,**kwargs): return HttpResponse('DELETE')
使用方法二:在class類上面加上這個裝飾器,不過須要多創一個屬性值,@method_decorator(csrf_token,name='dispatch')
# method_decorator 裝飾器使用方法二: from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator @method_decorator(csrf_exempt,name='dispatch') class StudentView(View): def get(self, request, *args, **kwargs): return HttpResponse('GET') def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE')
2.4 :csrf_protect裝飾器,若是在settings文件中將'django.middleware.csrf.CsrfViewMiddleware',這段代碼註釋掉了的話須要某個試圖函數經過csrf_token的驗證,就在這個視圖函數上面加上這個裝飾器。
from django.views.decorators.csrf import csrf_exempt @csrf_protect # 該函數需認證 def users(request): user_list = ['alex','oldboy'] return HttpResponse(json.dumps((user_list)))
3 django的中間件:以下
- process_request - process_view - process_response - process_exception - process_render_template
django的中間件作過什麼:權限;用戶認證。
process_view作過什麼:檢查視圖是否被免除csrf_token的驗證;去請求體的cookie中獲取csrf_token
三 restful規範
什麼是restful:
REST與技術無關,表明的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯爲「表徵狀態轉移」 REST從資源的角度類審視整個網絡,它將分佈在網絡中某個節點的資源經過URL進行標識,客戶端應用經過URL來獲取資源的表徵,得到這些表徵導致這些應用轉變狀態 REST與技術無關,表明的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯爲「表徵狀態轉移」 全部的數據,不過是經過網絡獲取的仍是操做(增刪改查)的數據,都是資源,將一切數據視爲資源是REST區別與其餘架構風格的最本質屬性 對於REST這種面向資源的架構風格,有人提出一種全新的結構理念,即:面向資源架構(ROA:Resource Oriented Architecture)
restful是爲了根據method的不一樣作這不一樣的操做。restful就是一個規範,只爲了讓咱們更好的記住url。
restful的API的設計有哪些。以下:
1 API與用戶的通訊協議,老是使用HTTPs協議。
2 域名 :
https://api.example.com 儘可能將API部署在專用域名(會存在跨域問題) https://example.org/api/ API很簡單
3 版本:
URL,如:https://api.example.com/v1/ 請求頭 跨域時,引起發送屢次請求
4 路徑,視網絡上任何東西都是資源,均使用名詞表示(可複數)
https://api.example.com/v1/zoos https://api.example.com/v1/animals https://api.example.com/v1/employees
5 method
GET :從服務器取出資源(一項或多項) POST :在服務器新建一個資源 PUT :在服務器更新資源(客戶端提供改變後的完整資源) PATCH :在服務器更新資源(客戶端提供改變的屬性) DELETE :從服務器刪除資源
6 過濾,經過在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:指定篩選條件
7 狀態碼
200 OK - [GET]:服務器成功返回用戶請求的數據,該操做是冪等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。 202 Accepted - [*]:表示一個請求已經進入後臺排隊(異步任務) 204 NO CONTENT - [DELETE]:用戶刪除數據成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操做,該操做是冪等的。 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。 403 Forbidden - [*] 表示用戶獲得受權(與401錯誤相對),可是訪問是被禁止的。 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操做,該操做是冪等的。 406 Not Acceptable - [GET]:用戶請求的格式不可得(好比用戶請求JSON格式,可是隻有XML格式)。 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再獲得的。 422 Unprocesable entity - [POST/PUT/PATCH] 當建立一個對象時,發生一個驗證錯誤。 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將沒法判斷髮出的請求是否成功。
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
8 錯誤處理,狀態碼是4xx時,應返回錯誤信息,error當作key。
{ error: "Invalid API key" }
9 返回結果,針對不一樣操做,服務器向用戶返回的結果應該符合如下規範。
GET /collection:返回資源對象的列表(數組) GET /collection/resource:返回單個資源對象 POST /collection:返回新生成的資源對象 PUT /collection/resource:返回完整的資源對象 PATCH /collection/resource:返回完整的資源對象 DELETE /collection/resource:返回一個空文檔
10 Hypermedia API,RESTful API最好作到Hypermedia,即返回結果中提供連接,連向其餘API方法,使得用戶不查文檔,也知道下一步應該作什麼。
{"link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" }}
借鑑:http://www.ruanyifeng.com/blog/2014/05/restful_api.html
四 restful的API面試解答
問題:請說一下你對restful的認識。
解答:
restful的API設計模式有API與用戶的通訊協議是基於HTTPs協議實現的,經過url傳參的形式傳遞搜索條件的,還有錯誤處理等等。 在我沒有遇到restful API以前須要記住各類各樣的不少的url路徑,並且寫的代碼全是靠着試圖函數給實現的,代碼看起來不是很高端,而且等等 RESTful架構,是比較流行的一種互聯網軟件架構。REST,即Representational State Transfer的縮寫。 說白點就是網站即軟件,再白點就是一個服務軟件支持http的四種方法: GET用來獲取資源,POST用來新建資源、更新資源,PUT用來更新資源,DELETE用來刪除資源。 在我遇到restful以後,不須要記住那麼多的url,能夠將共同的方法封裝到一個類下面,經過調用在這個類的方 法來訪問url,能夠經過這個類調用多個方法。而且API很好的解決了跨域的問題。版本有更新的時候,還能夠 將版本號寫在url中來提示使用的版本。還能夠本身寫訪問錯誤時返回的錯誤信息。傳入參數時還能夠經過url來傳遞參數等等,
借鑑 :
RESTful架構,目前是比較流行的一種互聯網軟件架構。REST,即Representational State Transfer的縮寫。 說白點就是網站即軟件,再白點就是一個服務軟件支持http的四種方法: GET用來獲取資源,POST用來新建資源、更新資源,PUT用來更新資源,DELETE用來刪除資源。 並對外提供一個或多個URI,每一個URI對應一個資源;客戶端經過URI配合上面的方法就能夠和服務 段的軟件交互。客戶端主要是瀏覽器,使用restful框架的軟件對http的支持也爲了web應用帶來方便。 REST這個詞,是Roy Thomas Fielding在他2000年的博士論文中提出的。他的貢獻不少, 能夠了解一下。本人工做的方向是SDN,也算是比較潮的東東,其中floodlight就用到了restful框架。 開發者爲軟件開發出一些功能,並提供URI api,用戶就能夠利用瀏覽器、curl等工具經過提供的URI 從軟件中得到想要的信息或者設置軟件的功能。 對於發開者來講,就是提供URI和URI對應的資源,並將他們對應上,相似dicts={'/path?':resource}。 好比重寫http GET方法:首先得到客戶端請求的url,解析url而後判斷其對應的URI,因爲URI與應一個資源, 那麼url就能夠訪問這個資源了。具體實現上資源也就是方法或者一個類,要看具體實現了。
五 認證
1:有些API須要用戶登錄事後,才能訪問,有些不須要登錄就可以訪問
2:基本使用認證組件:解決,建立兩張表,用戶登錄後,獲取token,存入到數據庫。
3:認證流程原理:
4:局部視圖使用和全局使用,匿名用戶request.user=None
5:內置認證類:
認證類,必須繼承:from rest_framework.authentication import BaseAuthentication
其餘認證類:BasicAuthentication
from rest_framework import exceptions from api import models from rest_framework.authentication import BaseAuthentication class FirstAuthtication(BaseAuthentication): def authenticate(self,request): pass def authenticate_header(self, request): pass class Authtication(BaseAuthentication): def authenticate(self,request): token = request._request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('用戶認證失敗') # 在rest framework內部會將整個兩個字段賦值給request,以供後續操做使用 return (token_obj.user, token_obj) def authenticate_header(self, request): return 'Basic realm="api"'
使用:
建立類:繼承BaseAuthentication; 實現:authenticate方法
返回值:None,我無論了,下一認證來執行;raise exceptions.AuthenticationFailed('用戶認證失敗') # from rest_framework import exceptions ; (元素1,元素2) # 元素1賦值給request.user; 元素2賦值給request.auth
局部使用:在view試圖類中直接調用
from rest_framework.authentication import BaseAuthentication,BasicAuthentication class UserInfoView(APIView): """ 訂單相關業務 """ authentication_classes = [BasicAuthentication,] def get(self,request,*args,**kwargs): print(request.user) return HttpResponse('用戶信息')
全局使用:就是在settings文件中加上如下操做:
REST_FRAMEWORK = { # 全局使用的認證類 "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ], # "UNAUTHENTICATED_USER":lambda :"匿名用戶" "UNAUTHENTICATED_USER":None, # 匿名,request.user = None "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None }
源碼流程:
- dispatch - 封裝request - 獲取定義的認證類(全局/局部),經過列表生成時建立對象。 - initial - perform_authentication request.user(內部循環....)
六 權限
不用視圖,不用權限訪問
基本使用:
class MyPermission(object): def has_permission(self,request,view): if request.user.user_type != 3: return False return True class OrderView(APIView): """ 訂單相關業務(只有SVIP用戶有權限) """ permission_classes = [MyPermission,] def get(self,request,*args,**kwargs): # request.user # request.auth self.dispatch ret = {'code':1000,'msg':None,'data':None} try: ret['data'] = ORDER_DICT except Exception as e: pass return JsonResponse(ret)
源碼流程:
另外一種使用:類,必須繼承:BasePermission,必須實現:has_permission方法
from rest_framework.permissions import BasePermission class SVIPPermission(BasePermission): message = "必須是SVIP才能訪問" def has_permission(self,request,view): if request.user.user_type != 3: return False return True - 返回值: - True, 有權訪問 - False,無權訪問 - 局部 :在view視圖文件 class UserInfoView(APIView): """ 訂單相關業務(普通用戶、VIP) """ permission_classes = [MyPermission1, ] def get(self,request,*args,**kwargs): return HttpResponse('用戶信息') - 全局 :settings文件中 REST_FRAMEWORK = { "DEFAULT_PERMISSION_CLASSES:['api.utils.permission.SVIPPermission'] }
源碼流程:
七 節流(控制訪問頻率)
控制訪問頻率:
建立訪問次數: 類, 繼承:BaseThrottle,實現:allow_request、wait
類, 繼承:SimpleRateThrottle,實現:get_cache_key、scope = "Luffy"(配置文件中的key)
import time VISIT_RECORD = {} class VisitThrottle(object): """60s內只能訪問3次""" def __init__(self): self.history = None def allow_request(self,request,view): # 1. 獲取用戶IP remote_addr = request.META.get('REMOTE_ADDR') ctime = time.time() if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr] = [ctime,] return True history = VISIT_RECORD.get(remote_addr) self.history = history while history and history[-1] < ctime - 60: history.pop() if len(history) < 3: history.insert(0,ctime) return True # return True # 表示能夠繼續訪問 # return False # 表示訪問頻率過高,被限制 def wait(self): """ 還須要等多少秒才能訪問 :return: """ ctime = time.time() return 60 - (ctime - self.history[-1])
局部使用:在view視圖中
class AuthView(APIView): """ 用於用戶登陸認證 """ authentication_classes = [] permission_classes = [] throttle_classes = [VisitThrottle,] def post(self,request,*args,**kwargs): ret = {'code':1000,'msg':None} try: user = request._request.POST.get('username') pwd = request._request.POST.get('password') obj = models.UserInfo.objects.filter(username=user,password=pwd).first() if not obj: ret['code'] = 1001 ret['msg'] = "用戶名或密碼錯誤" # 爲登陸用戶建立token token = md5(user) # 存在就更新,不存在就建立 models.UserToken.objects.update_or_create(user=obj,defaults={'token':token}) ret['token'] = token except Exception as e: ret['code'] = 1002 ret['msg'] = '請求異常' return JsonResponse(ret)
源碼流程:
內置控制頻率類:
from rest_framework.throttling import BaseThrottle,SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope = "Luffy" def get_cache_key(self, request, view): return self.get_ident(request) class UserThrottle(SimpleRateThrottle): scope = "LuffyUser" def get_cache_key(self, request, view): return request.user.username
全局使用:在settings文件中
REST_FRAMEWORK = { # 全局使用的認證類 "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ], # "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication', ], # "UNAUTHENTICATED_USER":lambda :"匿名用戶" "UNAUTHENTICATED_USER":None, # 匿名,request.user = None "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission'], "DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"], "DEFAULT_THROTTLE_RATES":{ "Luffy":'3/m', "LuffyUser":'10/m', } }
八 版本
url經過get傳參:
自定義:
http://127.0.0.1:8000/api/users/?version=v2 class ParamVersion(object): def determine_version(self, request, *args, **kwargs): version = request.query_params.get('version') return version class UsersView(APIView): versioning_class = ParamVersion def get(self,request,*args,**kwargs): #version = request._request.GET.get('version') #print(version) # version = request.query_params.get('version') # print(version) print(request.version) return HttpResponse('用戶列表')
在URL中傳參(推薦使用):在urls文件中
urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view()), ]
在settings文件中:
REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", "DEFAULT_VERSION":'v1', "ALLOWED_VERSIONS":['v1','v2'], "VERSION_PARAM":'version', }
在views文件中:
class UsersView(APIView): def get(self,request,*args,**kwargs): print(request.version) return HttpResponse('用戶列表')
總結:
使用: 配置文件: REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", "DEFAULT_VERSION":'v1', "ALLOWED_VERSIONS":['v1','v2'], "VERSION_PARAM":'version', } 路由系統: urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^api/', include('api.urls')), ] urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view(),name='uuu'), ] 視圖: class UsersView(APIView): def get(self,request,*args,**kwargs): # 1. 獲取版本 print(request.version) # 2. 獲取處理版本的對象 print(request.versioning_scheme) # 3. 反向生成URL(rest framework) u1 = request.versioning_scheme.reverse(viewname='uuu',request=request) print(u1) # 4. 反向生成URL u2 = reverse(viewname='uuu',kwargs={'version':2}) print(u2) return HttpResponse('用戶列表')
九 解析器
前戲:django:request.POST/ request.body
1. 請求頭要求: Content-Type: application/x-www-form-urlencoded PS: 若是請求頭中的 Content-Type: application/x-www-form-urlencoded,request.POST中才有值(去request.body中解析數據)。 2. 數據格式要求: name=alex&age=18&gender=男 如: a. form表單提交 <form method...> input... </form> b. ajax提交 $.ajax({ url:... type:POST, data:{name:alex,age=18} # 內部轉化 name=alex&age=18&gender=男 }) 狀況一: $.ajax({ url:... type:POST, headers:{'Content-Type':"application/json"} data:{name:alex,age=18} # 內部轉化 name=alex&age=18&gender=男 }) # body有值;POST無 狀況二: $.ajax({ url:... type:POST, headers:{'Content-Type':"application/json"} data:JSON.stringfy({name:alex,age=18}) # {name:alex,age:18...} }) # body有值;POST無 # json.loads(request.body) rest_framework 解析器,對請求體數據進行解析
總結:
使用: 配置: REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", "DEFAULT_VERSION":'v1', "ALLOWED_VERSIONS":['v1','v2'], "VERSION_PARAM":'version', "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser'] } 使用: class ParserView(APIView): # parser_classes = [JSONParser,FormParser,] """ JSONParser:表示只能解析content-type:application/json頭 JSONParser:表示只能解析content-type:application/x-www-form-urlencoded頭 """ def post(self,request,*args,**kwargs): """ 容許用戶發送JSON格式數據 a. content-type: application/json b. {'name':'alex',age:18} :param request: :param args: :param kwargs: :return: """ """ 1. 獲取用戶請求 2. 獲取用戶請求體 3. 根據用戶請求頭 和 parser_classes = [JSONParser,FormParser,] 中支持的請求頭進行比較 4. JSONParser對象去請求體 5. request.data """ print(request.data) return HttpResponse('ParserView') 源碼流程 & 本質: a. 本質 請求頭 :... 狀態碼: ... 請求方法:... b. 源碼流程 - dispatch: request封裝 - request.data
十 序列化
部分總結:
1. 寫類
class RolesSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField() class UserInfoSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo # fields = "__all__" fields = ['id','username','password',]
2. 字段
a. title = serializers.CharField(source="xxx.xxx.xx.xx") b. title = serializers.SerializerMethodField() class UserInfoSerializer(serializers.ModelSerializer): rls = serializers.SerializerMethodField() # 自定義顯示 class Meta: model = models.UserInfo fields = ['id','username','password','rls',] # 自定義方法 def get_rls(self, row): role_obj_list = row.roles.all() ret = [] for item in role_obj_list: ret.append({'id':item.id,'title':item.title}) return ret c. 自定義類
3 自動序列化連表
class UserInfoSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo # fields = "__all__" fields = ['id','username','password','group','roles'] depth = 1 # 0 ~ 10
4. 生成連接
class UserInfoSerializer(serializers.ModelSerializer): group = serializers.HyperlinkedIdentityField(view_name='gp',lookup_field='group_id',lookup_url_kwarg='xxx') class Meta: model = models.UserInfo # fields = "__all__" fields = ['id','username','password','group','roles'] depth = 0 # 0 ~ 10 class UserInfoView(APIView): def get(self,request,*args,**kwargs): users = models.UserInfo.objects.all() ser = UserInfoSerializer(instance=users,many=True,context={'request': request}) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret)
源碼:
對象, Serializer類處理; QuerySet,ListSerializer類處理; # ser.data
十一 數據請求校驗
class XXValidator(object): def __init__(self, base): self.base = base def __call__(self, value): if not value.startswith(self.base): message = '標題必須以 %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 UserGroupSerializer(serializers.Serializer): title = serializers.CharField(error_messages={'required':'標題不能爲空'},validators=[XXValidator('老男人'),]) class UserGroupView(APIView): def post(self,request,*args,**kwargs): ser = UserGroupSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data['title']) else: print(ser.errors) return HttpResponse('提交數據')
注意問題: 自定義驗證規則時,須要鉤子函數?請問鉤子函數如何寫?
十二 分頁
分頁在這裏分爲了三部分:普通分頁,基於limit offset分頁和基於加密的分頁
Response:直接就能夠序列化,不須要json。
普通分頁:查看第幾頁,顯示幾條數據,可是若是數據量大的話,分頁約日後面越慢
from rest_framework.pagination import PageNumberPagination class MyPageNumberPagination(PageNumberPagination): page_size = 2 page_size_query_param = 'size' max_page_size = 5 page_query_param = 'page' class Pager1View(APIView): def get(self,request,*args,**kwargs): # 獲取全部數據 roles = models.Role.objects.all() # 建立分頁對象 # pg = MyPageNumberPagination() pg = PageNumberPagination() # 在數據庫中獲取分頁的數據 pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self) # 對數據進行序列化 ser = PagerSerialiser(instance=pager_roles, many=True) return Response(ser.data) # return pg.get_paginated_response(ser.data)
基於limit offset的分頁:在n個位置,向後面查看n條數據。LimitOffsetPagintion:基於limit offset分頁的原生類
from api.utils.serializsers.pager import PagerSerialiser from rest_framework.response import Response from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination class MyLimitOffsetPagination(LimitOffsetPagination): default_limit = 2 limit_query_param = 'limit' offset_query_param = 'offset' max_limit = 5 class Pager1View(APIView): def get(self,request,*args,**kwargs): # 獲取全部數據 roles = models.Role.objects.all() # 建立分頁對象 # pg = MyLimitOffsetPagination() pg = LimitOffsetPagination() # 在數據庫中獲取分頁的數據 pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self) # 對數據進行序列化 ser = PagerSerialiser(instance=pager_roles, many=True) return Response(ser.data) # return pg.get_paginated_response(ser.data)
基於加密的分頁:記住每一頁的最大值和最小值的ID,若是點擊下一頁就從最大值的ID向後數n條數據;若是點擊的是上一頁,就從最小值向前數n條數據。cursorParination:加密分頁,不須要傳入任何的參數。
from api.utils.serializsers.pager import PagerSerialiser from rest_framework.response import Response from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination class MyCursorPagination(CursorPagination): cursor_query_param = 'cursor' page_size = 2 ordering = 'id' page_size_query_param = None max_page_size = None class Pager1View(APIView): def get(self,request,*args,**kwargs): # 獲取全部數據 roles = models.Role.objects.all() # 建立分頁對象 # pg = CursorPagination() pg = MyCursorPagination() # 在數據庫中獲取分頁的數據 pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self) # 對數據進行序列化 ser = PagerSerialiser(instance=pager_roles, many=True) # return Response(ser.data) return pg.get_paginated_response(ser.data)
Pagination下面PageNumberpagination:分頁器的一個模塊,在settings文件中配置一下PAGE_SIZE:每頁數據的條數。
REST_FRAMEWORK = { "PAGE_SIZE":2, }
十二 視圖
過去:
class Pager1View(View): pass
如今:
class Pager1View(APIView): # View pass
無用:
from api.utils.serializsers.pager import PagerSerialiser from rest_framework.generics import GenericAPIView class View1View(GenericAPIView): # APIView queryset = models.Role.objects.all() serializer_class = PagerSerialiser pagination_class = PageNumberPagination def get(self,request,*args,**kwargs): # 獲取數據 roles = self.get_queryset() # models.Role.objects.all() # [1, 1000,] [1,10] pager_roles = self.paginate_queryset(roles) # 序列化 ser = self.get_serializer(instance=pager_roles,many=True) return Response(ser.data)
GenericViewSet(ViewSetMixin, generics.GenericAPIView):
路由:
url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list'})),
視圖:
from api.utils.serializsers.pager import PagerSerialiser from rest_framework.viewsets import GenericViewSet class View1View(GenericViewSet): queryset = models.Role.objects.all() serializer_class = PagerSerialiser pagination_class = PageNumberPagination def list(self, request, *args, **kwargs): # 獲取數據 roles = self.get_queryset() # models.Role.objects.all() # [1, 1000,] [1,10] pager_roles = self.paginate_queryset(roles) # 序列化 ser = self.get_serializer(instance=pager_roles, many=True) return Response(ser.data)
增刪改查:
路由:as_view():括號裏面是能夠傳入參數的。
url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})), url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)/$', views.View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
視圖:
from api.utils.serializsers.pager import PagerSerialiser from rest_framework.viewsets import GenericViewSet,ModelViewSet from rest_framework.mixins import ListModelMixin,CreateModelMixin class View1View(ModelViewSet): queryset = models.Role.objects.all() serializer_class = PagerSerialiser pagination_class = PageNumberPagination
總結:
a. 增刪改查 ModelViewSet
b. 增刪 CreateModelMixin,DestroyModelMixin GenericViewSet
c. 複雜邏輯 GenericViewSet 或 APIView
PS: 還債:
GenericAPIView.get_object
check_object_permissions
has_object_permission
十三 路由
在url後面加上format=json:寫給解析器看的,也等於早url裏面加上一個命名分組,format同樣的
自動生成url:urls文件導入routers模塊;而後再router=routers.DefaultRouter();最後在router.register(r'...',views.類名);最最後url(r'^',incloude(router.urls))。以上四步是是自動生成增刪改查的url的步驟。
a. url(r'^(?P<version>[v1|v2]+)/parser/$', views.ParserView.as_view()), b. url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})), c. # http://127.0.0.1:8000/api/v1/v1/?format=json url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})), # http://127.0.0.1:8000/api/v1/v1.json url(r'^(?P<version>[v1|v2]+)/v1\.(?P<format>\w+)$', views.View1View.as_view({'get': 'list','post':'create'})), url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)/$', views.View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})), url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)\.(?P<format>\w+)$', views.View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})), d. from api import views from rest_framework import routers router = routers.DefaultRouter() router.register(r'xxxxx', views.View1View) router.register(r'rt', views.View1View) urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/', include(router.urls)), ]
十四 渲染器
JSONRenderer:json渲染
在views文件中:局部渲染renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer class TestView(APIView): renderer_classes = [JSONRenderer,BrowsableAPIRenderer] def get(self, request, *args, **kwargs): # 獲取全部數據 roles = models.Role.objects.all() # 建立分頁對象 # pg = CursorPagination() pg = MyCursorPagination() # 在數據庫中獲取分頁的數據 pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self) # 對數據進行序列化 ser = PagerSerialiser(instance=pager_roles, many=True) return Response(ser.data)
在settings文件中全局渲染:
views文件:
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer class TestView(APIView): def get(self, request, *args, **kwargs): # 獲取全部數據 roles = models.Role.objects.all() # 建立分頁對象 # pg = CursorPagination() pg = MyCursorPagination() # 在數據庫中獲取分頁的數據 pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self) # 對數據進行序列化 ser = PagerSerialiser(instance=pager_roles, many=True) return Response(ser.data)
settings文件中
REST_FRAMEWORK = { "DEFAULT_RENDERER_CLASSES":[ 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ] }
找模板的順序:先去最外層查找,若是沒有再去某個app裏面尋找
十五 面試題
個人寫的類的試圖最多繼承了幾個類
6個類中有四個是增刪改查的類,有一個是局部更新的類,還有一個類是專門繼承其餘的類的。
你作過度頁嗎,數據量有多大:django自帶的分頁功能不完善,我本身定義過度頁,數據量差很少2000左右。數據量越大,分頁向後查找的速度就會越慢。在數據量很是大的狀況下,數據沒有必要所有的顯示,顯示必定的數量就能夠,避免數據量多的分頁。
rest_framowork的請求生命週期:
1.請求到達服務端,通過WSGI和中間件到達路由系統 2.路由系統執行配置的CBV或者FBV中的dispatch方法 3.在dispatch方法中,request方法被封裝添加了解析器,認證方法及選擇器等方法 4.而後執行initial方法 5.再獲取版本,進行認證操做,權限操做和節流操做 6.最後執行自定義的get,post,push,delete等自定義方法 7.在執行initial方法以前,經過try來捕獲可能出現的異常 8.若是出現異常,就執行handle_exception方法來處理捕獲到的異常 9.不論是否出現異常,最後的返回值都經過finalize_response方法來處理響應的內容
什麼請況下request.POST沒有值:
只有請求header中的'Content-Type'是'application/x-www-form-urlencoded'request.POST'的狀況下POST裏面纔會有值,,其它狀況下就會沒有值
序列化檢驗分爲哪些步驟:
本身寫的類繼承:serializer
字段繼承:SerializerMethodField。
生成url