Django RESTful 系列教程(一)


這是一個(gè)關(guān)于 Django RESTful 開發(fā)的教程。教程將會(huì)持續(xù)更新,更新進(jìn)度為每個(gè)星期一篇召嘶。我們將會(huì)學(xué)習(xí) Django RESTful 開發(fā)。在你閱讀這個(gè)系列的教程之前哮缺,你需要注意這些:

  • 筆者用的是 python3.5 弄跌,Django 1.11
  • 熟練 python 的使用。當(dāng)文中提到裝飾器或者等概念時(shí)尝苇,請(qǐng)有最基本的映像铛只。了解 JavaScript 的基本使用埠胖。
  • 在跟隨教程的任何過程中,有任何問題淳玩,大家可以評(píng)論留言直撤,或者給我發(fā)郵件 1130195942@qq.com ,或者是在 github 上提 issue。
  • 所有的代碼和教程的 MakrDown 文本都可以在 github上找到蜕着。歡迎大家 clone 或者 star 谋竖。
  • 以前做過 Django 的相關(guān)項(xiàng)目,對(duì) Django 有一定的了解承匣,至少完成過官方的入門教程蓖乘。在講到 模型視圖 等概念時(shí)韧骗,有一定的了解嘉抒。從一定程度上來說,這也是一個(gè)進(jìn)階的教程袍暴。
  • 在本教程的最后些侍,我們將會(huì)使用 DjangoDjango REST framework容诬、Vue娩梨、Vue-Router 來做一個(gè)前后端分離的項(xiàng)目,也就是說览徒,這篇教程會(huì)包含前端的內(nèi)容狈定,如果你對(duì)前端不了解也沒關(guān)系,在擁有最基本的基礎(chǔ)之上习蓬,大膽跟教程走可以了纽什。
  • 轉(zhuǎn)載請(qǐng)聯(lián)系!

本章概要:

很多的 web 框架都以方便使用而著稱躲叼,特別是 flask 芦缰,一個(gè)文件就可以做一個(gè) Hellow world 了,那 django 可以嗎枫慷?答案時(shí)肯定的让蕾。同時(shí),我們將會(huì)簡(jiǎn)單的了解下 REST 的概念 或听。最后探孝,我們將會(huì)利用我們才學(xué)的知識(shí)來編寫我們的第一個(gè) REST 項(xiàng)目

  • 單文件 django
  • REST 是什么
  • 第一個(gè) Django REST 項(xiàng)目。

單文件 Django

發(fā)生了什么誉裆?

相信大家對(duì) Django 有一定的了解顿颅,對(duì)構(gòu)建項(xiàng)目的每個(gè)過程也已經(jīng)非常清楚了∽愣總是重復(fù)的那么幾個(gè)步驟:

  1. 先運(yùn)行 django-admin startproject <your-project> 創(chuàng)建項(xiàng)目
  2. 再切換到項(xiàng)目路徑下運(yùn)行 python manage.py startapp <your-app>粱腻,創(chuàng)建項(xiàng)目的 app 庇配。
  3. 在每個(gè) App 里寫代碼,寫完了最后想要運(yùn)行項(xiàng)目時(shí)運(yùn)行 python manage.py runserver 來啟動(dòng)本地的開發(fā)服務(wù)器绍些。

有的時(shí)候捞慌,我們僅僅是想做個(gè)實(shí)驗(yàn),僅僅時(shí)想看看剛才手動(dòng)寫入的數(shù)據(jù)到底有沒有正確寫入或者是看看我的視圖反響解析出來到底是什么樣子遇革。更重要的是卿闹,我們不想每次需要查看一些相關(guān)數(shù)據(jù)時(shí),都需要從 app 目錄里切出來萝快,然后 runserver ≈牵或許你會(huì)辯駁說揪漩,我們有 django 提供的 shell 可用,這樣也可以很方便的和我們的應(yīng)用交互吏口。那么能不能再簡(jiǎn)單一點(diǎn)奄容?換句話說,我們能不能直接執(zhí)行我們當(dāng)前編寫的腳本呢产徊?

新建一個(gè)文件夾昂勒,叫做 test-project 。并在里面創(chuàng)建一個(gè)新的文件 test.py 舟铜。你的目錄結(jié)構(gòu)大概是這樣的:

test/
    test.py

在開頭引入這些包:

test.py

from django.conf import settings 
from django.http import HttpResponse
from django.conf.urls import url

我們依次來看看他們都是什么意思戈盈。

from django.conf import settings

settings 是 django 的配置文件鉤子,你可以在項(xiàng)目的任何地方引入它谆刨,可以通過 . 路徑符來訪問項(xiàng)目的配置塘娶。比如 settings.ROOT_URLCONF 就會(huì)返回根 url 配置。關(guān)于鉤子痊夭,我需要多說兩句刁岸。講道理,如果需要引用項(xiàng)目配置她我,標(biāo)準(zhǔn)的寫法難道不應(yīng)該是 import project.settings as settings 嗎虹曙,這樣才能連接到項(xiàng)目的配置啊,為什么我只是引入 django 自己的配置就可以了呢番舆。這就是 django 的神奇之處了酝碳,在一切都還沒有運(yùn)行之前,django 首先做的就是加載配置文件合蔽,并且把 settings 對(duì)象的屬性連接到各個(gè)配置上击敌。注意,settings 是個(gè)對(duì)象拴事,所以像 from django.conf.settings import DEBUG 之類的語法是錯(cuò)誤的沃斤。因?yàn)樗皇莻€(gè)模塊圣蝎。所以在訪問配置時(shí),只能以 settings.<key> 的形式來調(diào)用配置衡瓶。

首先加載配置文件是一件天經(jīng)地義的事情徘公,只有知道了各個(gè)部分的配置如何,相應(yīng)的功能才能按照需求運(yùn)轉(zhuǎn)哮针。請(qǐng)大家記住這一點(diǎn)关面,這非常重要。在 django 中十厢,加載配置文件有兩種方式:

第一種是使用 settings.configure(**settings)
手動(dòng)的寫每一項(xiàng)配置等太,這樣做的好處是,如果你需要配置的東西不多蛮放,那就不單獨(dú)再建個(gè)文件作為配置文件了缩抡。

第二種是使用 django.setup()
這是通過環(huán)境變量來配置的方法。
django.setup() 方法會(huì)自己查詢環(huán)境變量 'DJANGO_SETTINGS_MODULE` 的值包颁,會(huì)把它的值作為配置文件的路徑瞻想,并讀取這個(gè)文件的配置。

以上兩種方法都可以用來配置 django 娩嚼。我們這里采用第一種蘑险。注意,兩種方式必須用一種岳悟,也就是說佃迄,想要使用 django ,必須對(duì) django 進(jìn)行配置竿音。

