跳至主要内容

7 篇文章 含有標籤「Django」

檢視所有標籤

Django、Flask、FastAPI 吞吐量比較入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在 Python Web 開發領域,Django、Flask 與 FastAPI 是三個非常熱門且廣泛使用的框架。它們各有特點與適用場景,但在性能表現,特別是吞吐量(throughput)方面,存在一定差異。吞吐量通常指每秒鐘能處理的請求數量,是衡量 Web 框架在高併發環境中效率的重要指標。

本篇筆記將簡要介紹這三個框架的基本架構與設計理念,並透過簡單測試與範例比較其吞吐量,幫助初學者理解如何依需求選擇適合的框架。


重點摘要

  • Django

    • 全功能、重量級框架,內建 ORM、管理後台、驗證系統等
    • 同步同步處理,基於 WSGI,預設不支援非同步(Asynchronous)請求
    • 適合需要完整解決方案的中大型專案
    • 吞吐量相較較低,因同步阻塞限制高併發能力
  • Flask

    • 輕量級框架,核心簡單,擴展彈性高
    • 同樣基於同步 WSGI,預設不支援非同步
    • 適合小型、原型開發及彈性需求較多的專案
    • 吞吐量與 Django 相近,瓶頸多來自同步阻塞與部署環境
  • FastAPI

    • 新興的輕量且高性能框架,採用 ASGI 標準,內建非同步支援
    • 基於 Starlette 與 Pydantic,支援非同步 I/O,大幅提升吞吐量
    • 適合高併發、API 開發需求強烈的專案
    • 吞吐量明顯優於 Django、Flask,適合現代微服務架構
  • 吞吐量測試環境與結果

    • 使用同一台機器與相同測試工具(如 wrkabLocust)對簡單 API 進行測試
    • Django、Flask 在同步阻塞環境吞吐量約數千至上萬請求/秒
    • FastAPI 在非同步環境可突破十萬請求/秒,具備更好擴展性
  • 部署差異

    • Django、Flask 多搭配 Gunicorn(WSGI)同步服務器部署
    • FastAPI 搭配 Uvicorn、Hypercorn(ASGI)非同步服務器,性能最佳化

吞吐量比較實際範例

以下將以簡單「Hello World」API 為例,展示三個框架的基本實作,並提供吞吐量測試方法參考。

1. Django 範例

程式碼(views.py):

from django.http import JsonResponse

def hello(request):
return JsonResponse({"message": "Hello World"})

URL 設定(urls.py):

from django.urls import path
from .views import hello

urlpatterns = [
path('hello/', hello),
]

啟動方式:

python manage.py runserver

(正式部署可用 Gunicorn)

2. Flask 範例

程式碼(app.py):

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/hello')
def hello():
return jsonify(message="Hello World")

if __name__ == "__main__":
app.run()

啟動方式:

python app.py

(正式部署可用 Gunicorn)

3. FastAPI 範例

程式碼(main.py):

from fastapi import FastAPI

app = FastAPI()

@app.get("/hello")
async def hello():
return {"message": "Hello World"}

啟動方式:

uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

吞吐量測試方法簡介

使用 Linux 下的 wrk 工具對三個 API 進行測試:

若使用 Mac 作業系統可以安裝:

brew install wrk

下指令測試:

wrk -t4 -c100 -d30s http://localhost:8000/hello
  • -t4:4 個 thread
  • -c100:100 個持續連線
  • -d30s:測試持續 30 秒

測試結果可觀察 Requests/sec 欄位,即為吞吐量。


性能分析與比較

  • Django 在簡單請求下可達數千請求/秒,但因同步處理與較重的框架開銷,無法輕易擴展至高併發環境。
  • Flask 同樣是同步,吞吐量略優於 Django,但主要瓶頸仍在同步阻塞與伺服器資源分配。
  • FastAPI 採用非同步設計,利用 Python 的 async/await 及高效事件迴圈,能在相同硬體資源下達到數倍甚至十倍以上吞吐量,尤其適合 I/O 密集型服務。

總結

選擇適合的 Python Web 框架時,需根據專案規模、功能需求及預期流量做權衡。Django 適合快速搭建大型且功能完整的應用,Flask 適合靈活開發與小型專案,而 FastAPI 則是現代高效能 API 開發的首選。

