Django -- 從平凡到超凡

第 3 章    建立新專案

(1) 建立新專案

∗ Blog 專案

▸ 本教材將開發一套名為 blog (部落格) 之 Web 系統

▸ 系統主要功能:管理者可以發表部落格文章,一般訪客可以瀏覽,註冊使用者可以留言並且按讚

▸ 接下來,繼續動手做吧!

∗ 安裝虛擬環境

▸ 在虛擬環境目錄建立名為 blogVenv 之虛擬環境

$ cd ~/webapps/virtualenv
$ virtualenv blogVenv

New python executable in <...>/webapps/virtualenv/blogVenv/bin/python3
Also creating executable in <...>/webapps/virtualenv/blogVenv/bin/python
Installing setuptools, pip, wheel...done.

以上指令:
  1. 前往 virtualenv 目錄
  2. 建立名為 blogVenv 的虛擬環境 (系統訊息回覆已安裝 python3python,因此執行 python 等同於執行 python3)
名稱格式
  1. 之後的教材將使用 <project> 代表專案的根目錄 (~/webapps/workspace/blog/~/webapps/git/blog/blog/)
  2. 本教材中各種名稱的格式如下:
    • 類別 (class):大寫 駝峰式,例如:NumItemsPerPage
    • 常數 (constant):全大寫底線式,例如::NUM_ITEMS_PER_PAGE
    • 其他:小寫駝峰式,例如:numItemsPerPage (--> 這與 Python 的建議 不同,但作者實在不喜歡看太多的底線 ;-)

▸ 接著啟用虛擬環境,並在虛擬環境中安裝 djangopsycopg2 (PostgreSQL Database adapter,資料庫適配器)

$ source ~/webapps/virtualenv/blogVenv/bin/activate
(blogVenv)$ which pip
/home/<username>/webapps/virtualenv/blogVenv/bin/pip
(blogVenv)$ pip install django psycopg2
Collecting django
...
(blogVenv)$ pip freeze
Django==x.x.x
pyscopg2==x.x.x
...

以上指令:

  1. source ...:啟用虛擬環境 (啟用之後的 pip 指令來自於虛擬環境,而非作業系統, 而且系統提示符號會加上虛擬環境名稱 blogVenv)
  2. which ...:顯示 pip 來自何處,確認虛擬環境安裝及執行正確 (which 是 Linux 指令, 用來顯示執行檔的位置)
  3. pip install ...:在虛擬環境中安裝 djangopsycopg2
  4. pip freeze:顯示虛擬環境中所安裝的套件及其版本 (x.x.x 為版本序號)
註:停用虛擬環境之指令為 (blogVenv)$ deactivate
CLI 快速鍵

∗ 在 Eclipse 設定 Python 解譯器 (Interpreter)

▸ 建立名為 blogPython 的 Python 解譯器,並設定為虛擬環境中的 Python 程式:

Window --> Preferences --> PyDev --> Interpreters --> Python Interpreter --> (右欄) New... --> Interpreter Name: blogPython, Interpreter Executable: /home/<username>/webapps/virtualenv/blogVenv/bin/python --> OK --> OK --> Apply and Close

∗ 在 Eclipse 建立新的 Django 專案:blog

▸ File --> New --> Project... --> PyDev --> PyDev Django Project --> Next --> Project Name: blog (並確認 Project Contents 的 Use default 已勾選,且其值為 /home/<username>/webapps/workspace/blog), Grammar Version: 3.0 – 3.5, Interpreter: blogPython --> Next --> Next --> Django version: 1.4 or later --> Finish

有關解譯器的設定

▸ 建立專案之後,可以在 Eclipse 的專案資源管理器 (Project Explorer,即左側欄) 看到 Django 專案的標準目錄結構如下:

blog/
  blog/
    __init__.py
    settings.py
    urls.py
    wsgi.py
  manage.py