from django.http import HttpResponse

用于返回一個(gè)響應(yīng)和屎。

from django.conf.urls import url
用于配置 urlpatterns 。

首先春瞬,讓我們來編寫配置柴信,在 test.py下一行接著寫:

test.py

setting = {
    'DEBUG':True,
    'ROOT_URLCONF':__name__
}

settings.configure(**setting)

我們只是進(jìn)行了簡(jiǎn)單的配置,設(shè)置 DEGUBTrue 是因?yàn)槲覀兿胍诔鲥e(cuò)時(shí)能看到錯(cuò)誤報(bào)告宽气。設(shè)置 ROOT_URLCONF__name__ 也就是這個(gè)文件本身随常,也就是說,我們打算把 urlpatterns 這個(gè)變量寫進(jìn)這個(gè)文件中萄涯。

這個(gè)配置很簡(jiǎn)單吧绪氛。

接下來讓我們編寫視圖,在 test.py 加入以下代碼:

test.py

def home(request):
    return HttpResponse('Hello world!')

這個(gè)視圖非常簡(jiǎn)單涝影,僅僅是返回一個(gè)字符串枣察。

最后,把 urlpatterns 寫在下面:

test.py

urlpatterns = [url('^$',home,name='home')]

到目前為止,你的代碼應(yīng)該是這樣的:

test.py

from django.conf import settings
from django.http import HttpResponse
from django.conf.urls import url
setting = {
    'DEBUG':True,
    'ROOT_URLCONF':__name__
}

settings.configure(**setting)

def home(request):
    return HttpResponse('Hello world!')

urlpatterns = [url('^$',home,name='home')]

該如何運(yùn)行呢序目?一般情況下臂痕,我們是用 manage.py 來運(yùn)行的。那 manage.py 又是怎么運(yùn)行的猿涨?在 manage.py 內(nèi)部握童,它調(diào)用了 django 的 exute_from_command_line(**command_line_args) 方法來運(yùn)行我們的應(yīng)用,所以叛赚,把這部分代碼添加到最后(實(shí)際上澡绩,這是從 manage.py 復(fù)制粘貼過來的,去掉了不必要的部分俺附,大家也可以這么做肥卡,嘿嘿嘿):

test.py

if __name__ == '__main__':
    import sys
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)

此時(shí),你的代碼應(yīng)該長(zhǎng)這樣:

test.py

from django.conf import settings
from django.http import HttpResponse
from django.conf.urls import url
setting = {
    'DEBUG':True,
    'ROOT_URLCONF':__name__
}

settings.configure(**setting)

def home(request):
    return HttpResponse('Hello world!')

urlpatterns = [url('^$',home,name='home')]

if __name__ == '__main__':
    import sys
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)

回到 test 目錄下昙读,在終端運(yùn)行 python test.py runserver 召调,然后在瀏覽器訪問 127.0.0.1:8000 ,不出意外的話蛮浑,你會(huì)看到瀏覽器上有個(gè) hello world

我們僅僅用了 19 行代碼就完成了一個(gè)單文件的 django 應(yīng)用只嚣。其實(shí)它的原理很簡(jiǎn)單沮稚,就是把以前分開的代碼給放在了一起,urls.pytes.pysettings.pytest.py 册舞,views.pytest.py蕴掏,甚至連 manage.py 也是 test.py
這個(gè)小 demo 意義在于讓大家了解 django 在運(yùn)行的時(shí)候都發(fā)生了些什么调鲸,了解 django 的運(yùn)行流程盛杰,為以后的開發(fā)打下基礎(chǔ)。

REST 是什么

先有個(gè)印象

REST的種種好處我不再贅述藐石。簡(jiǎn)單的說說為什么我們需要用 REST 即供。相信寫過模板的同學(xué)都知道,只要哪怕頁面中的數(shù)據(jù)有一絲絲變動(dòng)于微,那整個(gè)頁面都需要重新渲染逗嫡,這對(duì)性能無疑是巨大的浪費(fèi),并且頁面中只有一些元素會(huì)和數(shù)據(jù)相聯(lián)系株依,比如列表中的 <li> 元素驱证,如果數(shù)據(jù)有變化,能直接只更新 <li> 元素就好了恋腕,REST 就是為此而生抹锄。
提到 REST ,很多人可能知道一些概念,比如我們將要做的前后端分離的項(xiàng)目會(huì)用到它伙单,大概明白它可以用用 json 來交換數(shù)據(jù)获高。REST 不是什么具體的軟件或者代碼,而是一種思想车份。這么說就太抽象了谋减,REST 剛出來的時(shí)候是以論文的形式提出的,是一種設(shè)計(jì)的形式扫沼。對(duì)它的概念我們就先了解到這里出爹。在本章,我們就把 REST 簡(jiǎn)單的當(dāng)作是不再讓 django 來渲染我們的前端缎除,而是用 JS 在前端請(qǐng)求數(shù)據(jù)严就,用 JS 來渲染我們的頁面。讓 django 專注于后端的數(shù)據(jù)處理器罐。

我們的 REST

為了明確我們的 REST 開發(fā)梢为,我們的前后端的分工大概如下:

客戶端(瀏覽器)----> 前端頁面-----> 后端處理數(shù)據(jù),并把數(shù)據(jù)以 json 形式發(fā)送到前端
(這里本來是 flow 流程圖轰坊,結(jié)果簡(jiǎn)書貌似不支持)

我們的 REST 設(shè)計(jì)目前就是這樣铸董,實(shí)際上,REST 的抽象架構(gòu)也就是這樣的肴沫,

第一個(gè)REST項(xiàng)目

這個(gè)項(xiàng)目的意義在于讓大家了解 REST 的大致開發(fā)流程粟害,踩踩需要踩的坑。這次我們會(huì)做一個(gè)簡(jiǎn)單的在線代碼執(zhí)行系統(tǒng)颤芬,由于不會(huì)用到數(shù)據(jù)庫和模版悲幅,所以我們就使用剛才學(xué)習(xí)的單文件 django 來開發(fā)這個(gè)應(yīng)用。

注意站蝠,在開發(fā)這個(gè)應(yīng)用時(shí)汰具,需要你對(duì) JavaScriptJQuery 有最基本的了解,要是你對(duì)他們還不了解菱魔,那就在敲代碼時(shí)多多查閱文檔留荔,在練習(xí)當(dāng)中學(xué)會(huì)他們。同時(shí)我們還會(huì)使用 Bootstrap 豌习。在跟隨教程敲代碼時(shí)存谎,注意多翻翻文檔,一邊敲一邊查看文檔肥隆,搞明白每一行代碼是是什么意思既荚。同時(shí),代碼注釋也是很好的文檔搜索關(guān)鍵詞栋艳。