在吞吐量需求高、需要非同步處理的情境下,FastAPI 明顯優勢突出。未來隨著非同步技術普及,FastAPI 的應用範圍將持續擴大。

參考文件

  1. locust 官方文件

Django「一對一」、「一對多」、「多對多」關聯入門教學筆記 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

當你在學 Django 或資料庫設計時,常會遇到「一對一」、「一對多」、「多對多」這三種關聯(Relationship)。這些是資料庫中表與表之間的關係,下面用簡單的例子幫你搞懂它們的差異:


一對一(One-to-One)

概念:

一個資料只對應另一個資料,彼此之間是唯一配對的關係。

例子:

每個人都有一張身份證 → 一個人對應一張身份證,一張身份證只對應一個人。

Django 實作:

class Person(models.Model):
name = models.CharField(max_length=100)

class IDCard(models.Model):
number = models.CharField(max_length=20)
owner = models.OneToOneField(Person, on_delete=models.CASCADE)

一對多(One-to-Many)

概念:

一筆資料可以對應到多筆資料,但每一筆資料只能對應回唯一一筆上層資料。

例子:

一個作者可以寫很多本書 → 一個作者對應多本書,但一本書只會有一個作者。

Django 實作:

class Author(models.Model):
name = models.CharField(max_length=100)

class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)

多對多(Many-to-Many)

概念:

一筆資料可以對應到多筆資料,而對方也可以對應回來多筆資料。

例子:

學生選修多門課程,一門課也有很多學生 → 學生對多門課,課程對多位學生。

Django 實作:

class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField('Course')

class Course(models.Model):
title = models.CharField(max_length=100)

(也可以在 Course 裡加 students = models.ManyToManyField(Student),結果會一樣)


小整理比較表:

類型關係形式範例Django 欄位
一對一A ➝ B 且 B ➝ A人 → 身份證OneToOneField
一對多A ➝ 多個 B作者 → 書ForeignKey
多對多A ⇄ 多個 B,互相多對多學生 ⇄ 課程ManyToManyField

Django Middleware 入門教學筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在 Web 應用程式中,每一次的 HTTP 請求與回應都會經過一連串的處理流程。在 Django 框架中,這些處理流程的攔截點與操作邏輯就是透過「Middleware(中介軟體)」來實現。

Middleware 是 Django 請求與回應處理流程中一個極為關鍵的機制,允許開發者在請求進入 View 前或回應送出前進行特定操作,例如:身份驗證、日誌紀錄、權限控制、跨域處理、錯誤攔截、壓縮回應等。

掌握 Middleware 的基本原理與撰寫方式,將有助於你更靈活地控制整個應用程式的行為與安全性。


重點摘要

  • Middleware 是介於「請求 → View → 回應」流程中,可插入自訂邏輯的中介處理元件。

  • 每個 Middleware 類別皆需實作特定的方法,如 __call__()process_request()process_response()

  • Middleware 可用於:日誌、認證、CORS、壓縮、IP 限制、防止 CSRF 等。

  • 所有 Middleware 的執行順序依 settings.pyMIDDLEWARE 設定順序為準。

  • Middleware 執行順序為:

    • 請求階段(request):從上往下
    • 回應階段(response):從下往上

Middleware 執行流程簡述

以下是 Django 處理請求的標準流程:

Request

Middleware(request)→ View 處理 → Middleware(response)

Response

舉例來說,假設你有三個 Middleware:

MIDDLEWARE = [
'myproject.middleware.FirstMiddleware',
'myproject.middleware.SecondMiddleware',
'myproject.middleware.ThirdMiddleware',
]

執行順序會是:

  • 請求階段:First → Second → Third → View
  • 回應階段:Third → Second → First → Client

Middleware 的基本結構與方法

從 Django 1.10 起,Middleware 使用「新式 Middleware」架構,基於 __call__() 方法設計。基本範例如下:

# middleware.py
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# 這裡是初始化,只在伺服器啟動時執行一次

def __call__(self, request):
# 處理請求邏輯(View 前)
print("Before view")

response = self.get_response(request)

# 處理回應邏輯(View 後)
print("After view")

return response

實際範例一:請求時間計算 Middleware

此範例會計算 View 執行所花費的時間,並加入到 HTTP 回應標頭中。

# middleware.py
import time

class TimerMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
start_time = time.time()
response = self.get_response(request)
duration = time.time() - start_time
response["X-Process-Time"] = f"{duration:.4f}s"
return response