✶ 第一層 blog:專案根目錄 (Project root)
✶ 第二層 blog:專案的直屬應用程式 (App),與專案名稱相同
# __init__.py:設定此目錄是一個 Python 套件 (Package)
# settings.py:專案的設定檔
# urls.py:專案的 URL request 格式設定檔
# wsgi.py:伺服器閘道介面 (Web server gateway interface), 是 Python 程式與伺服器溝通的介面程式
manage.py:Django 專案的管理程式
有關 Django 專案
  1. 一個 Python 專案的目錄架構如下:
    • Project:「專案」,最高層目錄
    • Package:「套件」,內含有 __init__.py 檔案的目錄, 套件的目的在於提供其他單元匯入 (Import) 相關資料
    • Module:「模組」,副檔名為 .py 的 Python 程式檔
  2. manage.py 檔案底下還有 blogPython 項目, 這是 Eclipse 顯示 本專案所使用的 Python 解譯器,並非專案所屬的檔案
  3. 如果您發現專案的目錄架構與上述不同,那麼一定是建立專案的程序有誤,常見的狀況是未設定 「Django verison: 1.4 or later」,請刪除本專案再重新建立
    • 刪除專案:Right click project (右鍵點專案) --> Delete --> 勾選 Delete project contents on disk (cannot be undone) --> OK
  4. 亦可利用 CLI 來建立 Django 專案,指令如下 :
    (blogVenv)$ cd ~/webapps/workspace
    (blogVenv)$ django-admin startproject blog
    • django-admin ...:以 Django 指令建立專案
    • 以 CLI 方式建立專案的問題:不會產生 Eclipse 所需要的 .project.pydevproject 兩個設定檔,因此 Eclipse 不認得此專案, 必須自行在專案目錄中建立這兩個檔案,並在 Eclipse 環境匯入專案 (見下方「Eclipse 的相關操作」說明)
  5. 如果 "TM Terminal" 正確安裝的話,在 Eclipse 中可點選左上方工具列的 終端機 圖示在 Eclipse 之中開啟 Terminal 視窗以便輸入 CLI 指令,如此就不需要在 Eclipse 之外開啟 Terminal 了 (畢竟我們的螢幕實在太擠了 ;-)

∗ 編輯專案設定檔並做以下修改:

blog/settings.py:

...
ALLOWED_HOSTS = ['*']

...

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'blogDB',
        'USER': 'blog',
        'PASSWORD': 'blog',
        'HOST': 'localhost',
        'PORT': '',
    }
}

...

LANGUAGE_CODE = 'zh-hant'

TIME_ZONE = 'Asia/Taipei'

...

ALLOWED_HOSTS:指定本應用程式可透過哪些網域來連結, ['*'] 指所有網域均可,如果本系統僅允許某些網域使用,應填入該域名以排除沒有權限者

DATABASES = ... 說明我們將如何建置資料庫,包括:

✶ 資料庫引擎
✶ 資料庫名稱、使用者、及密碼
✶ 連結資料庫的主機及埠號:localhost 即本機 (因為之後將在本機建立資料庫), 埠號的空字串值表示使用系統標準埠號 (通常是 5432,但有可能因為作業系統的不同設定而有所不同,因此給空字串的意思就是 ... 交給系統決定吧)

LANGUAGE_CODE = 'zh-hant':設定語言為中文繁體 (中文簡體為 zh-hans)

zh:中華 (ZhongHua)
hant:漢語繁體 (Traditional)
hans:漢語簡體 (Simplified)

TIME_ZONE = 'Asia/Taipei':設定時區為亞洲台北,其他的時區請看 這裡