設(shè)計(jì)應(yīng)用

我們希望在用戶訪問我們的主頁恰聘,并能在頁面中編寫python代碼,在點(diǎn)擊執(zhí)行按鈕時(shí),主頁上能返回程序執(zhí)行的結(jié)果晴叨。

創(chuàng)建我們的應(yīng)用

新建一個(gè)文件夾凿宾,叫做 online_python ,并創(chuàng)建的目錄結(jié)構(gòu):

onlie_python/
    index.html
    online_app.py

準(zhǔn)備工作

先把剛才在 test.py 里的代碼復(fù)制過來兼蕊,

online_app.py

from django.conf import settings
from django.http import HttpResponse
from django.conf.urls import url
setting = {
    'DEBUG':True,
    'ROOT_URLCONF':__name__
}

settings.configure(**setting)

def home(request):
    return HttpResponse('Hello world!')

urlpatterns = [url('^$',home,name='home')]

if __name__ == '__main__':
    import sys
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)

我們需要用戶在訪問訪問 http://127.0.0.1:8000/ 時(shí)初厚,視圖應(yīng)該返回主頁的 html,也就是我們的 index.html 孙技。由于我們并沒有使用 django 的模板引擎产禾,所以 render 函數(shù)也不能用了。所以我們需要自己手動(dòng)的把 index.html 寫入到響應(yīng)中牵啦。所以把我們的 home 函數(shù)改成這個(gè)樣子:

online_app.py

def home(request):
    with open('index.html','rb') as f:
        html = f.read()
    return HttpResponse(html)

注意亚情,這里是以二進(jìn)制讀取的方式('rb')打開的 index.html ,也就是說最終的 html 的值為字節(jié)串哈雏,也就是 b'....'的形式楞件,為什么要用二進(jìn)制形式打開呢?
原因有兩個(gè):

  1. 最主要的也是最重要的裳瘪,在一個(gè) html 文件中土浸,你不知道會(huì)有什么樣的語言夾雜進(jìn)去,一旦 python 無法識(shí)別其中的編碼彭羹,就會(huì)報(bào)編碼錯(cuò)誤踏烙。然而實(shí)際上芍躏,讀取并解析 html 是瀏覽器來完成的工作,django 只是簡(jiǎn)單的充當(dāng)一個(gè)傳遞者的角色值戳,它只需要把 html 文件傳給瀏覽器即可西篓。
  1. 這也涉及到了一些瀏覽器和服務(wù)器數(shù)據(jù)傳輸?shù)闹R(shí)愈腾,瀏覽器與服務(wù)器的內(nèi)容交互都是以二進(jìn)制流的方式進(jìn)行的,所以正規(guī)的響應(yīng)就應(yīng)返回字節(jié)串岂津。django 的 HttpResponse 為我們做了轉(zhuǎn)換的工作虱黄,所以你也可以把字符串傳給 HttpResponse

由于我們的 index.html 還沒有任何內(nèi)容吮成,在 index.html 寫入以下內(nèi)容:

index.html

<!DOCTYPE html>
<html>
<head>
    <title>在線 Python 解釋器</title>
</head>
<body>
<h1>在線 Python 解釋器</h1>
</body>
</html>

在根路徑下運(yùn)行 python online_app.py runserver 橱乱,在瀏覽器中訪問 http://127.0.0.1:8000 ,你應(yīng)該可以在瀏覽器中看到 在線 Python 解釋器 的字樣粱甫。

一切已經(jīng)就緒泳叠,你準(zhǔn)備好了嗎?

前端開發(fā)

接下來茶宵,讓我們專注于前端的開發(fā)危纫,如果你對(duì) js 和 jqery 不是很了解,那也沒關(guān)系,教程中會(huì)進(jìn)行講解种蝶,如果有不懂的地方契耿,利用教程中的關(guān)鍵詞去查文檔就行了。

我們需要使用 Bootstrap 螃征,所以要引入 jqery搪桂。在 Bootstrap 的官網(wǎng)的基本模板給復(fù)制進(jìn)來并替換掉原來的代碼,刪除其中的注釋盯滚,此時(shí)你的 index.html 是這樣的:

index.html

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
    <h1>你好踢械,世界!</h1>
    <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    <script src="js/bootstrap.min.js"></script>
  </body>
</html>

我們需要從頁面中來引用 Bootstrap 的 js 文件和 css 文件淌山,所以把第 8 行 替換為:

<link rel="stylesheet"  integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

把第 13 行替換為:

<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

現(xiàn)在裸燎,讓我們正式開始前端頁面的開發(fā)。最好把 Bootstrap 的官方文檔打開泼疑,好方便隨時(shí)查閱德绿。

首先,我們需要對(duì)頁面進(jìn)行布局退渗,先把我們頁面的大概的樣子設(shè)計(jì)好移稳,我們的頁面大概是這樣的:

+-------------------------+
+       ---標(biāo)題----       +
+ 代碼輸入框     結(jié)果顯示框 +
+            |            +
+  +------+  |  +------+  +
+  +      +  |  +      +  +
+  +      +  |  +      +  +
+  +      +  |  +      +  +
+  +------+  |  +------+  +
+            |            +
+                         +
+-------------------------+

由于前端代碼的特殊性,代碼所在的行對(duì)最終的結(jié)果有影響会油,所以我給下面的代碼手動(dòng)添加了行號(hào)个粱。

注意:在下面的代碼中 + 與 - 分別代表代碼的刪除、增加翻翩,他們之前的數(shù)字是行號(hào)都许。

把第 7 行的改成:
index.html

7- <title>Bootstrap 101 Template</title>
7+ <title>在線 PYthony 解釋器</title>

刪除第 11 行內(nèi)容, 并替換為Bootstrap 布局容器 <div class=container></div>嫂冻,我們將會(huì)在這個(gè)布局容器中完成我們的頁面胶征。

index.html

11- <h1>你好,世界桨仿!</h1>
11+  <div class="container"><!-- 頁面的整體布局 -->
12+      
13+  </div>

我們可以大致把頁面看成兩個(gè) Bootstrap container 的兩個(gè) row睛低。
也就是:

+-------------------------+
+       ---標(biāo)題----       +---------> 標(biāo)題單獨(dú)為一行
+ 代碼輸入框   結(jié)果顯示框   +------>+
+            |            +      +
+  +------+  |  +------+  +      +
+  +      +  |  +      +  +      +
+  +      +  |  +      +  +      +-----> 主體內(nèi)容可以看作一行分成了兩列
+  +      +  |  +      +  +      +
+  +------+  |  +------+  +      +
+            |            +      +
+                         +----->+
+-------------------------+

按照上面的布局,我們這樣來寫代碼:

inxex.html

