不會DRF?源碼都分析透了確定不來看?
不會DRF?源碼都分析透了確定不來看?

可以更方便的使用django寫出符合restful規範的介面
下載安裝
pip3 install djangorestframework- pycharm下載
注意
rest_framework是一個app需要註冊!
👉官網://www.django-rest-framework.org/

-
drf安裝默認安裝的最新版本,如果django版本過低會自動升級到3.x版本
-
版本支援對應關係

示例
'''views.py'''
from rest_framework.views import APIView
from rest_framework.response import Response
class Test(APIView):
def get(self,request):
data = {'status':200,'msg':'success'}
return Response(data)
def post(self,request):
data = {'status': 200, 'msg': 'success'}
return Response(data)
'''urls.py'''
path('test/', views.Test.as_view()),
'''settings.py'''
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'
]
get請求

post請求

注意:前後端分離csrf已經沒有用了,查看一下源碼
def as_view(cls, **initkwargs)
return csrf_exempt(view)
我們發現返回值是csrf_exempt(view)局部不驗證,在Django總結到了不懂可以看一下:CSRF 跨站請求偽造
快速使用DRF寫出介面
序列化和反序列化
API介面開發,最核心最常見的一個過程就是序列化,所謂序列化就是把數據轉換格式,序列化可以分兩個階段:
- 序列化:把我們語言識別的數據轉換成指定的格式提交給別人(前端)
- 比如python中的字典,列表,對象等轉json,xml,prop····
- 反序列化:把別人提供的數據轉換成我們所需的格式
- 最常見的比如我們使用json模組來對數據進行處理····
在Djangorestframework中的序列化反序列化又是如何?
- 序列化: 在Django中獲取到的數據默認是模型對象(QuerySet對象),但是模型對象數據無法直接提供給前端或別的平台使用,我們需要把數據進行序列化,變成字元串或json數據提供給前端或其他平台;
- 反序列化: 前端或其他平台傳入數據到後台,比如是json格式字元串,後端需要存入資料庫,需要轉換成python中的對象,然後處理存入資料庫;
- 總結:
- python對象 -----》json格式字元串 (序列化)
- json格式字元串 -----》 python 對象 (反序列化)
如何使用DRF快速寫出增刪查改的介面?
drf快速使用
快速寫5個介面
使用Django寫五個介面得配5個路由,5個視圖函數去處理,現在使用drf不需要了,如下:
-查詢所有---》get->//127.0.0.1:8000/books/
-查詢一個---》get->//127.0.0.1:8000/books/1/
-新增一個---》post->//127.0.0.1:8000/books/ #在body中帶數據
-修改-----》put,patch--->實際編碼中,基本都用put
//127.0.0.1:8000/books/1/ body體中傳入修改的數據
-刪除一個---》delete-->//127.0.0.1:8000/books/1/
'''介面都是這五個的變形'''
登錄就是查詢一個,註冊就是新增一個
'''
在我們使用postman的時候,地址嚴格寫,不能缺少/
'''
views.py
from rest_framework.viewsets import ModelViewSet
from app import models
from app.serializer import BookSerializer
class BookView(ModelViewSet):
serializer_class =BookSerializer
queryset = models.Book.objects.all()
serializer.py
from rest_framework.serializers import ModelSerializer
from app import models
class BookSerializer(ModelSerializer):
class Meta:
model = models.Book
fields = '__all__'
urls.py
from rest_framework.routers import SimpleRouter
from app import views
router = SimpleRouter()
router.register('books', views.BookView)
'''
register(prefix, viewset, base_name)
prefix 該視圖集的路由前綴
viewset 視圖集
base_name 路由名稱的前綴
'''
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
# 千萬注意別把注釋寫到urlpatterns列表中,那樣就不是注釋了,成字元串了!!!
在settings的app中註冊
INSTALLED_APPS = [
'rest_framework'
]
models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5,decimal_places=2)
authors = models.CharField(max_length=32)
postman測試
獲取所有

獲取一條