settings.py 中啟用:

MIDDLEWARE = [
# 其他內建 middleware...
'myapp.middleware.TimerMiddleware',
]

當你訪問任意頁面時,HTTP 回應標頭中會出現類似:

X-Process-Time: 0.0321s

實際範例二:只允許特定 IP 的 Middleware

你可以建立一個 Middleware,用來限制只有某些 IP 位址可以訪問網站。

# middleware.py
from django.http import HttpResponseForbidden

class IPWhitelistMiddleware:
ALLOWED_IPS = ["127.0.0.1", "192.168.1.100"]

def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
ip = request.META.get("REMOTE_ADDR")
if ip not in self.ALLOWED_IPS:
return HttpResponseForbidden("Access denied.")
return self.get_response(request)

這可以用來防止內部 API 被外部訪問。


常見用途與建議實作

用途建議工具或方式
請求/回應紀錄實作 logging Middleware
使用者權限控管自訂 Middleware 或使用 Django Authentication
防止過多請求與 Redis 結合實作 Rate Limiting Middleware
跨域(CORS)處理使用 django-cors-headers 套件
錯誤攔截與通知捕捉例外並結合外部通知工具(如 Sentry)

注意事項與開發建議

  1. Middleware 應保持單一職責:一個 Middleware 做一件事,方便測試與維護。
  2. 避免過度耦合 View 或 Model:Middleware 應專注於請求與回應處理,不應過度介入業務邏輯。
  3. 注意 Middleware 順序:Django 會依照 MIDDLEWARE 陣列順序執行,不當排序可能導致無效或錯誤。
  4. 善用內建 Middleware:如 SecurityMiddleware, CommonMiddleware, AuthenticationMiddleware,無須重造輪子。
  5. 除錯技巧:可在 Middleware 中使用 print()logging 或加入 HTTP 標頭協助觀察執行情況。

總結

Middleware 是 Django 框架中攔截與處理請求/回應流程的核心機制之一。學會撰寫與使用 Middleware,能讓你更有效地實現各種橫向功能(如安全、效能、紀錄等),並保持應用架構的乾淨與可維護性。

透過本教學筆記,你應該已理解:

  • Middleware 的基本概念與執行流程
  • 如何撰寫一個簡單的 Middleware 類別
  • 幾種常見的 Middleware 實用場景與範例

進一步建議可以閱讀 Django 官方文件中的 Middleware 章節,並觀察第三方套件的 Middleware 是如何實作的,將更有助於理解其威力與應用彈性。

Django 中的 n+1 問題入門教學筆記

· 閱讀時間約 4 分鐘
kdchang

前言

Django 作為一個功能完整的 Python Web 框架,其 ORM(Object-Relational Mapping)能讓開發者以物件導向方式操作資料庫。然而,這樣的便利也容易隱藏一些效能陷阱,其中最常見也最容易忽略的就是 n+1 查詢問題(n+1 query problem)

n+1 問題會導致程式在執行查詢時產生大量多餘的 SQL 語句,影響效能並拖慢頁面載入速度,特別是在處理關聯資料時(如 ForeignKey 或 ManyToManyField)。本篇筆記將帶你認識 n+1 問題在 Django 中的成因、辨識方法與解法。


重點摘要

  • n+1 問題定義:查詢一個主物件(n 筆),卻對每筆物件再執行一次額外查詢,總共造成 n+1 次查詢。

  • 常見發生情境:在模板或程式中存取 ForeignKey 或 ManyToManyField 時,未預先載入(eager loading)相關資料。

  • 效能影響:每個物件觸發一次額外 SQL,當資料量增加時,查詢數可能達到數百次以上。

  • 解法

    • 使用 select_related() 預先載入「多對一」與「一對一」的關聯。
    • 使用 prefetch_related() 預先載入「一對多」與「多對多」的關聯。
  • 如何偵測 n+1 問題

    • 開啟 django.db.backends 日誌觀察查詢數量與內容。
    • 使用 Django Debug Toolbar 查看 SQL 查詢次數與細節。

實際範例:部落格文章與作者

假設有以下兩個模型:

# models.py
class Author(models.Model):
name = models.CharField(max_length=100)

class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(Author, on_delete=models.CASCADE)

n+1 問題範例