12+      <div class="row"> <!-- 這一行單獨(dú)用來放標(biāo)題 -->
13+        <div class="col-md-12"> <!-- 根據(jù) bs規(guī)定服傍,所有內(nèi)容應(yīng)放在 col 中钱雷。這一列占滿一行 -->
14+           <p class="text-center h1"> <!-- text-center 類是 bs 中央排版,h1 是 bs 一號(hào)標(biāo)題類 -->
15+           在線 Python 解釋器
16+         </p>
17+        </div>
18+      </div>
19       <hr><!-- 標(biāo)題和真正內(nèi)容的分割線 -->
20+      <div class="row"></div><!-- 這一行用來放置主要內(nèi)容 -->

保存你的代碼吹零,在瀏覽器中打開 index.html 你可以看到瀏覽器中央已經(jīng)有個(gè)標(biāo)題了罩抗。

已經(jīng)可以看見標(biāo)題了

接下來我們把代碼輸入框和結(jié)果顯示框也完成。
因?yàn)槲覀兊闹黧w布局是左右布局瘪校,所以我們要先把左右布局先寫好:

index.html

20 <div class="row"><!-- 這一行用來放置主要內(nèi)容 -->
21+  <div class="col-md-6"></div><!-- 代碼輸入部分 -->
22+  <div class="col-md-6"></div><!-- 結(jié)果顯示部分 -->
23</div>

現(xiàn)在我們把需要在屏幕上顯示的具體元素先寫好:

代碼輸入部分:

index.html

21<div class="col-lg-6"><!-- 代碼輸入部分 -->
22+  <p class="text-center h3">
23+    在下面輸入代碼
24+  </p>
25+  <textarea id="code" class="form-control" placeholder="Your code here."></textarea> 
26+  <button type="button" class="btn btn-primary">運(yùn)行</button>
27</div>

結(jié)果顯示部分:

index.html

28<div class="col-lg-6"><!-- 結(jié)果顯示部分 -->
29+   <p class="text-center">運(yùn)行結(jié)果</p>
30+   <div class="col-lg-12"><textarea id="output" disabled placeholder="Please input your code and click <run> button to excute your python script" class="text-center form-control"></textarea></div>
31+   </div>
32</div>

我們?yōu)榱瞬蝗ヌ幚砬岸藦?fù)雜的轉(zhuǎn)義符號(hào)澄暮,我們就用 <textarea> 來展示我們的文本名段,只是這個(gè)文本是不可編輯的。

我們大概的框架就已經(jīng)寫好了泣懊,目前伸辟,你的 index.html 應(yīng)該是這個(gè)樣子的:

index.html

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>在線 Python 解釋器</title>
    <link rel="stylesheet"  integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  </head>
  <body><!--在下面的注釋中 bs 代表 bootstrap -->
  <div class="container"><!-- 頁面的整體布局 -->
      <div class="row"> <!-- 這一行單獨(dú)用來放標(biāo)題 -->
        <div class="col-lg-12"> <!-- 根據(jù) bs規(guī)定,所有內(nèi)容應(yīng)放在 col 中馍刮。這一列占滿一行 -->
          <p class="text-center h1"> <!-- text-center 是 bs 中央排版類信夫,h1 是 bs 一號(hào)標(biāo)題類 -->
            在線 Python 解釋器
          </p>
        </div>
      </div>
      <hr><!-- 標(biāo)題和真正內(nèi)容的分割線 -->
      <div class="row"><!-- 這一行用來放置主要內(nèi)容 -->
        <div class="col-lg-6"><!-- 代碼輸入部分 -->
          <p class="text-center h3">
            在下面輸入代碼
          </p>
          <textarea id="code" placeholder="Your code here." class="form-control"></textarea>
          <button id="run" type="button" class="btn btn-primary ">運(yùn)行</button>
        </div>
        <div class="col-lg-6"><!-- 結(jié)果顯示部分 -->
        <p class="text-center h3">運(yùn)行結(jié)果</p>
        <div class="col-lg-12"><textarea id="output" disabled placeholder="Please input your code and click <run> button to excute your python script" class="text-center form-control"></textarea></div>
        </div>
      </div>
  </div>
    <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
  </body>
</html>

現(xiàn)在,在瀏覽器里打開你的 index.html 卡啰,你會(huì)看到它是這個(gè)樣子的:

十分丑陋的界面

現(xiàn)在我們已經(jīng)看到了一個(gè)很粗糙的界面了静稻,雖然很丑,但是一切都在按照我們的計(jì)劃進(jìn)行匈辱。
接下來振湾,讓我們來編寫一些簡(jiǎn)單的 css 來讓界面變的美觀一點(diǎn),你要是不知道這些 css 都是什么意思亡脸,MDN 是個(gè)查詢文檔的好地方押搪。

在第8 行下面插入下面的代碼:

index.html

9+<style type="text/css">
10+    
11+</style>

按鈕的位置好像太偏了,讓我們用 css 來把它調(diào)整到一個(gè)合適的地方:

為了能夠改變 button 的位置浅碾,我們需要在外面套上一個(gè) div 元素大州,我們希望把按鈕放在右邊,所以需要用到 text-right
index.html

29- <button id="run" type="button" class="btn btn-primary">運(yùn)行</button>
29+ <div class="text-right"><button id="run" type="button" class="btn btn-primary ">運(yùn)行</button></div>

然后為我們的 button 添加上 css 樣式:

index.html

10+#run {
21+    width: 20%; /*規(guī)定按鈕的寬度*/
12+    margin-top: 10px; /*留出和輸入框的間距*/
13+}

保存你的 index.html 文件垂谢,在瀏覽器中打開它厦画,你會(huì)看到它是這個(gè)這樣子的:

美化之后的主頁

大家在打開的頁面中,試著輸入幾行代碼滥朱。你會(huì)發(fā)現(xiàn)這樣的情況:

不合適的輸入框

輸入框的大小是固定的根暑,只有手動(dòng)的點(diǎn)擊右邊的翻頁按鈕才可以看到下面的代碼,這樣的輸入框用起來簡(jiǎn)直不方便徙邻,我們需要改善一下用戶體驗(yàn)购裙。我們想讓輸入框的大小隨著輸入代碼的行數(shù)而改變,也就是說鹃栽,輸入框的大小是動(dòng)態(tài)變化的,同時(shí)躯畴,我們希望我們的輸出框也是動(dòng)態(tài)變化的民鼓。這就需要用到 js 了。
在 41 行下面插入一個(gè) <script> 標(biāo)簽
index.html

42+<script>
43+    
44+</script>

