🚀 Day 10:整合優化與專案發布展演(Integration, Deployment & Demo Day)
5 天營隊第二週大結局 (Day 10 / 總時數 6 Hours) | 視覺化數據圓餅圖、雲端 Server 部署上線與專屬網址發布會!
📖 為什麼學 these?——「理財咖啡館完工開幕!衝向雲端伺服器」關卡冒險故事線
過去 9 天我們從終端機控制台開始,一路建立了資料庫與動態互動網頁。今天是我們的 **畢業大登頂時刻**!我們要加入 **Chart.js 圓餅圖** 進行數據視覺化,打包相依套件並鎖上安全開關,最後將專案上傳到雲端主機平台,拿到專屬網址(如 xxx.pythonanywhere.com),立馬傳給班級群組與父母看你的心血結晶!
| 單元名稱 | 記帳程式關卡角色 | 為什麼這個單元不可或缺?(技術關聯) |
|---|---|---|
| 單元 1:前端進階:資料視覺化 | 📊 理財數據圓餅圖 (Chart.js) | 將 Django View 算出的分類統計數字丟給前端 JavaScript,自動渲染成漂亮的炫酷圓餅圖與柱狀圖! |
| 單元 2:上線前置準備 | 🧳 打包行李箱與安全鎖 (.env & DEBUG) | 匯出 requirements.txt 套件清單,將 DEBUG = False 關閉除錯保護商業機密,設定 ALLOWED_HOSTS。 |
| 單元 3:雲端部署平台介紹 | ☁️ 租用 24hr 不打烊雲端主機 (PaaS) | 認識 PythonAnywhere / Render 雲端平台,擺脫 127.0.0.1 本地端限制,邁向網際網路全世界。 |
| 單元 4:實戰部署 (Deployment) | 🚚 專案搬家上雲與 Cloud Migrate | 將程式碼上傳至雲端主機,在雲端伺服器執行 python manage.py migrate 建立正式資料庫。 |
| 單元 5:靜態檔案與除錯 | 🎨 雲端外觀收集與極限除錯 (collectstatic) | 執行 collectstatic 解決雲端 CSS/JS 遺失問題,進行上線前最後的 Live 除錯與壓力測試。 |
| 單元 6:🎓 Demo Day 成果發表會 | 🏆 理財全棧工程師雲端發布會 | 秀出專屬網址、現場操作網頁記帳,進行全班作品互評與體驗,頒發 30 小時 Python 全棧營隊結業證書! |
📌 課程前導與簡介說明
- 營隊定位:衝刺部署上線與成果展演。這是 30 小時密集營隊的巔峰與高潮。
- 高中生心理亮點:當學生拿到真正的公網 URL(如
xxx.pythonanywhere.com),用手機打開並真的可以在上面記帳時,這種成就感會深植人心一輩子! - 教學銜接點:強調真實軟體發布上線 (Deployment) 流程;靜態檔案 `collectstatic` 與 `DEBUG = False` 是 Deploy 時最常卡關之處,需細心引導。
歡迎來到最後一天大結局!今天你要把網站真正部署到雲端網際網路上!請閱讀每個單元的 **故事單元說明**,點擊 **延伸閱讀** 展開深層觀念與 Google 搜尋,遇到部署除錯問題時善用 **AI 協作除錯提示詞**!
💻 Day 10 專案總成果:Chart.js 圖表整合與雲端部署設定
【教師參考解答】學生於 Day 10 結束時將完成的圖表 View、HTML Canvas 與部署設定:
1. 圖表 View 資料準備 (`expenses/views.py`):
import json
from django.shortcuts import render
from .models import Expense
from django.db.models import Sum
def home(request):
records = Expense.objects.all().order_by('-date')
total_cost = records.aggregate(Sum('price'))['price__sum'] or 0
# 聚合各分類消費小計 (製作 Chart.js 數據)
category_data = (
Expense.objects.values('category')
.annotate(total=Sum('price'))
.order_by('-total')
)
chart_labels = [c['category'] for c in category_data]
chart_values = [c['total'] for c in category_data]
context = {
'records': records,
'total_cost': total_cost,
'chart_labels': json.dumps(chart_labels, ensure_ascii=False), # 序列化為 JSON 字串給前端
'chart_values': json.dumps(chart_values),
}
return render(request, 'expenses/home.html', context)
2. HTML Canvas 圖表渲染 (`templates/expenses/home.html`):
<!-- 引入 Chart.js CDN -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<!-- 圖表展示卡片 -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-header bg-white fw-bold py-3">📊 消費分類比例圓餅圖</div>
<div class="card-body" style="max-height: 300px; position: relative;">
<canvas id="categoryChart"></canvas>
</div>
</div>
<script>
const ctx = document.getElementById('categoryChart').getContext('2d');
const labels = {{ chart_labels|safe }};
const dataValues = {{ chart_values|safe }};
new Chart(ctx, {
type: 'pie', // 圓餅圖
data: {
labels: labels,
datasets: [{
data: dataValues,
backgroundColor: ['#0284c7', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6']
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
</script>
3. 雲端上線設定檔 (`myproject/settings.py`):
# ⚠️ 上線前安全設定
DEBUG = False # 關閉黃底詳細報錯頁面
# 允許存取的域名 (加入你的 PythonAnywhere 網址)
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '.pythonanywhere.com']
# 靜態檔案收集目錄
STATIC_ROOT = BASE_DIR / 'staticfiles'
【學生自主實作框架】請根據今日各單元所學,將空缺的 `____` 填入正確的 Chart.js 與部署關鍵字:
<!-- 🎯 Day 10 自主挑戰:請填入 HTML 與 settings.py 空缺的 ____ 關鍵字! -->
<!-- --- [檔案: templates/expenses/home.html] --- -->
<!-- 1. 繪製圖表的 HTML5 畫布標籤 -->
<____ id="categoryChart"></____> # 提示:HTML5 圖表畫布標籤 canvas
<script>
// 2. 避免 DTL 轉義 JSON 字串的過濾器
const labels = {{ chart_labels|____ }}; # 提示:讓字串不被自動轉義的 filter: safe
const dataValues = {{ chart_values|safe }};
new Chart(ctx, {
type: '____', # 提示:圓餅圖型態 pie
data: { labels: labels, datasets: [{ data: dataValues }] }
});
</script>
# --- [檔案: myproject/settings.py] ---
# 3. 關閉除錯保護機密
DEBUG = ____ # 提示:上線時需設定為 False
# 4. 允許連線的網域
ALLOWED_HOSTS = ['*']
⏰ 單元 1:前端進階:資料視覺化 (Chart.js)(09:00 - 10:00)
單看表格數字太枯燥了!我們要引入著名的 JavaScript 第三方圖表庫 **Chart.js**。我們在 Django View 端使用 ORM 的 .values('category').annotate(total=Sum('price')) 將各分類花費小計算好,序列化成 JSON 傳給前端,並在 HTML 用 {{ chart_labels|safe }} 渲染成炫酷的圓餅圖!
後端數據統計與前端圖表渲染的溝通橋樑:
- ORM
values().annotate():相當於 SQL 中的GROUP BY category,高效率計算各分類總額。 - DTL
|safe過濾器:預防 Django 自動轉義將 JSON 中的雙引號轉為 `"` 導致 JS 解析失敗。
🔗 Google 搜尋關鍵字 (依相關度排序,紫羅蘭標記為進階內容,點擊開啟新分頁):
🤖 AI 自主學習探索提示詞 (Prompt):
請說明如何使用 Django ORM 的 annotate(Sum('price')) 進行分類聚合統計,並示範如何將資料轉換為 JSON 傳給前端 Chart.js 繪製圓餅圖。
- 在 View 使用
values('category').annotate(total=Sum('price'))算統計。 - 在 HTML 引入 Chart.js CDN 並放置
<canvas id="categoryChart">。 - 強調 DTL 管道符
{{ chart_labels|safe }}的作用。
<!-- HTML5 畫布標籤 -->
<canvas id="categoryChart" width="400" height="200"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('categoryChart').getContext('2d');
new Chart(ctx, {
type: 'pie',
data: {
labels: {{ chart_labels|safe }},
datasets: [{ data: {{ chart_values|safe }} }]
}
});
</script>
- JS 語法錯誤 (Uncaught SyntaxError):在 HTML 中忘記加 `|safe`,導致傳過去的 JSON 被自動轉義為 `["餐飲"]` 造成 JS 當機。
我在用 Chart.js 畫圖時主控台跳出了 Uncaught SyntaxError: Unexpected token '&' 錯誤。請提醒我如何在 DTL 變數加上 |safe 過濾器。
⏰ 單元 2:上線前置準備 (requirements.txt & .env)(10:00 - 11:00)
要把網站上傳給雲端主機執行前,我們必須幫專案打包「行李箱」!執行 pip freeze > requirements.txt 生成套件清單,讓雲端主機知道需要安裝哪些庫。並且在 settings.py 將 DEBUG = False 鎖上黃底報錯頁面,預防駭客看光光我們的底層檔案路徑!對應 Day 5 的專案打包!
開發環境 (Development) 與正式上線環境 (Production) 的設定安全:
DEBUG = False:隱藏詳細 Traceback 與資料庫查詢資訊,防止機密外洩。關閉後必須指定ALLOWED_HOSTS。requirements.txt:記錄所有依靠套件(如 `Django==4.2`),雲端伺服器憑此自動還原環境。
🔗 Google 搜尋關鍵字 (依相關度排序,紫羅蘭標記為進階內容,點擊開啟新分頁):
🤖 AI 自主學習探索提示詞 (Prompt):
請說明在將 Django 部署上線前,為什麼必須執行 pip freeze > requirements.txt,以及將 DEBUG 設定為 False 後為什麼必須同時設定 ALLOWED_HOSTS。
- 執行
pip freeze > requirements.txt。 - 在
settings.py修改DEBUG = False與ALLOWED_HOSTS = ['*']。 - 概念連結:對應 Day 5 專案打包與部署清單準備。
# 1. 匯出套件相依清單
pip freeze > requirements.txt
# 2. 修改 myproject/settings.py
DEBUG = False
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '.pythonanywhere.com', '.onrender.com']
- CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False:將 DEBUG 改為 False 後,如果忘記設定 `ALLOWED_HOSTS`,伺服器會拒絕啟動!
我在將 DEBUG 設定為 False 後跳出了 CommandError: You must set settings.ALLOWED_HOSTS。請教我如何在 settings.py 設定 ALLOWED_HOSTS = ['*']。
⏰ 單元 3:雲端部署平台介紹 (PaaS)(11:00 - 12:00)
如果我們的網站只放在本機,電腦關機網站就死掉了。我們需要將專案託管到 **PaaS (平台即服務)** 雲端主機平台(如 PythonAnywhere 或 Render)!雲端主機就是一部 24 小時不斷電的 Linux 電腦,能給我們一串真正的專屬網址,讓全世界隨時連線!
了解現代雲端架構分工:
- PaaS (Platform as a Service):如 PythonAnywhere、Render,雲端廠商幫你維護好作業系統與 Python 環境,你只需上傳代碼。
- WSGI 網關:雲端伺服器 (Web Server) 透過 WSGI 協定(如 Gunicorn / uWSGI)呼叫你的 Django 應用。
🔗 Google 搜尋關鍵字 (依相關度排序,紫羅蘭標記為進階內容,點擊開啟新分頁):
🤖 AI 自主學習探索提示詞 (Prompt):
請向初學者說明什麼是 PaaS 雲端平台 (如 PythonAnywhere / Render),以及為什麼將 Django 專案部署到雲端主機後就能獲得公網網址。
- 帶學生註冊 PythonAnywhere 或 Render 免費帳號。
- 講解網址概念:將
127.0.0.1轉換為公網域名username.pythonanywhere.com。 - 概念連結:突破 localhost 本地端限制,邁向網際網路。
# 雲端部署前準備清單:
[✓] 1. GitHub 帳號 / 代碼倉庫已建立 (或 zip 打包準備好)
[✓] 2. PythonAnywhere / Render 免費帳號已註冊完成
[✓] 3. 專案根目錄包含成功的 requirements.txt 檔案
[✓] 4. settings.py 已修改 DEBUG = False 與 ALLOWED_HOSTS
- 雲端 Python 版本不符:本機用 Python 3.11 雲端預設用 3.8 $\rightarrow$ 在雲端平台設定檔中選取與本機相近的 Python 版本。
我在雲端平台建立 Web App 時該如何選擇 Python 版本?請教我如何檢查本機與雲端的 Python 版本相容性。
⏰ 單元 4:實戰部署 (Deployment)(13:00 - 14:00)
我們將程式碼上傳到雲端伺服器(透過 Git/GitHub 或 Zip 上傳)。接著在雲端主機的 Bash Console 裡面,建立虛擬環境、執行 pip install -r requirements.txt 安裝套件,並執行 python manage.py migrate!在雲端伺服器實體建造屬於雲端環境的 SQLite 資料庫!
標準的軟體部署 (Deployment) 流程:
- Git Clone 拉取代碼:透過
git clone https://github.com/...將代碼無縫複製至雲端伺服器。 - 雲端資料庫 Migrate:正式環境必須在雲端機器獨立執行
migrate建立 SQLite 或雲端 PostgreSQL 資料庫。
🔗 Google 搜尋關鍵字 (依相關度排序,紫羅蘭標記為進階內容,點擊開啟新分頁):
🤖 AI 自主學習探索提示詞 (Prompt):
請說明將 Django 專案部署上雲端時,在雲端主機的 Console 裡面執行 pip install -r requirements.txt 與 python manage.py migrate 的作用。
- 輔導學生將代碼上傳/Cloned 至雲端主機。
- 在雲端 Console 建立虛擬環境與執行
pip install -r requirements.txt。 - 執行
python manage.py migrate建立雲端資料庫。 - 概念連結:對應 Day 5 軟體工程發布與出版流程。
# 在雲端伺服器 (如 PythonAnywhere Bash) 內部執行:
# 1. 下載套件相依
pip install -r requirements.txt
# 2. 執行資料庫遷移
python manage.py migrate
# 3. 建立雲端超級管理員
python manage.py createsuperuser
- 500 Internal Server Error:雲端忘記執行 `migrate` 導致資料庫無表格,或是 WSGI 檔案路徑設定錯誤。
我的 Django 網站部署到雲端後出現 500 Internal Server Error。請教我如何查看雲端主機的 server.log 日誌找出出錯原因。
⏰ 單元 5:靜態檔案與除錯 (collectstatic)(14:00 - 15:00)
當我們開啟雲端網址時,突然發現「網站怎麼變回醜醜的純文字,Bootstrap CSS 完全失效了?」。因為當 DEBUG = False 時,Django 為了效能不會自動處理 CSS/JS!我們必須在 settings.py 設定 STATIC_ROOT,並執行 python manage.py collectstatic 把所有靜態裝潢打包在一起交給雲端 Web Server 處理!
靜態檔案 (Static Files) 在正式環境的高效管理:
collectstatic指令:將分散在各 App 目錄中的 CSS, JS, Image 圖檔統一複製收集至STATIC_ROOT指定資料夾。- WhiteNoise 套件:讓 Django 在 Production 模式下也能直接高效託管靜態檔案的熱門套件。
🔗 Google 搜尋關鍵字 (依相關度排序,紫羅蘭標記為進階內容,點擊開啟新分頁):
🤖 AI 自主學習探索提示詞 (Prompt):
請說明當 DEBUG = False 時,為什麼 Django 網頁上的 CSS/JS 樣式會失效,以及如何設定 STATIC_ROOT 並執行 collectstatic 指令解決問題。
- 在
settings.py加入STATIC_ROOT = BASE_DIR / 'staticfiles'。 - 在雲端 Console 執行
python manage.py collectstatic。 - 在 PythonAnywhere 的 Static Files 設定頁配對 `/static/` 路徑。
- 概念連結:極限壓力測試與 Live 除錯能力。
# 1. settings.py 設定靜態根目錄
STATIC_ROOT = BASE_DIR / 'staticfiles'
# 2. 雲端 Console 執行收集指令
python manage.py collectstatic --noinput
- 網頁排版變純文字無樣式:雲端平台的 Web 頁面沒有手動在 Static files 表格加入 `/static/` 對應至 `staticfiles` 目錄。
我的 Django 網站上線後 CSS 樣式都不見了。請教我如何在 PythonAnywhere 的 Static Files 設定欄位填寫 URL (/static/) 與 Directory 路徑。
⏰ 單元 6:🎓 Demo Day 成果發表會(15:00 - 16:00)
恭喜所有人登頂大圓滿!從一開始連 Python 都沒碰過的初學者,到現在擁有自己的雲端網址(如 xxx.pythonanywhere.com)!請上台秀出你的專屬網址、現場用手機連線記帳、向大家展示你的 **Chart.js 圓餅圖** 與 **Bootstrap 5 質感介面**!互相體驗同儕的作品,領取理財全棧工程師結業證書!
- 引導每位學生貼出個人專屬雲端 URL。
- 邀請學生輪流上台 Live 操作展示(展示圖表、新增、刪除、搜尋)。
- 頒發 30 小時 Python & Django 理財全棧營隊結業證書,拍照留念!
- 概念連結:敏捷開發 Sprint Demo 與工程團隊分享文化。
# 🎓 30 小時全棧營隊終極畢業檢查點:
[✓] 1. 成功取得個人公網網址 (如 xxx.pythonanywhere.com)。
[✓] 2. 網站能繪製 Chart.js 分類比例圓餅圖。
[✓] 3. 網頁表單能在雲端資料庫新增與刪除消費紀錄。
[✓] 4. 成功解鎖跨越 CLI 到 Web 全棧開發者成就!
- 現場展示突發連線問題:冷靜檢查雲端主機 Status 是否為 Active,或是 Console 重新 reload 網頁伺服器。
請扮演一位充滿熱情的資深軟體架構師,針對一位成功完成 30 小時課程並將 Django 記帳網站部署至雲端的學生,寫一段 100 字的畢業賀詞,肯定他從 CLI 到 Web 的學習毅力與成果。