# views.py
def post_list(request):
posts = Post.objects.all()
return render(request, "blog/post_list.html", {"posts": posts})
{# post_list.html #}
<ul>
{% for post in posts %}
<li>{{ post.title }} - {{ post.author.name }}</li>
{% endfor %}
</ul>

這段程式會發生 n+1 問題:

  • 第一次查詢取得所有文章 SELECT * FROM post
  • 每篇文章查詢一次作者 SELECT * FROM author WHERE id = ?

如果有 100 篇文章,總共會執行 101 次查詢。


# views.py(改進)
def post_list(request):
posts = Post.objects.select_related("author").all()
return render(request, "blog/post_list.html", {"posts": posts})

select_related 會使用 SQL JOIN 一次把作者資料一起載入。查詢數減少為 1 次:

SELECT post.*, author.*
FROM post
JOIN author ON post.author_id = author.id;

這種方式適合用於 ForeignKey(多對一)與 OneToOneField 關聯。


若改為一對多或多對多關係,例如:

class Tag(models.Model):
name = models.CharField(max_length=30)

class Post(models.Model):
title = models.CharField(max_length=200)
tags = models.ManyToManyField(Tag)

如果在模板中使用:

{% for post in posts %}
{{ post.title }}:
{% for tag in post.tags.all %}
{{ tag.name }}
{% endfor %}
{% endfor %}

這樣會產生 n+1 查詢問題(每個 post 查一次 tag)。解法:

posts = Post.objects.prefetch_related("tags").all()

prefetch_related() 會先查出所有關聯,再用 Python 記憶體關聯對應資料,不用 JOIN。

SELECT * FROM post;
SELECT * FROM post_tags WHERE post_id IN (...);
SELECT * FROM tag WHERE id IN (...);

特性select_relatedprefetch_related
關係類型一對一、外鍵(ForeignKey)一對多、多對多
查詢方式使用 JOIN 一次查出分別查詢後用 Python 關聯
查詢數量一次查詢即可完成最少兩次查詢
效能適用情境關聯資料不多且關係單純關聯資料多或複雜嵌套

如何偵測與除錯 n+1 問題

  1. 開啟 SQL 日誌: 在 settings.py 中設定:

    LOGGING = {
    "version": 1,
    "handlers": {
    "console": {
    "class": "logging.StreamHandler",
    },
    },
    "loggers": {
    "django.db.backends": {
    "handlers": ["console"],
    "level": "DEBUG",
    },
    },
    }
  2. 使用 Django Debug Toolbar: 安裝與設定後可視覺化查詢次數與內容。

  3. 查看 QuerySet 查詢次數: 使用 len(connection.queries) 或中間件分析每個 request 的查詢數。


總結

n+1 問題是 Django ORM 中最常見的效能陷阱之一,但只要了解其原理與解法,透過 select_related()prefetch_related() 搭配得當,幾乎可以完全避免這個問題。

掌握以下原則即可:

  • 遇到 ForeignKey 或 OneToOne 時用 select_related()
  • 遇到 ManyToMany 或反向 ForeignKey 時用 prefetch_related()
  • 避免在模板中直接使用 .related_set.all() 未預先載入資料
  • 對列表頁或頻繁查詢頁進行效能測試與 SQL 分析

良好的 ORM 使用習慣能大幅提升系統穩定性與使用者體驗,是每位 Django 開發者必備的基礎功。

Django CRUD(不使用 ModelForm)入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

在 Django 中,ModelForm 提供了一個快速建立表單與驗證的工具,但在某些情境下,我們可能希望自己掌控表單結構與驗證流程。這篇筆記將示範如何不依賴 ModelForm,手動實作一套 CRUD 系統,幫助你更深入理解 Django 表單處理的基本原理。

我們將製作一個簡單的「書籍管理系統」,支援新增(Create)、讀取(Read)、更新(Update)與刪除(Delete)書籍資訊。

1. 建立 Django 專案與應用

首先,安裝 Django 並建立新的專案與應用:

pip install django
django-admin startproject myproject
cd myproject
python manage.py startapp books

註冊 books 應用於 myproject/settings.pyINSTALLED_APPS

INSTALLED_APPS = [
...
'books',
]

2. 定義模型(Model)

books/models.py 中定義一個簡單的書籍模型:

from django.db import models

class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
published_date = models.DateField()

def __str__(self):
return self.title

遷移資料庫:

python manage.py makemigrations
python manage.py migrate

3. 撰寫 Views(不使用 ModelForm)

books/views.py 中撰寫手動處理的 CRUD 功能。

新增書籍(Create)

from django.shortcuts import render, redirect
from .models import Book
from django.utils.dateparse import parse_date

def create_book(request):
if request.method == 'POST':
title = request.POST.get('title')
author = request.POST.get('author')
published_date_str = request.POST.get('published_date')
published_date = parse_date(published_date_str)

if title and author and published_date:
Book.objects.create(title=title, author=author, published_date=published_date)
return redirect('book_list')
else:
error = "所有欄位皆為必填"
return render(request, 'books/book_form.html', {'error': error})
return render(request, 'books/book_form.html')

讀取書籍(Read)

def book_list(request):
books = Book.objects.all()
return render(request, 'books/book_list.html', {'books': books})

更新書籍(Update)

from django.shortcuts import get_object_or_404

def update_book(request, pk):
book = get_object_or_404(Book, pk=pk)
if request.method == 'POST':
title = request.POST.get('title')
author = request.POST.get('author')
published_date_str = request.POST.get('published_date')
published_date = parse_date(published_date_str)

if title and author and published_date:
book.title = title
book.author = author
book.published_date = published_date
book.save()
return redirect('book_list')
else:
error = "所有欄位皆為必填"
return render(request, 'books/book_form.html', {'book': book, 'error': error})
return render(request, 'books/book_form.html', {'book': book})

刪除書籍(Delete)

def delete_book(request, pk):
book = get_object_or_404(Book, pk=pk)
if request.method == 'POST':
book.delete()
return redirect('book_list')
return render(request, 'books/book_confirm_delete.html', {'book': book})

4. 設定 URL 路由

books/urls.py 中設定對應的路由:

from django.urls import path
from . import views

urlpatterns = [
path('', views.book_list, name='book_list'),
path('create/', views.create_book, name='create_book'),
path('update/<int:pk>/', views.update_book, name='update_book'),
path('delete/<int:pk>/', views.delete_book, name='delete_book'),
]

並在 myproject/urls.py 引入 books 路由:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('books/', include('books.urls')),
]