我們先來梳理一下動(dòng)態(tài)輸入輸出框的邏輯蓬抄。輸入一次大小就變化一次丰嘉,也就是說,它們的 css 是動(dòng)態(tài)變化的嚷缭,也就是它們的高度是動(dòng)態(tài)變化的饮亏。在 <textarea> 中耍贾,當(dāng)用戶的輸入超出了 <textarea> 的大小時(shí),它的右邊就會(huì)自動(dòng)出現(xiàn)一個(gè)滾動(dòng)條路幸,如果我們讓 textarea 的高度等度滾動(dòng)條的高度荐开,那么此時(shí) <textarea> 的高度就等于用戶輸入的文本高度了。所以我們需要在用戶輸入一次之后就調(diào)整一下大小简肴。
先來編寫改變大小的函數(shù):

index.html

43+ // 改變大小函數(shù)
44+function changeSize(ele){
45+   $(ele).css({'height':'auto','overflow-y':'hidden'}).height(ele.scrollHeight)
46+}

我們用 js 動(dòng)態(tài)的改變了 <textarea> 的高度 晃听,在這里我們需要注意一點(diǎn),我們并沒有一來就把高度設(shè)置為 <textarea> 滾動(dòng)條的高度砰识,而是先讓它自動(dòng)適應(yīng)能扒,然后再改變它的大小。這是為了讓輸入框能夠自動(dòng)“縮回去”辫狼,想想看初斑,如果我輸入了幾行文本,出現(xiàn)了滾動(dòng)條膨处,此時(shí)我們的輸入框自動(dòng)調(diào)整大小见秤,滾動(dòng)條消失,然后又刪除這幾行文本灵迫,你會(huì)發(fā)現(xiàn)秦叛,我們的輸入框“回不去”了,為什么呢瀑粥?因?yàn)榇藭r(shí)挣跋,滾動(dòng)條的高度還是原來的高度,所以輸入框還是原來的大小狞换,需要改變這個(gè)大小避咆,所以我們就需要 height:auto 來幫我們“縮回去。

沒看懂剛才的解釋修噪?沒關(guān)系查库,等我們完成這一部分,會(huì)有一個(gè)小實(shí)驗(yàn)黄琼,大家跟著試一次就明白了樊销。

現(xiàn)在把這個(gè)動(dòng)態(tài)的變化應(yīng)用到輸入框。

index.html

47+// 應(yīng)用到輸入框
48+$('#code').each(function(){
49+    this.oninput = function(){
50+      changeSize(this)
51+    }
52+  })

現(xiàn)在脏款,保存你的 index.html围苫,在瀏覽器里打開他,不出意外的話撤师,你看到的會(huì)是這個(gè)效果:

動(dòng)態(tài)輸入框效果

現(xiàn)在我們來做之前說的實(shí)驗(yàn)剂府。
大家把 45 行改成這樣,去掉了:

index.html

45- $(ele).css({'height':'auto','overflow-y':'hidden'}).height(ele.scrollHeight)}
45+$(ele).css({'height':ele.scrollHeight,'overflow-y':'hidden'})
      }

保存之后在瀏覽器里打開剃盾,你會(huì)發(fā)現(xiàn)效果是這樣的:


不能“縮回去”的輸入框

所以我們先使 height:auto 腺占,再讓高度等于滾動(dòng)條的高度淤袜,才能讓輸入框“縮回去”。現(xiàn)在實(shí)驗(yàn)做完了衰伯,把 45 行改回去铡羡。

index.html

45-$(ele).css({'height':ele.scrollHeight,'overflow-y':'hidden'})}
45+ $(ele).css({'height':'auto','overflow-y':'hidden'}).height(ele.scrollHeight)}

仔細(xì)的讀者也許已經(jīng)發(fā)現(xiàn),輸入和輸出框右下角有個(gè)小三角嚎研,那是瀏覽器為了方便用戶自己調(diào)整大小而產(chǎn)生的蓖墅,然而我們并不希望用戶這樣做,所以我們需要禁用這個(gè)功能临扮,只需要在 css 里加入 resize: none; 就可以了论矾。是不是覺得字體很小杆勇?我們也把字體改的大一點(diǎn)贪壳。

index.html

14+#code {
15+  font-size: 25px;
16+  resize: none;
17+}
18+#output {
19+  font-size: 25px;
20+  resize: none;
21+}

此時(shí)你的 index.html 應(yīng)該長(zhǎng)得像這樣:

index.html

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>在線 Python 解釋器</title>
    <link rel="stylesheet"  integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style>
      #run {
          width: 20%; /*規(guī)定按鈕的寬度*/
          margin-top: 10px; /*留出和輸入框的間距*/
      }
      #code {
        font-size: 25px;
        resize: none;
      }
      #output {
        font-size: 25px;
        resize: none;
      }
    </style>
  </head>
  <body><!--在下面的注釋中 bs 代表 bootstrap -->
  <div class="container"><!-- 頁面的整體布局 -->
      <div class="row"> <!-- 這一行單獨(dú)用來放標(biāo)題 -->
        <div class="col-lg-12"> <!-- 根據(jù) bs規(guī)定,所有內(nèi)容應(yīng)放在 col 中蚜退。這一列占滿一行 -->
          <p class="text-center h1"> <!-- text-center 是 bs 中央排版類闰靴,h1 是 bs 一號(hào)標(biāo)題類 -->
            在線 Python 解釋器
          </p>
        </div>
      </div>
      <hr><!-- 標(biāo)題和真正內(nèi)容的分割線 -->
      <div class="row"><!-- 這一行用來放置主要內(nèi)容 -->
        <div class="col-lg-6"><!-- 代碼輸入部分 -->
          <p class="text-center h3">
            在下面輸入代碼
          </p>
          <textarea id="code" placeholder="Your code here." class="form-control" ></textarea>
          <div class='text-right'><button id="run" type="button" class="btn btn-primary ">運(yùn)行</button></div>
        </div>
        <div class="col-lg-6"><!-- 結(jié)果顯示部分 -->
        <p class="text-center h3">運(yùn)行結(jié)果</p>
        <div class="col-lg-12"><textarea id="output" disabled placeholder="Please input your code and click <run> button to excute your python script" class="text-center form-control"></textarea></div>
        </div>
      </div>
  </div>
    <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    <script>
      // 動(dòng)態(tài)大小函數(shù)
      function changeSize(ele){
        $(ele).css({'height':'auto','overflow-y':'hidden'}).height(ele.scrollHeight)
      }
      // 應(yīng)用到輸入框
      $('#code').each(function(){
          this.oninput = function(){
            changeSize(this)
          }
        })
    </script>
  </body>
</html>

保存你的 index.html ,在瀏覽器里打開它钻注,你看到的應(yīng)該是這樣:

最終的樣子

到目前蚂且,我們有關(guān)界面 UI 顯示的的部分已經(jīng)全部完成了。當(dāng)然幅恋,有興趣的同學(xué)可以自己加一個(gè)背景杏死。我自己隨便選了個(gè)背景。捆交。