命名法則
  1. 以上所規劃的資料庫的名稱、使用者、及密碼都刻意與專案名稱 blog 相同或相似,這是幫助記憶、簡化命名工作的好方法
    --> 電腦科學領域裡有兩件最困難的事,其中之一就是命名,絕不可輕忽 ;-)
  2. 未來您一定會會發覺,一個專案裡所包含的各類名稱 (目錄名、檔名、變數名、常數名、資料表名、欄位名 ...) 簡直多到滿天飛, 撰寫程式時如果都需要查來查去,那不僅會累斃了,更會錯誤百出。因此對於某些重要名稱,作者自行發展出一套命名法則, 程式設計師在撰寫程式時只要依循該法則,那麼該名稱就可以直接推算出來,使用名稱的複雜度及錯誤率都會大幅降低, 各種命名法則之後會陸續說明,但此處我們可以小結一下,目前我們已完成的命名如下:
    • 專案名稱:blog
    • 專案虛擬環境名稱:blogVenv
    • 虛擬環境 Python 解譯器名稱:blogPython
    • 資料庫名稱:blogDB
    • 資料庫使用者名稱:blog
    • 資料庫使用者密碼:blog

    看出規律了嗎?命名原則都是圍繞在專案名稱,這樣的規則讓各個專案都有一定的規律, 不僅開發者自己很清楚哪個名稱是屬於哪個專案,各個開發者之間也不必協調上述的名稱,就直接寫程式就對了, 名稱都是「預設」的,大家也都了然於胸,這是件好事!

  3. 一定有人懷疑:這麼簡單的資料庫密碼,過得了資安專家這一關嗎?可以的!說明如下:
    • 如果未來專案將部署到雲端 PaaS 供應商,那麼供應商會有自己的資料庫設定機制,以上設定都只在開發環境有效, 所以與生產環境的安全性無關 (有興趣的話可以先跳到第 11 章「部署專案」了解)
    • 如果未來專案將部署到自己架設的伺服器,那麼應該將安全機制重點放在作業系統及伺服器部份, 就好像家裡最好的鎖應該放在大門,而不是臥室門,因此,看管好作業系統及伺服器的安全才是最重要的工作。 如果駭客入侵了伺服器,此設定檔的內容也就曝光,因此,再複雜的密碼也沒有用的

(2) 建立資料庫

∗ 依據 settings.py 裡的設定來建立資料庫

▸ 在 Terminal 裡執行以下指令以建立資料庫與使用者,並設定權限:

$ sudo -i -u postgres
[sudo] password for <username>:
postgres@<username>$ createdb blogDB
postgres@<username>$ createuser -P blog
Enter password for new role: blog
Enter it again: blog
postgres@<username>$ psql
postgres=# grant all privileges on database "blogDB" to "blog";
GRANT
postgres=# \q
postgres@<username>$ exit

sudo -i ...:以 postgres 使用者登入系統 (需輸入管理者密碼,-i:登入, -u postgres 以 postgres 使用者的身份,postgres 是資料庫預設的使用者,登入後就有權限執行資料庫相關指令)
createdb ...:建立資料庫
createuser -P ...:建立使用者,-P: 同時也設定密碼 (注意 P 要大寫)
psql:進入 SQL 環境
grant all ...:賦予使用者所有資料庫權限 (系統回覆 GRANT 表示執行成功)
# psql 環境中,大寫字母會被轉為小寫,因此在 blogDB 加上雙引號以維持大寫
# psql 指令需以分號結束
\q:離開 SQL 環境
exit:離開 Postgres 環境
Postgres 常用指令

▸ 顯示 Postgres 版本

$ psql --version

▸ 啟動 Postgres

$ sudo /etc/init.d/postgresql start

▸ 停止 Postgres

$ sudo /etc/init.d/postgresql stop

▸ Postgres 可能 需要下列檔案的存取權限,設定其檔案權限為 700

$ sudo chmod 700 /var/lib/postgresql/9.3/main

▸ Postgres 的 log 檔 (x.x 為版本):

/var/log/postgresql/postgresql-x.x-main.log

▸ 檢查 Postgres 是否正在執行

$ ps -f | grep postgres

▸ 建立新的資料庫<database>

postgres@<username>$ createdb <database>

▸ 刪除資料庫<database>

postgres@<username>$ dropdb <database>

▸ 建立新的使用者 <user>