5. 建立模板(Templates)

手動撰寫簡單的 HTML 表單與顯示畫面。

book_list.html

<h1>書籍列表</h1>
<a href="{% url 'create_book' %}">新增書籍</a>
<ul>
{% for book in books %}
<li>
{{ book.title }} - {{ book.author }} - {{ book.published_date }}
<a href="{% url 'update_book' book.id %}">編輯</a>
<a href="{% url 'delete_book' book.id %}">刪除</a>
</li>
{% endfor %}
</ul>

book_form.html

<h1>{% if book %}編輯書籍{% else %}新增書籍{% endif %}</h1>

{% if error %}
<p style="color:red;">{{ error }}</p>
{% endif %}

<form method="post">
{% csrf_token %}
<p>
標題:<input type="text" name="title" value="{{ book.title|default_if_none:'' }}">
</p>
<p>
作者:<input type="text" name="author" value="{{ book.author|default_if_none:'' }}">
</p>
<p>
出版日期(格式 yyyy-mm-dd):<input type="text" name="published_date" value="{{ book.published_date|default_if_none:'' }}">
</p>
<button type="submit">儲存</button>
</form>

<a href="{% url 'book_list' %}">返回列表</a>

book_confirm_delete.html

<h1>刪除書籍</h1>
<p>確定要刪除 "{{ book.title }}" 嗎?</p>
<form method="post">
{% csrf_token %}
<button type="submit">確定刪除</button>
</form>
<a href="{% url 'book_list' %}">取消</a>

6. 啟動伺服器測試

啟動 Django 開發伺服器:

python manage.py runserver

在瀏覽器開啟 http://127.0.0.1:8000/books/,你將可以新增、查詢、編輯和刪除書籍。


總結

這篇教學示範了在 不使用 ModelForm 的情況下,手動撰寫表單處理與資料驗證,完整實作了 Django 的 CRUD 功能。

這種方式的優點在於靈活度高,你可以完全控制表單的結構、驗證邏輯與錯誤處理,非常適合需要客製化表單行為或前後端分離的專案。不過,相較於使用 ModelForm,開發成本略高,也容易產生重複程式碼,因此適時選擇工具是重要的工程判斷。

