首先django要明白django是一個(gè)MVC結(jié)構(gòu)的web框架婿滓,MVC框架中老速,邏輯的處理流程集中在view層,這也就是我們?cè)诙xurl的時(shí)候凸主,都是指定url對(duì)應(yīng)某個(gè)view橘券,而對(duì)于view,通常又有view對(duì)象和view函數(shù),初期很多時(shí)候约郁,我們直接將url對(duì)應(yīng)到某個(gè)函數(shù)缩挑,而rest-framework是采用view對(duì)象,因?yàn)閷?duì)象可以封裝更多的信息和操作鬓梅,更重要的是可以繼承供置,便于統(tǒng)一(聯(lián)想到C和C++的區(qū)別)。
其實(shí)還要明白一點(diǎn)绽快,對(duì)于一個(gè)框架芥丧,或者一種協(xié)議或者規(guī)范,其實(shí)就是指定了數(shù)據(jù)的格式和處理的流程坊罢,要使用這個(gè)框架或者協(xié)議续担,就要遵守這個(gè)框架流程』詈ⅲ或許會(huì)問物遇,對(duì)于規(guī)定好的框架,我們?cè)趺炊ㄖ坪透哪睾度澹拖駌est-framework怎么嵌套到django中的呢询兴?聯(lián)想到python的裝飾器,我們可以在django中的某兩層中間增加一層我們自己的層次起趾,來做我們的特定處理诗舰,比如對(duì)應(yīng)請(qǐng)求方法是否合理的判斷,參數(shù)是否正確训裆,域名ip是否被禁止訪問眶根,只是要注意,我們新增的這層的輸出一定只是原來上一層輸出的一個(gè)過濾或者流程的截?cái)啾吡穑荒芨淖冊(cè)瓉砹鞒痰妮敵鍪舭伲蝗痪蜁?huì)引入問題。下面以rest-framwork和django為例艺骂。
對(duì)于django诸老,我們還要明白一點(diǎn),流程的處理入口是django/core/handlers/wsgi.py中的WSGIHandler類的__call__函數(shù)钳恕,而不是我們定義的urls.py中的urlpatterns别伏,這一點(diǎn)很重要,對(duì)于部分新手來說忧额,很可能把urls.py中的urlpatterns當(dāng)成http請(qǐng)求的入口厘肮。這里再次簡(jiǎn)單的說一下生產(chǎn)環(huán)境中,一個(gè)基于django框架的web服務(wù)的處理請(qǐng)求流程:
nginx作為接入層睦番,通過其反向代理功能类茂,監(jiān)聽80端口獲取請(qǐng)求連接 -> 根據(jù)nginx配置耍属,將請(qǐng)求交給后端服務(wù)器程序wsgi server ->wsgi server調(diào)用執(zhí)行django的wsgi.py處理請(qǐng)求邏輯 -> WSGIHandler的__call__函數(shù)就是整個(gè)邏輯處理流程 -> 首先在WSGIHandler __init__中的加載中間件(中間件是一些列的類,這些類中有兩大類函數(shù)巩检,process_request和process_response厚骗,可以把它想象成一個(gè)裝飾器,就是對(duì)request對(duì)象做處理兢哭,或者對(duì)response對(duì)象做處理) ->中間件處理request -> urlpatterns中指定的邏輯 -> 中間件處理response ->返回response
rest-framwork就是上面流程中的urlpatterns指定的邏輯層领舰,這個(gè)邏輯層對(duì)應(yīng)的操作無非就是http協(xié)議定義的那幾個(gè)方法,get迟螺,put冲秽,post,patch矩父,delete等锉桑,rest-framwork所作的就是將這幾個(gè)方法封裝到特定的類里面,同時(shí)在這些類里面增加了一些其他的函數(shù)窍株,比如請(qǐng)求方法判定民轴,權(quán)限鑒定等操作,用戶使用rest-framework的時(shí)候就可以繼承rest-framework中定義好的某個(gè)或者某些類夹姥,當(dāng)然rest-framework也給用戶留了很多定制的空間杉武,也可以理解,在使用rest-framework時(shí)辙售,我們必須實(shí)現(xiàn)其中的部分功能。下面就具體的部分類作說明飞涂。
假如urlpatterns中有如下的一個(gè)url組:
url(
r"^users/(?P<pk>.*?)/?$",
users.UserRetrieveModelView.as_view(),
name="user_details",
),
url(
r"^users/?$",
users.UserCreateModelView.as_view(),
name="user_create",
),
UserRetrieveModelView在users.py中定義如下
from rest-framework import generics
class UserRetrieveModelView(generics.RetrieveAPIView):
#下面再解釋為什么需要新增加這些屬性
queryset = user.objects.filter()
serializer_class = UserSerializer
permission_classes = (
permissions.IsAdminUser,
)
RetrieveAPIView旦部,顧名思義,就是用來查詢的较店,所以這個(gè)類通常用來處理get請(qǐng)求士八,看看RetrieveAPIView類的源碼
class RetrieveAPIView(mixins.RetrieveModelMixin, GenericAPIView):
"""
Concrete view for retrieving a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
我們通常在寫代碼的時(shí)候,會(huì)將自己的View繼承自RetrieveAPIView等這一系列由rest-framework提供的類梁呈,然后dispatch去根據(jù)請(qǐng)求方法調(diào)用對(duì)應(yīng)的方法婚度,比如上面的get,get就會(huì)調(diào)用retrieve官卡,下面分析retrieve函數(shù)蝗茁。
class RetrieveModelMixin(object):
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
retrieve單詞的意思就是"查詢,檢索"寻咒,所以這個(gè)操作怎么對(duì)應(yīng)于數(shù)據(jù)庫(kù)中的select的呢哮翘?這一步其實(shí)就是對(duì)應(yīng)于get_object函數(shù),這個(gè)函數(shù)來源于哪兒呢毛秘?其實(shí)來自于GenericAPIView類饭寺,具體位置是rest-framwork/generics.py中的GenericAPIView類里面的get_object函數(shù)阻课,當(dāng)然我們也可以在我們的view類中重新定義GenericAPIView中提供的方法,或者自定義類中queryset,serializer_class艰匙,queryable_fields限煞,permission_classes 等屬性去定制我們自己的操作,因?yàn)檫@些屬性都是這個(gè)框架中某些流程中固定會(huì)用到的员凝。對(duì)于GenericAPIView中的get_object函數(shù)
def get_object(self):
"""
Returns the object the view is displaying.
You may want to override this if you need to provide non-standard
queryset lookups. Eg if objects are referenced using multiple
keyword arguments in the url conf.
"""
queryset = self.filter_queryset(self.get_queryset())
# Perform the lookup filtering.
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
assert lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs)
# May raise a permission denied
self.check_object_permissions(self.request, obj)
return obj
這個(gè)get_object函數(shù)就會(huì)調(diào)用self.get_queryset()函數(shù)去獲取我們自己定義的view類的queryset屬性署驻,然后通過制定的queryset查詢數(shù)據(jù)庫(kù)叶圃。這也就是為什么UserRetrieveModelView類定義了queryset屬性的原因澎迎,因?yàn)間et_object函數(shù)會(huì)調(diào)用到這個(gè)屬性。queryset屬性就是對(duì)應(yīng)輸出庫(kù)的一個(gè)查詢操作蛾方,所以也就是我們看到user.objects.filter()骤公。
我們查詢獲得的是一個(gè)model的實(shí)例抚官,要獲取model的數(shù)據(jù),那就得用到序列化類serializer阶捆,也就是retrieve函數(shù)中的第二個(gè)流程凌节,我們可以指定自己的serializer去處理查詢得到的,也就是UserRetrieveModelView中指定的UserSerializer類洒试。UserSerializer類一般直接繼承rest-framework/serializers.py中的ModelSerializer(Serializer)就是了倍奢,serializers使用時(shí)是需要和model綁定的,怎么綁定垒棋,具體就是通過類中class Meta指定model卒煞,具體例子如下:
from rest-framework.serializers import ModelSerializer
from models import users
class UserSerializer(ModelSerializer):
class Meta:
model = users
關(guān)于ModelSerializer的具體用法參見:http://www.django-rest-framework.org/tutorial/1-serialization/
這就是對(duì)generics.RetrieveAPIView的簡(jiǎn)單使用。
下面接著對(duì)generics.CreateAPIView進(jìn)行分析叼架,就如上面url的例子UserCreateModelView
from rest-framework import generics
class UserCreateModelView(generics.CreateAPIView):
serializer_class = UserSerializer
創(chuàng)建用戶的時(shí)候我們一般使用post畔裕,這時(shí)候dispatch方法就會(huì)調(diào)用post方法,而post方法在哪兒定義的呢乖订?查看一下CreateAPIView的源碼扮饶,通過繼承關(guān)系我們可以發(fā)現(xiàn),post其實(shí)是調(diào)用create乍构,而create來源于CreateModelMixin類
class CreateAPIView(mixins.CreateModelMixin, GenericAPIView):
"""
Concrete view for creating a model instance.
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class CreateModelMixin(object):
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
當(dāng)然rest-framework中也提供了很多方法的組合類甜无,比如rest-framework/generics.py中RetrieveUpdateDestroyAPIView類,這些內(nèi)容的方法是依賴于rest-framework/generics.py中GenericAPIView中定義的方法的哥遮,我們只要重寫這些方法岂丘,就可以將rest-framework按照?qǐng)鼍靶枨髴?yīng)用到實(shí)際生產(chǎn)中。
另外昔善,個(gè)人覺得元潘,明白一點(diǎn)很重要,那就是軟件領(lǐng)域的問題君仆,都可以通過增加一個(gè)中間層去處理翩概,其實(shí)就像python裝飾器的原理牲距。所以如果要修改某個(gè)類xxx方法,如果這個(gè)類被其他類繼承了钥庇,那么直接去修改這個(gè)類的方法會(huì)導(dǎo)致其他類也跟著改變牍鞠,所以我們可以直接繼承這個(gè)類,然后重構(gòu)類的這個(gè)xxx方法评姨,如果我們不想完全重構(gòu)难述,只是想新增部分操作,那么可以在新增操作之后吐句,調(diào)用super(Fclass, cls).xxx(*args, *kwargs)胁后,這就是面向?qū)ο蟮暮锰帯?br> 比如
class OrgCls(object):
def xxx(*args, *kwargs):
"""
do some thing
"""
class MyCls(OrgCls):
def xxx(*args, *kwargs):
"""
do some thing special
"""
super(MyCls, self).xxx(*args, *kwargs)