为什么选择Django Ninja?
Django Ninja 是一个基于 Python 类型提示的 Django REST 框架,它结合了 Django 的成熟生态和 FastAPI 的简洁高效。相比于 Django REST Framework (DRF),Ninja 具有以下优势:
- 自动生成 OpenAPI 文档:无需额外配置,即得 Swagger UI 和 ReDoc。
- 类型安全:利用 Pydantic 进行参数校验,减少运行时错误。
- 高性能:基于异步视图,支持高并发场景。
- 轻量级:API 代码更简洁,学习成本低。
本文将带你从零搭建一个图书管理 API 系统,涵盖常见业务场景。
环境准备
确保已安装 Python 3.8+,然后创建虚拟环境并安装依赖:
pip install django ninja
第一步:创建 Django 项目
django-admin startproject bookmanager
cd bookmanager
python manage.py startapp books
将 books 添加到 INSTALLED_APPS:
# bookmanager/settings.py
INSTALLED_APPS = [
...
'books',
]
第二步:定义模型
# 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()
isbn = models.CharField(max_length=13, unique=True)
price = models.DecimalField(max_digits=6, decimal_places=2)
def __str__(self):
return self.title
执行迁移:
python manage.py makemigrations
python manage.py migrate
第三步:创建 Ninja API
在 books 目录下创建 api.py:
# books/api.py
from ninja import NinjaAPI, ModelSchema, Schema
from typing import List
from .models import Book
from django.shortcuts import get_object_or_404
api = NinjaAPI()
# 定义输入输出 Schema
class BookIn(Schema):
title: str
author: str
published_date: str # 接收字符串,后续转换
isbn: str
price: float
class BookOut(ModelSchema):
class Meta:
model = Book
fields = ['id', 'title', 'author', 'published_date', 'isbn', 'price']
# 转换日期字符串
from datetime import datetime
@api.post("/books/", response=BookOut)
def create_book(request, payload: BookIn):
# 将字符串日期转换为 date 对象
try:
published_date = datetime.strptime(payload.published_date, "%Y-%m-%d").date()
except ValueError:
return api.create_response(request, {"error": "日期格式错误,应为 YYYY-MM-DD"}, status=400)
book = Book.objects.create(
title=payload.title,
author=payload.author,
published_date=published_date,
isbn=payload.isbn,
price=payload.price
)
return book
@api.get("/books/", response=List[BookOut])
def list_books(request):
return Book.objects.all()
@api.get("/books/{book_id}", response=BookOut)
def get_book(request, book_id: int):
book = get_object_or_404(Book, id=book_id)
return book
@api.put("/books/{book_id}", response=BookOut)
def update_book(request, book_id: int, payload: BookIn):
book = get_object_or_404(Book, id=book_id)
try:
published_date = datetime.strptime(payload.published_date, "%Y-%m-%d").date()
except ValueError:
return api.create_response(request, {"error": "日期格式错误"}, status=400)
book.title = payload.title
book.author = payload.author
book.published_date = published_date
book.isbn = payload.isbn
book.price = payload.price
book.save()
return book
@api.delete("/books/{book_id}", response={204: None})
def delete_book(request, book_id: int):
book = get_object_or_404(Book, id=book_id)
book.delete()
return 204, None
第四步:注册 API 路由
在 bookmanager/urls.py 中:
from django.contrib import admin
from django.urls import path
from books.api import api
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', api.urls),
]
第五步:运行并测试
python manage.py runserver
访问 http://127.0.0.1:8000/api/docs 即可看到 Swagger 文档。
测试 API
使用 curl 或 Postman 测试:
# 创建图书
curl -X POST http://127.0.0.1:8000/api/books/ \
-H "Content-Type: application/json" \
-d '{"title":"Django Ninja实战","author":"张三","published_date":"2024-01-15","isbn":"9781234567890","price":39.99}'
# 获取列表
curl http://127.0.0.1:8000/api/books/
进阶技巧:ORM 优化与分页
当数据量大时,直接返回 Book.objects.all() 可能导致性能问题。Ninja 内置了分页支持:
from ninja.pagination import paginate, PageNumberPagination
@api.get("/books/", response=List[BookOut])
@paginate(PageNumberPagination, page_size=10)
def list_books(request):
return Book.objects.all().select_related() # 优化关联查询
同时,在查询中使用 selectrelated 或 prefetchrelated 减少数据库查询次数。
错误处理最佳实践
使用 Ninja 的异常处理机制统一返回格式:
from ninja.errors import HttpError
@api.exception_handler(HttpError)
def custom_http_error_handler(request, exc):
return api.create_response(
request,
{"detail": exc.message},
status=exc.status_code,
)
总结
本文介绍了如何使用 Django Ninja 快速构建一个图书管理 API,涵盖模型定义、Schema 设计、CRUD 操作、自动文档生成以及性能优化。相比于 DRF,Ninja 的代码量更少,类型安全更强,非常适合现代 API 开发。
下一步方向
- 集成认证(JWT 或 Session)
- 添加单元测试
- 使用异步视图处理 I/O 密集型任务
欢迎在评论区交流你的实战经验!