進一步的優化方向包括:

  • 加入更完整的資料驗證
  • 增加欄位格式錯誤提示
  • 使用 JavaScript 增強表單互動
  • 將表單資料與邏輯封裝成 Class-Based Views(CBV)

透過本篇範例,希望你對 Django 低階處理表單與 CRUD 流程有更深入的理解。

concurrently.js 入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在前端與全端開發中,我們經常需要同時執行多個指令,例如一邊啟動前端開發伺服器,一邊啟動後端 API 伺服器,或一邊監看 Sass 編譯,一邊執行 TypeScript 編譯。在這樣的情境下,concurrently 是一個非常實用的工具,它能讓我們在同一個命令列同時執行多個命令。

本篇筆記將介紹 concurrently 是什麼、如何安裝、基本用法,以及實際應用範例。


什麼是 concurrently?

concurrently 是一個 Node.js 套件,可以在一個命令中同時執行多個命令行指令,而且會把各個執行的結果分別以不同顏色標示,方便辨認。它常用來取代 npm-run-allgulp 等工具中針對「平行執行任務」的需求。

官方說明:“Run multiple commands concurrently.”

例如你想同時執行一個 React 專案的前端開發伺服器與後端 API 伺服器:

npm run start-frontend & npm run start-backend

在不同系統或 shell 可能有兼容性問題,用 concurrently 則會簡單許多。


1️⃣ 安裝 concurrently

首先,你需要在專案中安裝 concurrently

npm install concurrently --save-dev

或者使用 yarn:

yarn add concurrently --dev

安裝完成後,你可以在 package.jsonscripts 區塊中使用它。


2️⃣ 基本用法

最基本的用法如下:

npx concurrently "command1" "command2"

例如,同時執行 npm run servernpm run client

npx concurrently "npm run server" "npm run client"

執行時會在終端顯示兩個任務的 log,並自動用不同顏色標示來源。


3️⃣ 在 package.json 中使用

通常我們會把 concurrently 寫在 package.json 裡的 scripts 區塊:

{
"scripts": {
"server": "node server.js",
"client": "react-scripts start",
"dev": "concurrently \"npm run server\" \"npm run client\""
}
}

執行 npm run dev,就會同時執行 server.js(後端)與 react-scripts start(前端)。

這樣只需要一個指令就能同時啟動兩個服務,對開發非常方便。


4️⃣ 實際範例:React + Express 同時啟動

假設你有一個專案結構如下:

my-app/
client/
(React 專案)
server/
(Express 後端)

server/package.json

{
"name": "server",
"version": "1.0.0",
"scripts": {
"start": "node index.js"
}
}

client/package.json

{
"name": "client",
"version": "1.0.0",
"scripts": {
"start": "react-scripts start"
}
}

在專案根目錄的 package.json 中加入:

{
"name": "my-app",
"version": "1.0.0",
"scripts": {
"start": "concurrently \"npm start --prefix server\" \"npm start --prefix client\""
},
"devDependencies": {
"concurrently": "^8.0.0"
}
}
  • --prefix 是讓 npm 到指定資料夾執行
  • npm start --prefix server 會到 server/ 資料夾執行 npm start
  • npm start --prefix client 會到 client/ 資料夾執行 npm start

此時執行:

npm start

就會同時啟動 React 前端與 Express 後端,而且都會在終端中顯示 log,方便觀察開發情況。


5️⃣ 進階用法

✅ 自訂 prefix 與顏色

可以自訂 log 標籤與顏色,方便辨識哪一個 log 來自哪個指令:

npx concurrently --names "BACK,FRONT" --prefix-colors "bgBlue.bold,bgMagenta.bold" "npm run server" "npm run client"

效果:

[BACK] server running on port 5000
[FRONT] webpack compiled successfully

✅ 自動終止所有執行中的任務

當一個任務失敗時,讓其他任務也自動停止:

npx concurrently --kill-others "npm run server" "npm run client"

當其中一個 command 錯誤,另一個也會被 kill,避免殘留無用的背景 process。


6️⃣ 常見用途

concurrently 常見的使用情境有:

  • 同時啟動多個 Node.js 服務
  • 一邊監看 Sass/LESS 編譯,一邊執行 webpack
  • 同時跑 test 與 build
  • 同時監看前端與後端程式碼