新增一條數據

全部修改:修改id為1的數據,這裡盡量在body體里寫全參數

局部修改:修改id為1的數據,修改啥寫啥

刪除一條數據:刪除id為2的數據


CBV源碼流程分析
因為DRF框架里大部分都是基於CBV(視圖類)寫,所以流程是什麼,如何執行需要了解,同時也方便理解APIView,順便提一嘴APIView也是繼承了View ---->
class APIView(View)這裡需要強調一下,CBV路由歸根結底還是FBV都是函數的記憶體地址,比如
views.類.as_view()底層仍然是函數的記憶體地址
CBV源碼執行流程
'''views.py'''
from django.views import View
from django.http import HttpResponse
class TestView(View):
def get(self,request):
return HttpResponse('get請求')
# post csrf認證注釋掉就好了
def post(self,request):
return HttpResponse('post請求')
'''urls.py'''
path('test/',views.TestView.as_view())
寫一個視圖類,寫了get方法和post方法,來了get請求就走get方法,來了post請求就走post方法,過程如何?且看分析源碼執行過程~
'''請求來了在不考慮中間件的情況下,從路由的匹配關係和視圖函數來看'''
1、cbv路由寫法:path('test/', views.TestView.as_view())
# 第二個參數是函數記憶體地址,CBV的底層也是FBV,as_view是類的綁定方法,自己的類中沒有去父類(View)找,as_view()執行完,也是一個記憶體地址,記憶體地址是誰的?是閉包函數view的如下源碼👇
@classonlymethod
def as_view(cls, **initkwargs):
···
def view(request, *args, **kwargs):
···
return self.dispatch(request, *args, **kwargs)
···
return view
# @classonlymethod通過描述符自定義裝飾器
2、請求來了,路由也匹配成功,執行上邊返回的view(requets),加括弧調用,並且傳入了當次請求的request對象
3、然後又返回了當前對象的dispatch方法,自己的名稱空間內沒有,那麼去父類中找,然後發現父類(View)的dispatch方法返回了return handler(request, *args, **kwargs)
4、dispatch方法處理請求,什麼請求對應什麼方法
# 父類dispatch方法:
def dispatch(self, request, *args, **kwargs):
# 請求方法小寫如果在當前對象的http_method_names中(八個請求方法)
'''
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
'''
if request.method.lower() in self.http_method_names:
# 如果成立,那麼執行下面的反射,從當前對象(視圖類的對象)拿到請求方法,如果是get請求就拿到get方法,post請求就拿到post方法,然後賦給handler,handler就是相應請求對應的方法,最後返回handler(request),本質其實是get(request)
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
# 不成立,也就是沒有該請求對應的方法,執行http_method_not_allowed方法,彈出警告
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
# http_method_not_allowed源碼
def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
return HttpResponseNotAllowed(self._allowed_methods())
getattr(obj,pro,None):按pro判斷是否有無obj.pro屬性,沒有返回None,None可以自訂製- 反射忘記的可以看俺的這篇部落格:python 多態、組合、反射
Django View和DRF APIView的小插曲
-
DRF中最「牛逼」的視圖類是
APIView,不過也繼承了View -
導入:
from rest_framework.views import APIView -
Django中最「牛逼」的視圖類是
View -
導入:
from django.views import View # 最簡 from django.views.generic import View from django.views.generic.base import View # 最完整 '''這三個導入的結果都是一個View,想知道緣由還得看源碼和目錄結構,如下:''' # 目錄結構 -django -··· # 此處省略n個包 -views -- __init__.py -- generic ---- __init__.py ---- base.py # 源碼分析 '''問題:為什麼上述不同的py文件都可以指向同一個類?''' 我的思路是羊毛出在羊身上,既然都指向類,那麼從結果出發是不是更好 1、View類在base.py中,那麼顯而易見最完整的導入是沒問題的,重點是為什麼簡寫也可以 2、base.py所在的包generic,如果從包中導入py文件我們知道可以通過「白名單」__all__來指定誰可以被其他文件導入,我們發現generic包中指定了: from django.views.generic.base import RedirectView, TemplateView, View和__all__ = [ 'View',·····],到此第二個導入解決 # 那麼第一個這麼短這麼簡單又是為啥? 3、同樣在views包的__init__.py文件中發現: from django.views.generic.base import View __all__ = ['View'] View類被__all__變數包裹了
ps:不管是DRF中的APIView還是亂七八糟的xxView,最後只要繼承了Django中的View就是視圖類
DRF之APIView和Request對象分析
APIView的執行流程
# 同樣和Django中一樣寫一個視圖類,只不過DRF中用APIView底層還是View
'''views.py'''
from rest_framework.response import Response
class Test(APIView):
def get(self, request):
data = {'status': 200, 'msg': 'success'}
return Response(data)
def post(self, request):
data = {'status': 200, 'msg': 'success'}
return Response(data)
'''urls.py'''
path('test/', views.Test.as_view())
APIView流程源碼分析
1、路由:path('test/', views.Test.as_view()),path第二個參數任然返回函數記憶體地址,也還是類綁定方法,Test沒有as_view方法,去繼承的APIView中找,這次不需要和Django一樣去View中找了,慶幸的是APIView中有as_view方法,核心源碼如下:
@classmethod
def as_view(cls, **initkwargs):
# 校驗反射的結果
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '····
)
···
# 核心程式碼
# 這裡調用了父類的as_view,查看源碼發現又回到了Django中的View類,所以本質還是和上面一樣,用到了閉包返回的view
view = super().as_view(**initkwargs)
···
# 局部去掉了csrf校驗和加裝飾器的效果是一樣的
return csrf_exempt(view)
2、view = super().as_view(**initkwargs),這裡跳轉了一下,其實看了父類(View)的源碼是和上面Django中寫視圖類繼承的View是一樣的,這裡的(APIView)的as_view只是進行了簡單處理和去掉了csrf中間件校驗,真實使用的還是View類中的as_view
3、然後還是閉包函數的返回值view加括弧調用,傳入了當前對象的request,繼續執行了self.dicpatch(),當前類(Test)沒有去父類(APIview)找dispatch方法,發現APIView類中有,千萬注意了這裡可不是View中的dispatch方法了
4、APIView類中的dispatch主要源碼:
# APIView的dispatch
def dispatch(self, request, *args, **kwargs):
# request是新的drf提供的request,它是由老的django的request得到的
# 通過老request,生成一個新request,drf的Request的對象
request = self.initialize_request(request, *args, **kwargs)
# 把新的request,放到了視圖類對象中,可以通過self調用新的request和傳入的request是一個,因為放到了self中
self.request = request
try:
# 執行了三大認證(認證,許可權,頻率)
self.initial(request, *args, **kwargs)
# self.http_method_names是個列表
if request.method.lower() in self.http_method_names:
# 原來dispatch的核心程式碼
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 原來dispatch寫的,但是request已經不是老request了,是新的
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
總結:
1、路由:path('test/', views.Test.as_view())
2、APIView類的as_view執行,最終使用View類的as_view
3、執行閉包返回view加括弧調用到此就是as_view加括弧調用
4、調用執行了view()返回dispatch,但是這裡的父類不是View,是APIview所以執行的dispatch是APIView中的dispatch方法
5、dispatch方法中包裝了新的Request對象,以後視圖類中的方法傳入的request都是新的,無論三大認證還是視圖函數的方法,執行過程中出了異常,都會被處理掉
6、dispatch執行完畢返回reponse對象,跳轉回進入視圖函數繼續執行as_view去掉了csrf校驗
如何包裝了新的request?
# APIView中的dispatch方法處理老的request
request = self.initialize_request(request, *args, **kwargs)
# initialize_request方法,當前類(自己寫的)沒有去父類找,還是APIView
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request, # 老的request,傳入的非處理的,返回後就是新的了
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
注意:上面返回的Request對象是rest_framework導入的,然後實例化後返回的,實例化就少不了__init__構造函數
from rest_framework.request import Request
'''Request類'''
class Request:
# 這裡即將要初始化的request是傳入的老的request
def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
···
# 初始化,將傳入的老的request放入新的_request中,所以request._request是老的,self.request._request等價於request._request
self._request = request
···
# 驗證一下新老request
class Test(APIView):
def get(self, request):
data = {'status': 200, 'msg': 'success'}
# 新的request
print(type(request)) # <class 'rest_framework.request.Request'>
print(type(self.request))# <class 'rest_framework.request.Request'>
# 老的request
print(type(self.request._request)) # <class 'django.core.handlers.wsgi.WSGIRequest'>
print(type(request._request)) # <class 'django.core.handlers.wsgi.WSGIRequest'>
return Response(data)
新的:<class 'rest_framework.request.Request'>
老的:<class 'django.core.handlers.wsgi.WSGIRequest'>
三大認證如何執行?
# APIView類處理三大認證
self.initial(request, *args, **kwargs)
# 核心源碼
def initial(self, request, *args, **kwargs):
····
self.perform_authentication(request) # 認證
self.check_permissions(request) # 許可權
self.check_throttles(request) # 頻率
···
# 經過了三大認證才進入了視圖函數
中間件---路由---···---三大認證---視圖函數····
# 類似二次校驗
Request對象分析
這裡是經過包裝後的request
rest_framework.request.Request常用屬性和方法
這裡的request和原來的Django使用request一樣,只是多了一個request.data
-
request.data:前端POST提交的數據,可以處理多種格式的數據,無論前端傳什麼編碼post提交的數據都在data中
ps:原來提交的數據在request.POST里,有局限性只能處理urlencoded和formdata編碼格式,json格式不能處理,是沒有
request.data的,request其餘使用方法的都一樣
# 如果用過新包裝過的request來調用原來的方法呢?這樣給使用者的感覺確實沒什麼變化源碼如下:
'''重寫了getattr'''
def __getattr__(self, attr):
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
重寫getattr的結果是,新的request.method執行的時候本質是request._request.method執行了原來老的request的方法,通過這句getattr(self._request, attr)反射,所以才一樣
總結:新的request當老的用即可,只是多了個data前端post請求傳入的數據,三種編碼格式都可以獲取
驗證處理三種編碼格式
- json格式,只有request.data可以獲取,結果是字典
- form-data格式和urlencode格式都可以獲取並且是QueryDict對象
from rest_framework.response import Response
class Test(APIView):
def post(self, request):
data = {'status': 200, 'msg': 'success'}
print(request.POST)
print(request._request.POST)
print(request.data)
return Response(data)
驗證
# form-data格式結果
<QueryDict: {'name': ['hammer']}>
<QueryDict: {'name': ['hammer']}>
<QueryDict: {'name': ['hammer']}>
# urlencode格式結果
<QueryDict: {'name': ['hammer']}>
<QueryDict: {'name': ['hammer']}>
<QueryDict: {'name': ['hammer']}>
# json格式結果
<QueryDict: {}>
<QueryDict: {}>
{'name': 'Hammer'}
官網的一些解釋

原來的django中沒有request.data,造一個!
# 原來的django的request對象中沒有data,使得request.data-->無論什麼編碼格式,post提交數據,data都有值
from django.views import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from functools import wraps
def outter(func):
@wraps(func)
def inner(request,*args,**kwargs):
import json
if request.POST:
request.data = request.POST
else:
# 將request.body從json對象轉換為字典
request.data = json.loads(request.body)
res = func(request,*args,**kwargs)
return res
return inner
@method_decorator(outter,name='post')
class TestView(View):
def get(self, request):
return HttpResponse('get請求')
def post(self, request):
# 只測試了json格式其餘都可以也測了
print(request.data) # {'name': 'Hammer'}
print(request.POST) # <QueryDict: {}>
return HttpResponse('post請求')
靈感來源:傳送門