我加的背景

現(xiàn)在重頭來了淑翼,我們將實(shí)現(xiàn)前端頁面和后端的交互部分。
同樣的品追,我們先來完成對(duì)交互的設(shè)計(jì)玄括。我們希望這樣來交互:
用戶點(diǎn)擊 運(yùn)行 按鈕時(shí),js 自動(dòng)發(fā)送輸入框的代碼肉瓦,待后端處理完之后遭京,接收來自后端的結(jié)果,然后再把結(jié)果顯示在輸出框內(nèi)泞莉。我們希望我們用 POST 方法向后端的 /api/ 路徑發(fā)送用戶代碼洁墙。

在真正開始開發(fā)之前,在這里我們會(huì)用到一個(gè)東西戒财,叫做 ajax,它相當(dāng)于前端的 requests 捺弦,為我們提供了 js 向 URL 發(fā)送請(qǐng)求的功能饮寞,只是功能沒 requests 那么強(qiáng)大孝扛,jquery 提供了 ajax 支持,所以我們直接使用就好了幽崩。不過我建議苦始,對(duì) ajax 不了解的的同學(xué),現(xiàn)在最好打開 jquery 的 ajax 部分的文檔慌申,在跟隨代碼時(shí)對(duì)照著看陌选。

先獲取用戶輸入框代碼:

index.html

61+//獲取輸入框代碼
62+function getCode(){
63+  return $('#code').val()
64+}

將獲取的結(jié)果打印到輸出框,同時(shí)蹄溉,輸出框需要根據(jù)內(nèi)容的大小而改變咨油。
index.hthml

65+//打印結(jié)果到輸出框并改變輸出框大小
66+function print(data){
67+  var ele = document.getElementById('output')
68+  output.value = data['output']
69+  changeSize(output)
70+}

需要注意的是,我們的打印函數(shù)最終是作為 ajax 請(qǐng)求成功之后的回調(diào)函數(shù)來使用的柒爵,ajax 會(huì)自動(dòng)往里面?zhèn)魅胍粋€(gè) data 參數(shù)役电,這個(gè) data 是響應(yīng)數(shù)據(jù)。我們并沒有直接就打印 data 棉胀,因?yàn)槿f一后端需要對(duì)數(shù)據(jù)做進(jìn)一步的分類法瑟,比如多一個(gè) status 字段來表示代碼執(zhí)行狀態(tài)(成功或者失敗)唁奢,那么直接打印 data 就是不合適的做法了霎挟。所以我們選擇的是提取 data 的 output 字段,這樣不管 data 怎么變麻掸,只要有 output 參數(shù)酥夭,我們展示結(jié)果的代碼就能正常執(zhí)行。

最后论笔,把發(fā)送代碼的動(dòng)作綁定到點(diǎn)擊按鈕:

index.html

71+// 點(diǎn)擊按鈕發(fā)送代碼
72+$('#run').click(function(){
73+  $.ajax({
74+    url:'/api/', //代碼發(fā)送的地址
75+    type:'POST', // 請(qǐng)求類型
76+    data: {'code':getCode()},//調(diào)用代碼獲取函數(shù)采郎,獲得代碼文本
77+    dataType: 'json', //期望獲取的響應(yīng)類型為 json
78+    success: print // 在請(qǐng)求成功之后調(diào)用 pprint 函數(shù),將結(jié)果打印到輸出框
79+  })
80+})

到這里狂魔,我們前端的所有內(nèi)容就算完成了蒜埋。完整的前端代碼大家可以在 github 中找到,就不貼在這里了最楷。接下來整份,讓我們進(jìn)入后端開發(fā)。

后端開發(fā)

經(jīng)過了漫長(zhǎng)的前端開發(fā)籽孙,我們終于來到了后端烈评。我們的代碼在這里將不會(huì)再標(biāo)行數(shù)。所以大家可以靈活安排自己的代碼犯建,自由的做相應(yīng)的調(diào)整讲冠。

打開 online_app.py ,現(xiàn)在頂部引入:

online_app.py

from django.views.decorators.http import require_POST # 目前的 API 視圖只能用于接收 POST 請(qǐng)求
from django.http import JsonResponse # 用于返回 JSON 數(shù)據(jù)

先來編寫我們的 api 視圖函數(shù):

online_app.py

@require_POST
def api(request):
    code = request.POST.get('code')
    output = run_code(code)
    return JsonResponse(data={'output':output})

具體運(yùn)行代碼的函數(shù)我們將會(huì)在下面實(shí)現(xiàn)适瓦。在下面把我們的 URL 配置改成這樣竿开,加上我們的 api 視圖谱仪。

online_app.py

urlpatterns = [url('^api/$',api,name='api'),
                url('^$',home,name='home')]

現(xiàn)在我們來實(shí)現(xiàn) run_code 函數(shù)。在接著往下看之前否彩,先做個(gè)深呼吸疯攒,因?yàn)檫@個(gè)函數(shù)會(huì)用到你可能不熟悉的模塊 subprocess ,當(dāng)很多人看到這個(gè)模塊的名字或者聽到“多進(jìn)程”這個(gè)詞的時(shí)候列荔,或許他能對(duì) python 實(shí)現(xiàn)多進(jìn)程的種種缺點(diǎn)批判一番敬尺,但是當(dāng)叫他真的寫個(gè)多進(jìn)程時(shí)卻會(huì)感到十分為難。別擔(dān)心贴浙,我們只是在這里簡(jiǎn)單的使用 subprocess 封裝好了的功能砂吞。為了更好的編寫這個(gè)函數(shù),確保它的功能正常悬而,我們需要為這個(gè)函數(shù)編寫測(cè)試呜舒。所以我們需要在編寫好了這個(gè)函數(shù)在把它應(yīng)用到我們的 app 中,所以在你的 app 的路徑笨奠,也就是 online_python 下建一個(gè)新文件 test.py 袭蝗。為了一切從簡(jiǎn),這里我們就不使用 unittest 了般婆,我們使用人肉測(cè)試到腥。

先引入 subprocess
test.py

import subprocess