$ sudo -i -u postgres
[sudo] password for <username>:
postgres@<username>$ createuser -P <user>
Enter password for new role:
Enter it again:

▸ 刪除使用者 <user>

postgres@<username>$ dropuser <user>

▸ 進入 psql 環境

postgres@<username>$ psql

▸ psql 常用指令

✶ 設定使用者的資料庫操作權限
postgres=# grant all privileges on database "<database>" to "<user>";
✶ 更改使用者<user>的密碼
postgres=# \password <user>
✶ 列出所有資料庫
postgres=# \l
✶ 列出 <database> 裡的所有資料表
postgres=# \c <database>
<database>=# \dt
✶ 列出 <database> 裡的資料表 <table> 裡的所有資料
postgres=# \c <database>
<database>=# \l <table>
✶ 列出 <database> 裡的所有資料表裡的所有資料
postgres=# \c <database>
<database>=# \l
✶ 離開 psql
postgres=# \q

▸ 離開 postgres 環境

postgres@<username>$ exit

▸ 覺得 sudo -i -u postgres 指令輸入很麻煩?

--> 在 ~/.bashrc 檔案加入以下這行,以後簡單地打 pg 就可以囉!
alias pg='sudo -i -u postgres'

∗ 資料庫遷移 (Database migration)

▸ 資料庫建立之後,下一個程序就是建立資料表 (Data table) 與資料表裡的欄位 (Field)

▸ Django 建立資料表的程序:

✶ 設計資料模型:利用 Python 的類別 (Class) 來建立資料模型 (Model),包括資料模型名稱、欄位名稱、 及欄位資料型態等
✶ 資料庫遷移:依照所規劃的資料模型在資料庫中建立資料表

▸ Django 之資料庫遷移指令

makemigrations:依照資料模型產生 SQL 程式,用來建立資料表
migrate:執行上述的 SQL 程式在資料庫中建立資料表

▸ 目前 blog 專案尚無任何資料模型,但 Django 本身預設有許多資料模型 (例如: User model) 需要建立資料表,因此,在建立資料庫之後就要立刻進行資料庫遷移來產生 Django 預設的資料表

▸ 後續只要我們的程式有新增或修改資料模型,都需要立即執行資料庫遷移程序

▸ 在 Eclipse 中執行資料庫遷移

✶ 產生 SQL 程式 (Makemigrations):Right click project --> Django --> Custom Command --> Select the command to run or enter a new command: makemigrations --> OK
No changes detected
Finished "/home/<username>/webapps/workspace/blog/manage.py makemigrations" execution.
# 因尚未建立本專案的Model,因此出現 No changes detected (Django 預設的資料模型已經執行過 makemigrations,所以此時再次執行會得到 No changes detected)
# 第一次執行時需要輸入 makemigrations 指令,下次執行時該指令就會出現在選項中, 直接點選即可
✶ 利用 SQL 程式產生資料表 (Migrate):Right click project --> Django --> Migrate
  Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  ...
Finished "/home/<username>/webapps/workspace/blog/manage.py migrate" execution.
資料庫遷移之 CLI

亦可以 CLI 執行資料庫遷移,指令如下:

(blogVenv)$ cd <project>
(blogVenv)$ python manage.py makemigrations
(blogVenv)$ python manage.py migrate

(3) 測試

∗ 啟動伺服器

▸ Right click project --> Run As --> PyDev: Django

Console (控制台) 視窗會出下以下訊息:
Performing system checks...
System check identified no issues (0 silenced).
...
Django version ..., using settings 'blog.settings'
""Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
✶ 經過第一次啟動伺服器後,以後就可以可利用 Eclipse 的上方工具列 執行 來啟動伺服器:點選右方的向下箭頭,從選單中點選 1 blog blog 項目即可執行:
執行blog
啟動伺服器之 CLI

亦可以 CLI 啟動伺服器,指令如下:

(blogVenv)$ cd <project>
(blogVenv)$ python manage.py runserver

