2026年10月16日 星期五

用 Django 打造即時泡泡牆:從網際網路理論到多媒體實作

網際網路 × 多媒體應用 × Django 實作

用 Django 打造即時泡泡牆
從網際網路理論到多媒體實作

一個泡泡牆看起來像是把文字變成彩色圓泡泡,背後其實串起了瀏覽器、HTTP、JSON、伺服器、資料庫與即時推播。本文以 Django 製作一套可在教室展示的泡泡牆,帶大一新生透過完整作品認識網際網路與多媒體應用的基礎。






一、設計動機

大一新生初次接觸網際網路與多媒體應用時,常會分別學到網頁、伺服器、資料格式、資料庫和視覺設計等名詞,卻不容易看見它們如何合作。泡泡牆把這些概念放進一個能親手操作、能立即看見結果的情境:學生輸入一句想法,按下送出,教室投影畫面就多出一顆彩色泡泡。

因此,這個作品的動機是讓大一新生瞭解網際網路以及多媒體應用的理論基礎。學生可以沿著一則資料的旅程,觀察瀏覽器如何發出請求、Django 如何驗證與處理資料、資料如何存進資料庫,以及伺服器如何把新內容即時送到投影畫面。

二、教學目的

  • 認識用戶端與伺服器的分工,理解瀏覽器如何透過 HTTP 與網站溝通。
  • 分辨 GET 與 POST 的用途,讀懂 200、201、400、401 等狀態碼。
  • 理解 JSON 如何承載文字、顏色、隊伍等結構化資料。
  • 了解 Django URL 路由、View、Model 與 SQLite 資料庫的關係。
  • 體驗 Server-Sent Events(SSE)如何將新泡泡由伺服器推送到投影瀏覽器。
  • 連結多媒體的文字、色彩、動態效果與全螢幕展示,思考科技如何支持課堂互動。
學習成果:完成後,學生不只會看到泡泡牆的畫面,也能說明一顆泡泡如何從投稿表單出發,經過網路傳輸、伺服器處理與資料儲存,最後出現在投影幕上。

三、系統如何運作

學生瀏覽器HTTP POSTDjango API寫入SQLite
投影瀏覽器GET /api/state載入既有泡泡
Django SSE推送新事件投影瀏覽器新增泡泡

學生端將文字、顏色、隊伍名稱組成 JSON,並在 HTTP 標頭附上 Bearer token。伺服器檢查 token 和欄位後,建立資料記錄,回傳 201 Created。同時,SSE 連線會收到新泡泡事件,讓投影畫面無須重新整理就能更新。

介面方法與路徑用途成功/錯誤回應
載入泡泡GET /api/state取得既有泡泡200 OK + JSON
新增泡泡POST /api/bubbles驗證並儲存投稿201 Created;格式錯誤 400;token 無效 401
即時事件GET /api/events維持 SSE 連線、接收新泡泡text/event-stream

四、重要程式解析

1. Model:定義泡泡資料

Django Model 描述一筆泡泡要保存哪些欄位。執行資料庫遷移後,Django 會依此建立資料表。

from django.db import models

class Bubble(models.Model):
    text = models.CharField(max_length=160)
    color = models.CharField(max_length=7)
    team = models.CharField(max_length=40)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)

    class Meta:
        ordering = ['-created_at']

    def as_event(self):
        return {
            'id': self.pk,
            'text': self.text,
            'color': self.color,
            'team': self.team,
            'created_at': self.created_at.isoformat(),
        }

CharField 用來保存文字欄位,auto_now_add=True 會在建立記錄時自動記下時間。as_event() 將 Model 物件轉成適合 JSON 傳輸的 Python 字典。

2. URL 路由:把網址交給對應功能

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
    path('projector/', views.projector, name='projector'),
    path('api/state', views.state, name='state'),
    path('api/bubbles', views.create_bubble, name='create_bubble'),
    path('api/events', views.events, name='events'),
]

路由可以想成網站的分機表:收到不同路徑,Django 就呼叫不同的 View。一般頁面負責回傳 HTML;以 /api/ 開頭的路徑主要交換資料。

3. View:驗證資料、存入資料庫並回傳狀態碼

以下節錄新增泡泡的關鍵邏輯。實際專案也會檢查 JSON 格式、欄位長度與色碼格式。

@require_POST
def create_bubble(request):
    token = request.headers.get('Authorization', '')
    token = token.removeprefix('Bearer ').strip()

    if not token or token not in settings.BUBBLE_TOKENS:
        return JsonResponse(
            {'error': 'unauthorized'}, status=401
        )

    data = json.loads(request.body)
    text = data.get('text', '').strip()
    color = data.get('color', '')
    team = data.get('team', '').strip()

    # 正式程式在此檢查欄位內容與格式
    bubble = Bubble.objects.create(
        text=text, color=color.upper(), team=team
    )
    return JsonResponse(bubble.as_event(), status=201)

Authorization: Bearer ... 是 HTTP 標頭中的通行憑證。驗證失敗回傳 401;資料欄位無效回傳 400;建立成功回傳 201。這些狀態碼是瀏覽器或其他用戶端判斷結果的依據。