如果你用 create-react-appNext.jsViteExpressNestJS 這些框架開發全端應用,concurrently 非常適合整合開發流程。


7️⃣ 與其他工具比較

concurrently 的特色在於:

  • 簡單語法(不需要額外配置檔)
  • 支援 Windows、Linux、Mac
  • 輕量、僅做平行執行,不強制任務流程
  • log 輸出自帶標籤與顏色
  • 可搭配 npm script 或 npx 單獨執行

如果需要「先後順序執行多個任務」,則可以搭配 npm-run-all


總結

concurrently 是一個適合在開發環境中同時執行多個命令的小工具,對需要前後端同時啟動、前端多個編譯工作並行的開發者非常實用。

它用法簡單、安裝快速、跨平台,推薦將它納入專案開發腳本中,以提升開發效率。

希望這篇筆記能幫助你了解 concurrently 的用途與基本操作!

Django RESTful API 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

隨著 Web 與行動應用的發展,API(應用程式介面)已成為前後端溝通的橋樑。RESTful API 是目前最常見的 API 設計風格之一,而 Django REST Framework(簡稱 DRF)則是基於 Django 的強大工具,讓開發 RESTful API 變得更簡單。

本篇筆記將帶我們從零開始,快速建立一個 Django RESTful API 專案,實作一個基本的「文章系統」。


1. 安裝 Django 與 Django REST Framework

首先,建立一個虛擬環境並安裝必要套件:

python -m venv venv
source venv/bin/activate # Windows 用戶請使用 venv\Scripts\activate
pip install django djangorestframework

接著,建立 Django 專案:

django-admin startproject myapi
cd myapi

建立一個 app 來處理文章功能:

python manage.py startapp articles

2. 設定專案

myapi/settings.py 中,將 rest_frameworkarticles 加入 INSTALLED_APPS

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'articles',
]

3. 建立模型

打開 articles/models.py,定義一個 Article 模型:

from django.db import models

class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return self.title

然後執行遷移:

python manage.py makemigrations
python manage.py migrate

4. 建立序列化器(Serializer)

articles 資料夾中建立 serializers.py

from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'

這樣就完成了資料庫模型與 JSON 之間的轉換設定。


5. 建立 API View

articles/views.py 中加入以下程式:

from rest_framework import generics
from .models import Article
from .serializers import ArticleSerializer

class ArticleListCreateView(generics.ListCreateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer

class ArticleDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer

我們使用了 DRF 的「通用類別視圖」,大幅減少手寫程式碼。


6. 設定 URL

articles 資料夾中建立 urls.py

from django.urls import path
from .views import ArticleListCreateView, ArticleDetailView

urlpatterns = [
path('articles/', ArticleListCreateView.as_view(), name='article-list-create'),
path('articles/<int:pk>/', ArticleDetailView.as_view(), name='article-detail'),
]

接著將 articles/urls.py 加入主專案的 myapi/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('articles.urls')),
]

現在 API 路由已經設定完成。


7. 測試 API

啟動開發伺服器:

python manage.py runserver

打開瀏覽器,造訪 http://127.0.0.1:8000/api/articles/,我們會看到 DRF 提供的漂亮瀏覽介面,可以直接用網頁表單測試 API。

我們也可以用工具如 Postman 或 curl 測試:

建立新文章

curl -X POST -H "Content-Type: application/json" \
-d '{"title": "第一篇文章", "content": "這是一篇測試文章"}' \
http://127.0.0.1:8000/api/articles/

取得文章列表

curl http://127.0.0.1:8000/api/articles/

取得單篇文章

curl http://127.0.0.1:8000/api/articles/1/

更新文章

curl -X PUT -H "Content-Type: application/json" \
-d '{"title": "更新後標題", "content": "更新後內容"}' \
http://127.0.0.1:8000/api/articles/1/

刪除文章

curl -X DELETE http://127.0.0.1:8000/api/articles/1/

8. 總結

透過這篇筆記,我們完成了:

  • 安裝 Django 與 DRF
  • 定義模型與序列化器
  • 使用泛型類別視圖實作 CRUD API
  • 設定 URL 路由
  • 使用瀏覽器或命令列工具測試 API

Django REST Framework 提供了許多自動化與簡化開發的工具,讓我們能快速建立出符合 REST 標準的 API。當然,隨著需求增加,我們也可以進一步學習自定義權限、驗證、過濾與分頁等功能。