▸ 測試:開啟瀏覽器輸入本機網址 localhost:8000 (或 127.0.0.1:8000),得到以下結果,哇啦!又成功了!

django成功
--> Django 伺服器使用的標準埠號為 8000,如果要改換其他埠號 (例如 5000):
(blogVenv)$ python manage.py runserver 5000
Eclipse 的操作
  1. 隱藏專案:同時開發許多專案時,左側欄會出現許多專案的目錄及檔案,可以將目前不需要看到的專案隱藏起來
    Right click project --> Delete --> OK (注意:不要勾選 Delete contents on disk)
  2. 匯入 Eclipse 專案 (有 .project.pydevproject 檔案): 將隱藏的專案重新呈現或從其他目錄將專案匯入到目前工作區
    File --> Import --> General --> Existing Projects into Workspace --> Next --> 決定是否勾選 Copy projects into workspace, (Select root directory) Browse --> 選擇專案根目錄 --> OK --> Finish
  3. 匯入非 Eclipse 專案 (沒有 .project.pydevproject 檔案):從其他 Eclipse 專案複製 .project.pydevproject 兩個設定檔到專案根目錄,修改內容,然後匯入
    .project:修改專案名稱
    <?xml version="1.0" encoding="UTF-8"?>
    <projectDescription>
      <name>blog</name>
      <comment></comment>
      <projects>
      </projects>
      <buildSpec>
        <buildCommand>
          <name>org.python.pydev.PyDevBuilder</name>
          <arguments>
          </arguments>
        </buildCommand>
      </buildSpec>
      <natures>
        <nature>org.python.pydev.pythonNature</nature>
        <nature>org.python.pydev.django.djangoNature</nature>
      </natures>
    </projectDescription>
          
    .pydevproject:修改虛擬環境名稱
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <?eclipse-pydev version="1.0"?><pydev_project>
    <pydev_variables_property name="org.python.pydev.PROJECT_VARIABLE_SUBSTITUTION">
    <key>DJANGO_MANAGE_LOCATION</key>
    <value>manage.py</value>
    </pydev_variables_property>
    <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
    <path>/${PROJECT_DIR_NAME}</path>
    </pydev_pathproperty>
    <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.0</pydev_property>
    <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">blogPython</pydev_property>
    </pydev_project>
          
  4. 刪除專案:
    Right click project --> Delete --> (勾選 Delete contents on disk) OK
  5. Console 不見了:Window --> Show View --> Console
  6. 執行按鈕不見了:Window --> Perspective --> Customize Perspective --> (展開) Launch --> 勾選 Run --> OK
  7. Perspective 弄亂了:Window --> Perspective --> Reset Perspective --> Yes
  8. 若出現 Unable to bind localhost:8000 錯誤訊息,表示 Socket 被佔用 (可能是之前執行未正常結束),可將其刪除: $ fuser -k 8000/tcp

(4) Model-view-controller (MVC) 軟體架構模式

▸ Web 系統開發常用 Model-view-controller (MVC) 模式,將一個專案的程式及資料分成三部份:

✶ Model:資料庫模型
✶ View:網頁外觀的呈現,例如 HTML 及 CSS等
✶ Controller:流程控制與決策的程式 (即商業邏輯,Business logic)

▸ 未使用 MVC 模式範例:以下 PHP 程式 (綠色) 與 HTML 碼 (藍色) 互相穿插、混雜在一起, 維護困難且無法重複使用,這將會是程式設計師的夢魘

<?php
if ($_POST['formSubmit'] == "Submit") {
  $errorMessage = "";
  if (empty($_POST['formMovie'])) {
    $errorMessage .= "<li>您忘了輸入電影名稱!</li>";
  }
  if (empty($_POST['formName'])) {
    $errorMessage .= "<li>您忘了輸入名字!</li>";
  }
  $varMovie = $_POST['formMovie'];
  $varName = $_POST['formName'];
  if (empty($errorMessage)) {
    $fs = fopen("mydata.csv","a");
    fwrite($fs,$varName . ", " . $varMovie . "\n");
    fclose($fs);
    header("Location: thankyou.html");
    exit;
  }
}
?>

