django rest framemark

一 內容回顧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"'
View Code

 使用:

  建立類:繼承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('用戶信息')
View Code

  全局使用:就是在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
                    }
View Code

 源碼流程:

- 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)    
View Code

 源碼流程:

 另外一種使用:類,必須繼承: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']
        }
                
View Code

 源碼流程:

七 節流(控制訪問頻率)

 控制訪問頻率:

  建立訪問次數: 類, 繼承: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 Code

  局部使用:在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)
View Code

 源碼流程:

 內置控制頻率類:

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
        
View Code

 全局使用:在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',
                }
            }
View Code

 八 版本

 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('用戶列表')    
View Code

 在URL中傳參(推薦使用):在urls文件中

urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view()),
]
View Code

 在settings文件中:

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

 在views文件中:

class UsersView(APIView):

    def get(self,request,*args,**kwargs):
        print(request.version)
        return HttpResponse('用戶列表')
View Code

 總結:

                        使用:
                配置文件:
                    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('用戶列表')
                                
View Code

九 解析器

 前戲: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 解析器,對請求體數據進行解析
            
View Code

 總結:

                          使用:    
                配置:
                    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 
                
View Code

十 序列化

 部分總結:

  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',]
View Code

  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. 自定義類
                        
View Code

  3 自動序列化連表

class UserInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        # fields = "__all__"
        fields = ['id','username','password','group','roles']
        depth = 1 # 0 ~ 10
View Code

  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)
                
View Code

 源碼:

對象, Serializer類處理;
QuerySet,ListSerializer類處理;
# ser.data
View Code

十一 數據請求校驗

                                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('提交數據')
                
View Code

 注意問題: 自定義驗證規則時,須要鉤子函數?請問鉤子函數如何寫?

 十二 分頁

 分頁在這裏分爲了三部分:普通分頁,基於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)
View Code

 基於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)
View Code

 基於加密的分頁:記住每一頁的最大值和最小值的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)
View Code

 Pagination下面PageNumberpagination:分頁器的一個模塊,在settings文件中配置一下PAGE_SIZE:每頁數據的條數。

REST_FRAMEWORK = {
   
    "PAGE_SIZE":2,

}
View Code

十二 視圖

 過去:

class Pager1View(View):
    pass
View Code

  如今:

class Pager1View(APIView): # View
        pass
View Code

 無用:

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)
    
View Code

 GenericViewSet(ViewSetMixin, generics.GenericAPIView):

  路由:

url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list'})),
View Code

  視圖:

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)
                        
View Code

 增刪改查:

  路由: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'})),
View Code

  視圖:

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
                    
View Code

 總結:

a. 增刪改查 ModelViewSet
    b. 增刪     CreateModelMixin,DestroyModelMixin  GenericViewSet
    c. 複雜邏輯  GenericViewSet 或 APIView 
            
    PS: 還債: 
        GenericAPIView.get_object
            check_object_permissions
                has_object_permission
View Code

十三 路由

 在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)),
]
View Code

十四 渲染器

 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)
View Code

  在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)
                
View Code

   settings文件中

REST_FRAMEWORK = {
        
            
    "DEFAULT_RENDERER_CLASSES":[
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ]
}
                
View Code

 找模板的順序:先去最外層查找,若是沒有再去某個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方法來處理響應的內容
View Code

 什麼請況下request.POST沒有值:

只有請求header中的'Content-Type'是'application/x-www-form-urlencoded'request.POST'的狀況下POST裏面纔會有值,,其它狀況下就會沒有值

  序列化檢驗分爲哪些步驟:

本身寫的類繼承:serializer

字段繼承:SerializerMethodField。

生成url
View Code
相關文章
相關標籤/搜索