接下來我們需要仔細(xì)考慮 run_code 會(huì)遇到的情況:

  1. 能夠正確執(zhí)行來自客戶端的代碼。也就是說蔚袍,如果客戶端的代碼是正確的乡范,那么 run_code 的輸出結(jié)果也應(yīng)該是預(yù)期的那樣。
  2. 當(dāng)用戶代碼發(fā)生錯(cuò)誤時(shí)啤咽,能夠返回錯(cuò)誤信息晋辆。來自客戶端的代碼難免會(huì)有錯(cuò)誤,我們需要像 python 解釋器一樣返回詳盡的錯(cuò)誤跟蹤信息宇整。
  3. 當(dāng)用戶的代碼執(zhí)行時(shí)間過長(zhǎng)時(shí)瓶佳,自動(dòng)中斷代碼的執(zhí)行,并在前端給出執(zhí)行超時(shí)提示鳞青。有的時(shí)候霸饲,客戶端的代碼可能陷入死循環(huán),為了提早讓用戶知道代碼異常臂拓,我們應(yīng)該主動(dòng)中斷代碼執(zhí)行厚脉。有的時(shí)候用戶代碼可能是正確的,但是執(zhí)行時(shí)間真的太長(zhǎng)胶惰,我們也需要中斷執(zhí)行傻工,不能讓這個(gè)進(jìn)程一直占用系統(tǒng)資源。一旦用戶過多,系統(tǒng)資源很快就會(huì)支撐不住

在編寫 run_code 的過程中中捆,也是對(duì) subprocess 模塊的學(xué)習(xí)威鹿,所以大家可以把 subprocess 文檔打開對(duì)照著看

首先轨香,run_code 能正確的執(zhí)行客戶端代碼。由于我們是直接運(yùn)行的字符串幼东,所以首先得解決如何用 python 腳本來執(zhí)行 python 字符串臂容。那就是使用 python -c <your_script_code> 命令。所以我們應(yīng)該開一個(gè)進(jìn)程來執(zhí)行這個(gè)命令根蟹。在 subprocess 中脓杉,執(zhí)行一個(gè)進(jìn)程最常用的方法是 subprocess.run(*args,**kwargs), 但是它不返回輸出結(jié)果简逮,所以我們需要使用 subprocess.check_output(*args,**kwargs)∏蛏ⅲ現(xiàn)在我們來編寫 run_cdoe 函數(shù):

test.py

import subprocess

def run_cdoe(code):
    output = subprocess.check_output(['python','-c',code])
    return output

code = """print('Test success')"""
print(run_cdoe(code))

現(xiàn)在我們來看看輸出,看看是不是我們想要的輸出:

輸出

輸出的是字節(jié)串散庶。但是我們期望的是字符串蕉堰。我們有兩種辦法,第一種是直接手動(dòng)轉(zhuǎn)換結(jié)果悲龟,將 output 轉(zhuǎn)換為 string屋讶,但是這會(huì)有個(gè)問題。你要是直接解碼须教,會(huì)出現(xiàn)一個(gè)問題皿渗,如果你得到的結(jié)果是來自你的 shell ,那輸出結(jié)果的編碼就是 shell 的編碼轻腺,每個(gè)系統(tǒng)的 shell 編碼是不同的乐疆,難道需要我們?yōu)槊總€(gè) shell 編寫解碼代碼嗎?所以這個(gè)看起來可行的方法是沒有普適性的贬养。所以我們就只能采用第二種方法了挤土,第二個(gè)方法很簡(jiǎn)單,那就是加上 universal_newlines=True 參數(shù)煤蚌,加上這個(gè)參數(shù)之后耕挨,subprocess 會(huì)自動(dòng)為我們將輸出解碼為字符串。它具體是怎么實(shí)現(xiàn)的尉桩,大家可以去文檔看介紹⊥舱迹現(xiàn)在正確執(zhí)行代碼給出正確輸出結(jié)果的功能解決了。
期望輸出

現(xiàn)在解決第 2 個(gè)需求蜘犁。輸出錯(cuò)誤翰苫。
在 subprocess 中,有個(gè)參數(shù)是 stderr ,大家看意思就已經(jīng)明白它是干什么的了奏窑,是用來控制錯(cuò)誤輸出流的导披。默認(rèn)的錯(cuò)誤輸出是輸出到主進(jìn)程的,也就是調(diào)用這個(gè)進(jìn)程的進(jìn)程埃唯。讓我們來故意引發(fā)一個(gè)錯(cuò)誤撩匕,看看具體是怎么回事:

錯(cuò)誤輸出

大家可以看到,子進(jìn)程的錯(cuò)誤輸出也在主進(jìn)程的錯(cuò)誤輸出里墨叛。
我們希望錯(cuò)誤輸出也能輸出到 output 上止毕,output 本來是子進(jìn)程的標(biāo)準(zhǔn)輸出,所以現(xiàn)在我們需要捕捉子進(jìn)程的錯(cuò)誤輸出流導(dǎo)漠趁。怎么做呢扁凛,那就是讓 stderr=subprocess.STDOUT,大家就會(huì)看到這個(gè)效果:
錯(cuò)誤輸出流重定向

子進(jìn)程的報(bào)錯(cuò)已經(jīng)看不到了闯传,因?yàn)殄e(cuò)誤輸出流已經(jīng)被重定向到了子進(jìn)程谨朝。但是我們看,主進(jìn)程依然報(bào)錯(cuò)了甥绿。這是 sbuprocess 的機(jī)制字币,在子進(jìn)程沒有執(zhí)行成功時(shí),就會(huì)引發(fā) subprocess.CalledProcessError 妹窖,這個(gè)錯(cuò)誤的 output 屬性包含了子進(jìn)程的錯(cuò)誤輸出纬朝。所以我們這樣來編寫 run_code

test.py

import subprocess

def run_cdoe(code):
    try:
        output = subprocess.check_output(['python','-c',code],universal_newlines=True,stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        output = e.output
    return output

我們來看看效果:

最終效果

現(xiàn)在還剩下第 3 個(gè)需求,控制客戶端代碼執(zhí)行時(shí)間骄呼。同樣的我們還是依靠給 subprocess 傳遞參數(shù)來實(shí)現(xiàn)控制共苛,這個(gè)參數(shù)就是 timeout ,它的單位是秒蜓萄,所以我們希望在 30 秒之后還沒執(zhí)行結(jié)束就中斷執(zhí)行隅茎。在 subprocess 中,超時(shí)引發(fā)的錯(cuò)誤是 subprocess.TimeoutExpired嫉沽,它的 output 參數(shù)也包含了子進(jìn)程的錯(cuò)誤輸出辟犀。所以把 run_code 改成這樣:

test.py

import subprocess

def run_cdoe(code):
    try:
        output = subprocess.check_output(['python','-c',code],
                universal_newlines=True,
                stderr=subprocess.STDOUT,
                timeout=30)
    except subprocess.CalledProcessError as e:
        output = e.output
    except subprocess.TimeoutExpired as e:
        output = '\r\n'.join(['Time Out!!!',e.output])
    return output

讓我們來看看測(cè)試:

最終測(cè)試

效果不錯(cuò)!大功告成绸硕!
現(xiàn)在把我們的 run_code 函數(shù)復(fù)制到 online_app.py 里堂竟,記得也要導(dǎo)入 subprocess 庫。
最終你的 online_app.py 會(huì)是這樣的:

online_app.py

from django.conf import settings
from django.http import HttpResponse, JsonResponse# JsonResponse 用于返回 JSON 數(shù)據(jù)
from django.conf.urls import url
from django.views.decorators.http import require_POST
import subprocess
setting = {
    'DEBUG':True,
    'ROOT_URLCONF':__name__,
}

settings.configure(**setting)

# 主視圖
def home(request):
    with open('index.html','rb') as f:
        html = f.read()
    return HttpResponse(html)
# 執(zhí)行客戶端代碼核心函數(shù)
def run_code(code):
    try:
        output = subprocess.check_output(['python','-c',code],
                universal_newlines=True,
                stderr=subprocess.STDOUT,
                timeout=30)
    except subprocess.CalledProcessError as e:
        output = e.output
    except subprocess.TimeoutExpired as e:
        output = '\r\n'.join(['Time Out!!!',e.output])
    return output
# API 請(qǐng)求視圖
@require_POST
def api(request):
    code = request.POST.get('code')
    output = run_code(code)
    return JsonResponse(data={'output':output})
# URL 配置
urlpatterns = [url('^$',home,name='home'),
               url('^api/$',api,name='api')]

if __name__ == '__main__':
    import sys
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)

