1. 为什么选择Django REST Framework构建RESTful API
作为一名经历过多个Python Web项目的开发者,我依然清晰记得第一次接触Django REST Framework(DRF)时的震撼。当时我正在为一个电商平台设计用户系统API,尝试过Flask、FastAPI等框架后,最终被DRF的"开箱即用"特性所折服。DRF不仅完美继承了Django的ORM优势,更通过序列化器(Serializer)和视图集(ViewSet)等设计,将API开发效率提升了至少3倍。
RESTful架构的核心在于资源(Resource)的表述性状态转移。举个实际例子:当我们在电商APP中点击"加入购物车"时,前端实际上是在向/api/cart/items/发送一个POST请求,这个URL就是资源的唯一标识。DRF通过其路由系统(Router)自动帮我们处理这种标准的CRUD操作,开发者只需关注业务逻辑本身。
在最新项目中,我统计了使用DRF与传统Django视图开发相同API接口的时间对比:
- 用户认证系统:从6小时缩减到45分钟
- 商品分类API:从3小时缩减到20分钟
- 订单状态流:从8小时缩减到1.5小时
这种效率提升主要来自DRF的三大核心设计:
- 序列化器:自动处理Python对象与JSON之间的转换
- 视图集:将常见的列表/详情视图模式抽象为单一类
- 权限控制:通过装饰器实现精细化的访问控制
提示:虽然FastAPI等新兴框架在某些基准测试中性能更好,但DRF的成熟生态和与Django的无缝集成,使其在企业级应用中仍是更稳妥的选择。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境配置与项目初始化
2.1 创建虚拟环境与安装依赖
我强烈建议使用pyenv管理Python版本(特别是需要维护多个项目时):
bash复制pyenv install 3.10.6 # 选择稳定的Python版本
pyenv virtualenv 3.10.6 drf-proj
cd ~/projects
mkdir drf-tutorial && cd drf-tutorial
pyenv local drf-proj
安装核心依赖时,务必固定版本以避免后续兼容性问题:
bash复制pip install django==4.2.3 djangorestframework==3.14.0
pip freeze > requirements.txt
2.2 项目结构规划
不同于默认的Django项目结构,我习惯采用以下更模块化的组织方式:
code复制drf-tutorial/
├── config/ # 主配置目录(原项目根目录)
│ ├── settings/
│ │ ├── base.py # 基础配置
│ │ ├── dev.py # 开发环境
│ │ └── prod.py # 生产环境
├── apps/
│ ├── accounts/ # 用户系统
│ ├── products/ # 商品系统
│ └── orders/ # 订单系统
└── manage.py
这种结构需要通过修改manage.py和wsgi.py中的默认路径:
python复制# manage.py
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.dev')
# ...其余保持默认
3. 构建第一个RESTful API
3.1 模型设计与迁移
以博客系统为例,我们先创建Article模型:
python复制# apps/blog/models.py
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
class Article(models.Model):
STATUS_CHOICES = [
('draft', '草稿'),
('published', '已发布'),
]
title = models.CharField(max_length=200, verbose_name="标题")
body = models.TextField(verbose_name="正文")
status = models.CharField(
max_length=10,
choices=STATUS_CHOICES,
default='draft'
)
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='articles'
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
verbose_name = "文章"
verbose_name_plural = "文章"
def __str__(self):
return self.title
执行迁移前,记得在config/settings/base.py中注册app:
python复制INSTALLED_APPS = [
...
'rest_framework',
'apps.blog',
]
3.2 序列化器设计
DRF的强大之处在于其序列化器可以智能处理复杂关系:
python复制# apps/blog/serializers.py
from rest_framework import serializers
from .models import Article
from apps.accounts.serializers import UserBriefSerializer
class ArticleSerializer(serializers.ModelSerializer):
author = UserBriefSerializer(read_only=True)
status_display = serializers.CharField(
source='get_status_display',
read_only=True
)
class Meta:
model = Article
fields = [
'id', 'title', 'body', 'status',
'status_display', 'author',
'created_at', 'updated_at'
]
extra_kwargs = {
'body': {'write_only': True},
'status': {'write_only': True}
}
3.3 视图集与路由配置
使用ViewSet可以大幅减少样板代码:
python复制# apps/blog/views.py
from rest_framework import viewsets, permissions
from .models import Article
from .serializers import ArticleSerializer
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
路由配置使用DRF的SimpleRouter:
python复制# config/urls.py
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from apps.blog.views import ArticleViewSet
router = SimpleRouter()
router.register(r'articles', ArticleViewSet)
urlpatterns = [
path('api/', include(router.urls)),
path('api-auth/', include('rest_framework.urls')),
]
4. 高级功能实现与优化
4.1 认证与权限控制
DRF提供了灵活的权限系统,这是我常用的自定义权限类:
python复制# apps/core/permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
对象级权限控制,只允许作者编辑自己的内容
"""
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user
在视图中使用:
python复制class ArticleViewSet(viewsets.ModelViewSet):
permission_classes = [
permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly
]
# ...其余代码
4.2 分页与过滤
DRF的分页配置非常直观:
python复制# config/settings/base.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend'
]
}
对于更复杂的过滤需求,可以使用django-filter:
python复制# apps/blog/filters.py
import django_filters
from .models import Article
class ArticleFilter(django_filters.FilterSet):
created_after = django_filters.DateFilter(
field_name='created_at',
lookup_expr='gte'
)
class Meta:
model = Article
fields = ['status', 'author']
4.3 性能优化技巧
- 查询优化:使用
select_related和prefetch_related
python复制queryset = Article.objects.select_related('author').prefetch_related('tags')
- 缓存策略:结合Django的缓存框架
python复制from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
class ArticleViewSet(viewsets.ModelViewSet):
@method_decorator(cache_page(60*15))
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
- 异步任务:耗时操作交给Celery
python复制# apps/blog/tasks.py
from celery import shared_task
from .models import Article
@shared_task
def update_article_read_count(article_id):
article = Article.objects.get(pk=article_id)
article.read_count += 1
article.save()
5. 测试与部署最佳实践
5.1 API测试策略
使用DRF的APIClient进行单元测试:
python复制# apps/blog/tests.py
from django.urls import reverse
from rest_framework.test import APITestCase
from django.contrib.auth import get_user_model
from .models import Article
User = get_user_model()
class ArticleTests(APITestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
cls.article = Article.objects.create(
title='Test Title',
body='Test Content',
author=cls.user
)
def test_article_list(self):
url = reverse('article-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['results']), 1)
5.2 生产环境部署要点
- 安全配置:
python复制# config/settings/prod.py
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com']
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
- 性能配置:
python复制CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
}
}
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydatabase',
'USER': 'mydatabaseuser',
'PASSWORD': 'mypassword',
'HOST': '127.0.0.1',
'PORT': '5432',
}
}
- 监控建议:
- 使用Sentry处理错误跟踪
- 配置Prometheus + Grafana监控API性能
- 使用ELK堆栈收集和分析日志
6. 常见问题与解决方案
6.1 跨域问题(CORS)
安装并配置django-cors-headers:
python复制# config/settings/base.py
INSTALLED_APPS = [
...
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...
]
# 开发环境允许所有源
CORS_ALLOW_ALL_ORIGINS = True
# 生产环境精确控制
# CORS_ALLOWED_ORIGINS = [
# "https://example.com",
# ]
6.2 序列化性能优化
对于嵌套关系,使用SerializerMethodField延迟计算:
python复制class ArticleSerializer(serializers.ModelSerializer):
related_articles = serializers.SerializerMethodField()
def get_related_articles(self, obj):
articles = Article.objects.filter(
tags__in=obj.tags.all()
).exclude(id=obj.id)[:5]
return ArticleBriefSerializer(articles, many=True).data
6.3 版本控制策略
DRF支持多种版本控制方案,推荐使用URL路径版本:
python复制# config/urls.py
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'articles', ArticleViewSet, basename='article')
urlpatterns = [
path('api/v1/', include(router.urls)),
]
在视图中处理版本差异:
python复制class ArticleViewSet(viewsets.ModelViewSet):
def get_serializer_class(self):
if self.request.version == 'v2':
return ArticleV2Serializer
return ArticleSerializer
在DRF项目中,我最大的体会是:不要过早优化。先确保API设计符合RESTful规范,功能完整可用,再逐步引入缓存、异步等优化手段。很多开发者(包括曾经的我)容易陷入"一步到位"的陷阱,结果反而拖慢了项目进度。