安全提醒:教學原型使用環境變數設定 token;正式活動應將預設示範 token 換掉,避免把 token 寫在公開程式碼中。登入表單送出的 token 會由瀏覽器傳送到網站,因此公開部署須使用 HTTPS。

4. SSE:讓投影畫面收到新泡泡

SSE 是由伺服器單向推送資料到瀏覽器的機制。它適合「學生送出、投影端即時顯示」這種更新方向單純的情境。

def events(request):
    q = Queue()
    with _subscribers_lock:
        _subscribers.add(q)

    def stream():
        try:
            yield 'retry: 3000\n\n'
            while True:
                try:
                    payload = q.get(timeout=15)
                    yield f'event: bubble\ndata: {payload}\n\n'
                except Empty:
                    yield ': keep-alive\n\n'
        finally:
            with _subscribers_lock:
                _subscribers.discard(q)

    response = StreamingHttpResponse(
        stream(), content_type='text/event-stream'
    )
    response['Cache-Control'] = 'no-cache'
    response['X-Accel-Buffering'] = 'no'
    return response

投影瀏覽器先呼叫 /api/state 載入舊泡泡,再使用 EventSource 訂閱新事件:

fetch('/api/state')
  .then(response => response.json())
  .then(data => data.bubbles.forEach(addBubble));

const stream = new EventSource('/api/events');
stream.addEventListener('bubble', event => {
  const bubble = JSON.parse(event.data);
  addBubble(bubble);
});

收到資料後,JavaScript 建立泡泡元素、套用顏色,並將文字放入頁面。CSS 負責圓形外觀、陰影、浮動動畫和手機版排版,構成多媒體呈現的一部分。

五、執行步驟

步驟 1:準備環境

先安裝 Python 3.10 以上版本,將 bubblewall-django.zip 解壓縮到一個工作資料夾。開啟 Windows 命令提示字元(CMD),使用 cd 切換到含有 manage.py 的專案目錄。

cd C:\\Users\\學生\\Desktop\\bubblewall-django

步驟 2:建立虛擬環境並安裝 Django

python -m venv .venv
.venv\\Scripts\\activate
pip install -r requirements.txt

看到命令提示字元前面出現 (.venv),表示虛擬環境已啟用。

步驟 3:設定活動 token

原型預設 token 是 demo-token,直接啟動也能測試。若要指定活動用 token,可在同一個 CMD 視窗執行:

set BUBBLE_TOKENS=class-a-2026

此 token 會用來驗證學生的投稿請求。CMD 視窗關閉後,這項設定會消失;正式部署需改用適當的環境變數管理方式。

步驟 4:建立資料表並啟動伺服器

python manage.py migrate
python manage.py runserver

看到 Django 的開發伺服器已啟動後,在同一個 Wi-Fi 網路中,可先用本機網址測試 http://127.0.0.1:8000/。開發伺服器預設只供本機使用;若要讓教室其他設備連線,需設定主機位址、防火牆及允許的網域。

步驟 5:開啟投稿頁與投影頁

  • 學生投稿頁:http://127.0.0.1:8000/
  • 投影展示頁:http://127.0.0.1:8000/projector/

用兩個瀏覽器視窗開啟這兩個頁面。在投稿頁輸入想法、隊伍、選擇顏色和 token,送出後觀察投影頁是否即時出現泡泡。

步驟 6:用 curl 測試 API

開啟另一個 CMD 視窗,以 curl 直接送出 JSON。若將 token 設定為示範值,命令如下:

curl.exe -X POST http://127.0.0.1:8000/api/bubbles ^
  -H "Authorization: Bearer demo-token" ^
  -H "Content-Type: application/json" ^
  -d "{\"text\":\"讓想法一起發光\",\"color\":\"#61D7C5\",\"team\":\"海洋隊\"}"

成功時會回傳泡泡資料及 HTTP 201。把 token 改錯可觀察 401;把色碼改成不符合格式的內容,則可觀察 400。

六、課堂討論與延伸

  1. 如果投稿資料只有文字、沒有顏色,JSON 欄位與表單要如何調整?
  2. 為什麼投影端要先載入既有泡泡,再訂閱 SSE?如果順序相反,可能發生什麼情況?
  3. GET 和 POST 在這個系統中各自負責什麼?為什麼新增資料不使用 GET?
  4. 除了隊伍名稱與文字,還可以加入圖片、表情符號或音效嗎?前端需要做哪些變更?
  5. 目前資料庫記錄會保留。若每次活動要建立獨立泡泡牆,可加入活動代碼或活動模型來分流資料。

七、原型限制與下一步

目前版本使用 SQLite,適合課堂示範和小型單機活動。SSE 訂閱者清單保存在單一 Django 程序的記憶體中,因此多程序、多台主機部署時,事件不會自動跨程序同步;正式擴充時可改用 Redis Pub/Sub 或 Django Channels。正式上線也應使用 HTTPS、妥善管理 token,並搭配正式的 Django 部署伺服器。

這個作品的價值,在於把抽象網路概念變成可觀察的資料流程。學生每送出一顆泡泡,就能看見用戶端、伺服器、資料庫與多媒體畫面如何協同工作,逐步建立對網際網路與互動應用的整體理解。

沒有留言:

張貼留言