Django Ninja 路由#

這是 Django Ninja 系列教學的第 8 篇。
上一篇文章中,我們介紹了 Django 傳統的路由設定方式。
如前所述,雖然有一個「路由清單」確實不錯。但隨著專案規模的擴大,不斷來回切換 urls.py 和 views.py 將大幅增加開發者的認知負擔——拉長了開發時間,還更容易導致錯誤。
Django Ninja 採用了一種更現代化的路由設計,結合了 Flask 和 FastAPI 的設計理念。不僅簡化了路由的定義,還提升了程式碼的可讀性,讓路由與 view 函式緊密結合。
範例專案動態#
本文關於路由設定的程式碼改動,可以參考這個 PR(Pull Request)。
範例專案合併了本文的 PR 後,已經正式成為一個「API 專案」,不過它目前(指這個 commit 狀態)尚無法正常運作,因為我們還沒有完善 view 函式的基本功能。
你可以一步步地跟著每一篇的 PR,來學習當次的新內容。這也是我為文章建立 PR 的用意所在。
範例專案#
現在,我們開始介紹 Django Ninja 的路由設定。
Django Ninja 路由概述#
Django Ninja 使用 Python 裝飾器(decorator)來定義路由和 HTTP 方法。這種方式將路由與 view 函式緊密結合,大大提高了程式碼的可讀性。
熟悉 Python 的都知道,其實這種「使用裝飾器定義路由」的方式,最早來自 Flask。作為一個輕量級框架,Flask 率先引入了這種簡潔優雅的設計,堪稱典範級的創舉。
這個設計後續被其他框架採用,比如 FastAPI 和本文的 Django Ninja,都繼承了這種靈活的路由定義模式。
在 Flask 中,開發者可以這樣定義路由:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, Flask!'
Django Ninja 也採用了類似的概念,只是在語法上更融入 Django 生態,並結合了型別提示(type hint)和 Pydantic 的資料驗證功能,讓 API 開發變得更加現代化。
以下是 Django Ninja 的一個簡單範例:
from ninja import NinjaAPI
api = NinjaAPI()
@api.get('/')
def hello(request):
return {"message": "Hello, Django Ninja!"}
Flask 與 Django Ninja 這兩種寫法,與其說非常相似,不如說一模一樣😎
更有組織的做法:使用 Router 物件#
雖然直接使用上述範例中的 NinjaAPI 來定義路由簡單而直觀。但實際工作中,我們更推薦使用 Router 物件(官方文件)來管理不同 Django app 的路由。
from ninja import Router
router = Router() # 建立 Router 物件
@router.get(path='/')
def hello(request):
return {"message": "Hello, Django Ninja!"}
這和 Django 傳統做法中「區分一級與二級路由」的基本精神相符。不僅讓專案的架構保持清晰,還能讓每個 app 的邏輯獨立。
在 Django Ninja 中,Router 物件提供了一種模組化的路由設定方式,讓每個 Django app 可以管理自己的路由,並在專案層級的 api.py 統一整合起來——也就是取代傳統的 urls.py 功能。
以下程式碼範例,我們都會以 Router 物件來實作。
專案架構變化#
先來看看採用 Django Ninja 以後,傳統 Django 專案的結構會有怎麼樣的變化。
Django 傳統路由結構#
以範例專案為例,這是傳統 Django 的典型結構:
├── NinjaForum
│ ├── urls.py # 專案一級路由
│ ├── ...
├── post
│ ├── urls.py # app 二級路由
│ ├── view.py # 放置 app 所屬 view 函式的地方
│ ├── ...
├── user
│ ├── urls.py # app 二級路由
│ ├── view.py # 放置 app 所屬 view 函式的地方
│ ├── ...
├── ...
Django app 層級的 urls.py 負責定義所有 app 內的路由,然後再由專案的 urls.py 進行統合。井然有序,權責分明。
Django Ninja 路由結構#
在採用 Django Ninja 後,專案結構會有一些變化。以下是一個典型的 Django Ninja 專案結構:
├── NinjaForum
│ ├── urls.py # 專案「零級」路由
│ ├── api.py # 專案一級路由
│ ├── ...
├── post
│ ├── api.py # post app 的路由 + view 函式
│ ├── ...
├── user
│ ├── api.py # user app 的路由 + view 函式
│ ├── ...
├── ...
在這個結構中,每個 Django app 都有一個 api.py,用於定義該 app 的所有 API 路由和 view 函式,取代了傳統 Django 中的 urls.py 和 views.py 的功能。
專案級別的 api.py 則負責整合所有 Django app 的 API。
此外,專案的 urls.py 仍然是必要的,它負責將 Django Ninja 的 API 路由再整合到 Django 的 URL 設定中。同時,還可以化身為「零級」路由,為所有 API 加上全專案統一的路由前綴,比如 /api/。
Django Ninja 路由實作#
了解了 Django Ninja 的路由結構,我們直接在範例專案的兩個 Django app 分別實作「取得所有使用者」和「取得文章列表」兩個 API。
我們會在接下來的數篇文章中,循序漸進地完善這些 API。目前只是雛形,先把焦點放在路由設定上。
一、建立二級路由#
在 user app 中建立一個 api.py, 內容為:
# user/api.py
from ninja import Router
router = Router()
@router.get(path='/')
def get_users(request):
users = User.objects.all()
return users
同理,我們在 post/api.py 中建立類似的路由與 view 函式:
# post/api.py
from ninja import Router
router = Router()
@router.get(path='/')
def get_posts(request):
posts = Post.objects.all()
return posts
這樣,我們就為 user 和 post 兩個 app 分別建立了 API。接下來,我們需要將這些路由整合到專案級別的 API 路由中。
二、建立一級路由#
在 Django 專案目錄(指 NinjaForum 目錄)底下,我們也需要建立一個 api.py。它將作為我們的一級路由,整合所有 app 的 API。以下是這個 api.py 的內容:
# NinjaForum/api.py
from ninja import NinjaAPI
api = NinjaAPI()
api.add_router(prefix='/users/', router='user.api.router')
api.add_router(prefix='/posts/', router='post.api.router')
值得留意的是,這裡的路由整合有兩種寫法,上面我習慣使用的。
另一種寫法是直接 import router 物件:
from user.api import router as user_router
from post.api import router as post_router
api.add_router(prefix='/users/', router=user_router)
api.add_router(prefix='/posts/', router=post_router)
這兩種方法在功能上是等效的,選擇哪種主要取決於個人偏好和專案的組織方式。
三、專案 urls.py#
在 Django Ninja 中,專案層級的 urls.py 化身為連接 Django 和 Django Ninja API 的橋梁。
在專案 urls.py 中,我們還能再定義全專案共用的路由前綴。長這樣:
from django.contrib import admin
from django.urls import path
from NinjaForum.api import api
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', api.urls),
]
這裡定義了一個專案路由前綴——/api/。
如此一來,「取得所有文章」的 API 端點將會是:
當然,如果你不需要額外的路由前綴,也可以直接省略:
urlpatterns = [
path('admin/', admin.site.urls),
path('', api.urls), # 省略前綴
]
至此,我們完成了 Django Ninja 的路由設定。
這樣的結構不僅保持了 Django 原有的模組化設計,還為我們的 API 開發提供了更大的靈活性。
本節收尾與下一步#
在第一節中,我們學習了如何使用 Django Ninja 定義路由,並了解 Django 傳統路由與 Django Ninja 路由的差異。
Django Ninja 的路由做法不僅讓程式碼更具可讀性,還保持了專案的清晰結構,改善了 Django 傳統路由的一些缺點。
下一篇,我們將進入 Django Ninja API 的核心部分——view 函式。