<!doctype html>
<html>
<head>
<title>My Form</title>
</head>
<body>

<?php
if (!empty($errorMessage)) {
  echo("<p>您的表單有問題:</p>\n");
  echo("<ul>" . $errorMessage . "</ul>\n");
}
?>

<form action="myform1.php" method="post">
  <p>您最喜愛的電影?<br>
    <input type="text" name="formMovie" maxlength="50" value="<?=$varMovie;?>">
  </p>
  <p>您的名字?<br>
    <input type="text" name="formName" maxlength="50" value="<?=$varName;?>">
  </p>
  <input type="submit" name="formSubmit" value="Submit">
</form>
</body>
</html>

▸ MVC 將資料、網頁、與程式分開,有以下優點:

✶ 程式可以重複使用
✶ 後續維護方便
✶ 未來功能擴充較為單純
✶ 減少系統三個部份的相互干擾,人員可各司其職,工作切割合理

▸ Django 亦使用 MVC 模式,但稱為 Model-template-view (MTV)

✶ MTV 的 model 即為 MVC 的 model
✶ MTV 的 template 即為 MVC 的 view
✶ MTV 的 view 即為 MVC 的 controller

(5) Django 的運作程序

django流程

  1. 使用者瀏覽器發出請求 (Request)
  2. 網站伺服器 (Web server) 接收到請求,並將請求轉給 WSGI 伺服器 (WSGI server)
  3. 送至中介軟體 (Middleware) 並依據所規劃的 URL 組態 (URLconf) 進行網址對應 (URL mapping):確定網址格式正確,並決定由哪一個程式負責執行
  4. 將請求送至該應用程式 (View) 執行
  5. View 程式處理該請求,如果有需要的話:
    1. 存取資料庫 (Database)
    2. 處理範本 (Template)
    3. 處理例外 (Exception)
  6. 將處理結果回覆給網站伺服器
  7. 最後網站伺服器將結果 (Response) 回覆瀏覽器

(6) 專案的組成要件

完成工作

▸ 一個專案的組成要件包括專案本身、作業系統、虛擬環境、整合式開發環境、及資料庫

▸ 我們目前已完成下列項目的安裝與設定:

✶ 作業系統:這當然早已安裝完成,否則我們根本無從開始 ;-)
✶ 虛擬環境:安裝建立虛擬環境所需要的套件、建立虛擬環境、在虛擬環境中安裝套件、在 Eclipse 中設定解譯器
✶ 整合式開發環境:Eclipse、PyDev、TM Terminal、Web Developer Tools、IDE 設定
✶ 資料庫:安裝資料庫套件、建立資料庫與使用者、資料庫遷移

▸ 如果有多位開發者協同開發專案,則以上五個要件中除了專案本身是分享的 (參見下一章:版本控制), 其餘都是各個開發者本機端的設定,並不分享,因此需要各自安裝及設定

▸ 接下來,我們就要專注在專案的開發,包括撰寫 Python/Django 程式、HTML、與 CSS 碼,休息一下再上路囉!

(7) 小結

∗ 建立一個新專案的程序

  1. 建立虛擬環境:virtualenv ...
  2. 啟用虛擬環境:source ...
  3. 安裝套件:pip install ...
  4. 在 Eclipse 中設定 Pyhton Interperter
  5. 在 Eclipse 中建立新 Django 專案
  6. 在專案的 settings.py 檔案進行相關設定
  7. 建立資料庫:sudo -i -u postgres --> createdb ...
  8. 建立資料庫使用者:createuser -P ...
  9. 賦予使用者權限:psql --> grant all privileges ...
  10. 資料庫遷移:makemigrations, migrate
  11. 啟動伺服器
  12. 瀏覽器測試

上一章       下一章