我們的“人肉測(cè)試模塊”已經(jīng)完成它的任務(wù)玻佩,現(xiàn)在可以刪掉了〕鲟冢現(xiàn)在我們完成了前后端的功能開發(fā),讓我們來試試吧咬崔!在根路徑運(yùn)行 python online_python.py runserver税稼,訪問 http://127.0.0.1:8000 烦秩。試試往里面輸入代碼,看看能不能得到想要的結(jié)果郎仆。然后你會(huì)發(fā)現(xiàn)輸出框什么變化也沒有只祠!打開控制臺(tái)看看,你會(huì)發(fā)現(xiàn)這個(gè)情況:

又出問題了扰肌!

請(qǐng)求被禁止了抛寝,你可以在 django 的控制臺(tái)kan原來是跨域請(qǐng)求錯(cuò)誤∈镄瘢跨域請(qǐng)求 django 是怎么處理的呢墩剖?寫過模板表單的同學(xué)都知道,是通過給表單加 {% csrf_tokne %} 來實(shí)現(xiàn)的夷狰。那現(xiàn)在我們已經(jīng)是 REST 架構(gòu)了,已經(jīng)不需要它了郊霎,所以我們就選擇禁用 csrf 功能沼头。修改 onlime_app.py 如下:

現(xiàn)在頂部引入:
online_app.py

from django.views.decorators.csrf import csrf_exempt

把我們的 api 視圖修改為:
online_app.py

@csrf_exempt
@require_POST
def api(request):
    code = request.POST.get('code')
    output = run_code(code)
    return JsonResponse(data={'output':output})

現(xiàn)在趕緊運(yùn)行 python online_app.py runserver ,訪問 http://127.0.0.1:8000书劝,寫幾行代碼試試进倍,運(yùn)行一下。

最終效果

恭喜你完成了第一個(gè) REST APP!

下一章做什么购对?

在本章猾昆,我們知道了單個(gè)文件的 django 也可以運(yùn)行,通過單文件的 django 我們大致了解了 django 初始化運(yùn)行流程是什么骡苞,同時(shí)我們簡(jiǎn)單的了解了 REST 的概念垂蜗,并構(gòu)建了一個(gè)簡(jiǎn)單的 APP 。在下一章解幽,我們將會(huì)深入 REST 贴见,我們將會(huì)制作一個(gè)符合 REST 標(biāo)準(zhǔn)的 APP ,以此來熟悉 REST 標(biāo)準(zhǔn)躲株,同時(shí)了解 REST 最核心的概念————一切皆資源片部。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市霜定,隨后出現(xiàn)的幾起案子档悠,更是在濱河造成了極大的恐慌,老刑警劉巖望浩,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辖所,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡曾雕,警方通過查閱死者的電腦和手機(jī)奴烙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人切诀,你說我怎么就攤上這事揩环。” “怎么了幅虑?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵丰滑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我倒庵,道長(zhǎng)褒墨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任擎宝,我火速辦了婚禮郁妈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绍申。我一直安慰自己噩咪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布极阅。 她就那樣靜靜地躺著胃碾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筋搏。 梳的紋絲不亂的頭發(fā)上仆百,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音奔脐,去河邊找鬼俄周。 笑死,一個(gè)胖子當(dāng)著我的面吹牛髓迎,可吹牛的內(nèi)容都是我干的栈源。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼竖般,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼甚垦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起涣雕,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤艰亮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后挣郭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迄埃,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年兑障,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了侄非。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蕉汪。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖逞怨,靈堂內(nèi)的尸體忽然破棺而出者疤,到底是詐尸還是另有隱情,我是刑警寧澤叠赦,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布驹马,位于F島的核電站,受9級(jí)特大地震影響除秀,放射性物質(zhì)發(fā)生泄漏糯累。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一册踩、第九天 我趴在偏房一處隱蔽的房頂上張望泳姐。 院中可真熱鬧,春花似錦暂吉、人聲如沸仗岸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至较锡,卻和暖如春业稼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚂蕴。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工低散, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骡楼。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓熔号,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親鸟整。 傳聞我的和親對(duì)象是個(gè)殘疾皇子引镊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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

  • (一)、啟動(dòng)服務(wù)器 (二)篮条、創(chuàng)建數(shù)據(jù)庫表 或 更改數(shù)據(jù)庫表或字段 Django 1.7.1及以上 用以下命令 1....
    夏天夏星閱讀 5,662評(píng)論 0 17
  • # Python 資源大全中文版 我想很多程序員應(yīng)該記得 GitHub 上有一個(gè) Awesome - XXX 系列...
    aimaile閱讀 26,496評(píng)論 6 427
  • GitHub 上有一個(gè) Awesome - XXX 系列的資源整理,資源非常豐富弟头,涉及面非常廣。awesome-p...
    若與閱讀 18,662評(píng)論 4 418
  • 環(huán)境管理管理Python版本和環(huán)境的工具涉茧。p–非常簡(jiǎn)單的交互式python版本管理工具赴恨。pyenv–簡(jiǎn)單的Pyth...
    MrHamster閱讀 3,797評(píng)論 1 61
  • 此段內(nèi)容簡(jiǎn)要來自自強(qiáng)學(xué)堂的教程詳情請(qǐng)查詢自強(qiáng)學(xué)堂 一、 后臺(tái)的運(yùn)作流程 接收request請(qǐng)求 處理數(shù)據(jù) 獲取請(qǐng)求...
    coder_ben閱讀 5,246評(píng)論 6 56