django rest-framework處理流程分析

首先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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嗦枢,隨后出現(xiàn)的幾起案子攀芯,更是在濱河造成了極大的恐慌,老刑警劉巖文虏,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侣诺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡氧秘,警方通過查閱死者的電腦和手機(jī)年鸳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丸相,“玉大人搔确,你說我怎么就攤上這事∶鹬遥” “怎么了妥箕?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)更舞。 經(jīng)常有香客問我,道長(zhǎng)坎吻,這世上最難降的妖魔是什么缆蝉? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮瘦真,結(jié)果婚禮上刊头,老公的妹妹穿的比我還像新娘。我一直安慰自己诸尽,他們只是感情好原杂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著您机,像睡著了一般穿肄。 火紅的嫁衣襯著肌膚如雪年局。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天咸产,我揣著相機(jī)與錄音矢否,去河邊找鬼。 笑死脑溢,一個(gè)胖子當(dāng)著我的面吹牛僵朗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播屑彻,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼验庙,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了社牲?” 一聲冷哼從身側(cè)響起粪薛,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎膳沽,沒想到半個(gè)月后汗菜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挑社,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年陨界,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痛阻。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡菌瘪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出阱当,到底是詐尸還是另有隱情俏扩,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布弊添,位于F島的核電站录淡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏油坝。R本人自食惡果不足惜嫉戚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望澈圈。 院中可真熱鬧彬檀,春花似錦、人聲如沸瞬女。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诽偷。三九已至坤学,卻和暖如春疯坤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拥峦。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工贴膘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人略号。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓刑峡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親玄柠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子突梦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容