为什么Instagram、Reddit、Mozilla这些科技巨头都选择Django REST Framework来构建他们的API服务?今天,我将手把手带你从零开始,用DRF打造一个功能完整、性能优越的企业级API系统,让你在35分钟内掌握核心技能!
开场:那些年,我们手写API的痛
还记得第一次写API时的场景吗?
# 传统Django视图写API - 繁琐又容易出错
def book_list(request):
if request.method != 'GET':
return JsonResponse({'error': 'Method not allowed'}, status=405)
books = Book.objects.all()
data = []
for book in books:
data.append({
'id': book.id,
'title': book.title,
'author': book.author,
'price': str(book.price) # Decimal要转字符串!
})
return JsonResponse(data, safe=False)
def book_create(request):
if request.method != 'POST':
return JsonResponse({'error': 'Method not allowed'}, status=405)
try:
data = json.loads(request.body)
except:
return JsonResponse({'error': 'Invalid JSON'}, status=400)
# 手动验证每个字段
if 'title' not in data or len(data['title']) == 0:
return JsonResponse({'error': 'Title required'}, status=400)
# 创建对象
book = Book.objects.create(
title=data['title'],
author=data.get('author', ''),
price=Decimal(data.get('price', '0.00'))
)
return JsonResponse({'id': book.id, 'message': 'Created'}, status=201)
痛点清单:
- 重复代码满天飞:每个视图都要处理请求方法判断、JSON解析
- 手动验证繁琐:字段验证、类型转换全要自己写
- 错误处理混乱:不同错误返回格式不一致
- 权限控制缺失:谁来验证用户权限?怎么验证?
- 文档维护困难:改了接口忘了更新文档
效果预览:DRF带来的效率革命
看看用DRF实现同样功能的代码量对比:
# DRF实现 - 5行代码搞定完整CRUD
from rest_framework import viewsets
from .models import Book
from .serializers import BookSerializer
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
是的,你没看错!5行代码实现完整的列表、详情、创建、更新、删除功能,还包括:
- 自动的请求验证
- 智能的错误返回
- 可浏览的API界面
- 内置的分页功能
- 灵活的权限控制
第一部分:环境搭建与项目初始化
1.1 创建虚拟环境并安装依赖
# 创建虚拟环境
python -m venv venv
# 激活虚拟环境(Linux/Mac)
source venv/bin/activate
# 激活虚拟环境(Windows)
venv\Scripts\activate
# 安装核心依赖
pip install django==5.1 djangorestframework==3.16
# 安装可选但强烈推荐的扩展
pip install django-filter # 过滤支持
pip install djangorestframework-simplejwt # JWT认证
pip install drf-spectacular # OpenAPI文档生成
1.2 创建Django项目和应用
# 创建项目
django-admin startproject bookstore_api
cd bookstore_api
# 创建API应用
python manage.py startapp api
# 创建用户管理应用(可选)
python manage.py startapp accounts
1.3 配置基础设置
打开 bookstore_api/settings.py,进行以下配置:
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 第三方应用
'rest_framework',
'rest_framework.authtoken', # Token认证
'django_filters', # 过滤支持
'drf_spectacular', # API文档
# 本地应用
'api.apps.ApiConfig',
'accounts.apps.AccountsConfig',
]
# DRF全局配置
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
],
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
# 数据库配置(使用SQLite示例)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
第二部分:数据模型设计 - 构建电商系统核心
2.1 设计产品模型
在 api/models.py 中创建电商系统核心模型:
# api/models.py
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MinValueValidator, MaxValueValidator
class Category(models.Model):
"""商品分类"""
name = models.CharField(max_length=100, unique=True, verbose_name="分类名称")
description = models.TextField(blank=True, verbose_name="分类描述")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "商品分类"
verbose_name_plural = verbose_name
ordering = ['name']
def __str__(self):
return self.name
class Product(models.Model):
"""商品模型"""
STATUS_CHOICES = [
('draft', '草稿'),
('published', '已发布'),
('out_of_stock', '缺货'),
('discontinued', '已下架'),
]
name = models.CharField(max_length=200, verbose_name="商品名称")
slug = models.SlugField(max_length=200, unique=True, verbose_name="URL标识")
description = models.TextField(verbose_name="商品描述")
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="价格")
category = models.ForeignKey(
Category,
on_delete=models.PROTECT,
related_name='products',
verbose_name="商品分类"
)
stock_quantity = models.PositiveIntegerField(default=0, verbose_name="库存数量")
image = models.ImageField(upload_to='products/', blank=True, verbose_name="商品图片")
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='draft',
verbose_name="商品状态"
)
created_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
related_name='products_created',
verbose_name="创建者"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "商品"
verbose_name_plural = verbose_name
ordering = ['-created_at']
indexes = [
models.Index(fields=['slug']),
models.Index(fields=['name']),
models.Index(fields=['status']),
]
def __str__(self):
return f"{self.name} (¥{self.price})"
def is_available(self):
"""检查商品是否可购买"""
return self.status == 'published' and self.stock_quantity > 0
class ProductImage(models.Model):
"""商品多图展示"""
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='images'
)
image = models.ImageField(upload_to='product_images/')
caption = models.CharField(max_length=200, blank=True)
display_order = models.PositiveIntegerField(default=0)
class Meta:
ordering = ['display_order', 'id']
class Review(models.Model):
"""商品评价"""
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='reviews'
)
user = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.PositiveIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)]
)
title = models.CharField(max_length=200)
content = models.TextField()
is_approved = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
unique_together = ['product', 'user'] # 每个用户只能评价一次
class Order(models.Model):
"""订单模型"""
STATUS_CHOICES = [
('pending', '待支付'),
('paid', '已支付'),
('shipped', '已发货'),
('delivered', '已送达'),
('cancelled', '已取消'),
('refunded', '已退款'),
]
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders')
order_number = models.CharField(max_length=20, unique=True)
total_amount = models.DecimalField(max_digits=12, decimal_places=2)
shipping_address = models.TextField()
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
notes = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return f"订单 {self.order_number} - {self.user.username}"
class OrderItem(models.Model):
"""订单项"""
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
product = models.ForeignKey(Product, on_delete=models.PROTECT)
quantity = models.PositiveIntegerField()
unit_price = models.DecimalField(max_digits=10, decimal_places=2)
class Meta:
ordering = ['id']
@property
def subtotal(self):
return self.quantity * self.unit_price
2.2 生成数据库迁移并应用
# 生成迁移文件
python manage.py makemigrations api
# 应用迁移
python manage.py migrate
# 创建超级用户(用于后台管理)
python manage.py createsuperuser
第三部分:序列化器 - DRF的灵魂
3.1 创建基础序列化器
在 api/serializers.py 中:
# api/serializers.py
from rest_framework import serializers
from .models import Category, Product, Review, Order, OrderItem
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
"""用户序列化器"""
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name']
read_only_fields = ['id']
class CategorySerializer(serializers.ModelSerializer):
"""分类序列化器"""
product_count = serializers.IntegerField(
source='products.count',
read_only=True
)
class Meta:
model = Category
fields = ['id', 'name', 'description', 'product_count', 'created_at']
read_only_fields = ['id', 'created_at']
class ProductImageSerializer(serializers.ModelSerializer):
"""商品图片序列化器"""
class Meta:
model = ProductImage
fields = ['id', 'image', 'caption', 'display_order']
read_only_fields = ['id']
class ProductSerializer(serializers.ModelSerializer):
"""商品序列化器"""
# 关联字段展示
category_name = serializers.CharField(
source='category.name',
read_only=True
)
created_by_username = serializers.CharField(
source='created_by.username',
read_only=True
)
# 嵌套序列化器
images = ProductImageSerializer(many=True, read_only=True)
average_rating = serializers.FloatField(read_only=True)
review_count = serializers.IntegerField(read_only=True)
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'description', 'price',
'category', 'category_name', 'stock_quantity',
'image', 'status', 'created_by', 'created_by_username',
'created_at', 'updated_at', 'images',
'average_rating', 'review_count'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'slug']
extra_kwargs = {
'category': {'write_only': True},
'created_by': {'write_only': True},
}
def validate_price(self, value):
"""价格验证"""
if value <= 0:
raise serializers.ValidationError("价格必须大于0")
return value
def validate_stock_quantity(self, value):
"""库存验证"""
if value < 0:
raise serializers.ValidationError("库存数量不能为负数")
return value
def create(self, validated_data):
"""创建商品时自动设置创建者"""
request = self.context.get('request')
if request and request.user.is_authenticated:
validated_data['created_by'] = request.user
return super().create(validated_data)
class ReviewSerializer(serializers.ModelSerializer):
"""评价序列化器"""
user_username = serializers.CharField(source='user.username', read_only=True)
user_email = serializers.EmailField(source='user.email', read_only=True)
class Meta:
model = Review
fields = [
'id', 'product', 'user', 'user_username', 'user_email',
'rating', 'title', 'content', 'is_approved',
'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'user']
def validate_rating(self, value):
"""评分验证"""
if value < 1 or value > 5:
raise serializers.ValidationError("评分必须在1-5之间")
return value
def create(self, validated_data):
"""创建评价时自动设置用户"""
request = self.context.get('request')
if request and request.user.is_authenticated:
validated_data['user'] = request.user
return super().create(validated_data)
class OrderItemSerializer(serializers.ModelSerializer):
"""订单项序列化器"""
product_name = serializers.CharField(source='product.name', read_only=True)
product_price = serializers.DecimalField(
source='product.price',
max_digits=10,
decimal_places=2,
read_only=True
)
subtotal = serializers.DecimalField(
max_digits=12,
decimal_places=2,
read_only=True
)
class Meta:
model = OrderItem
fields = [
'id', 'product', 'product_name', 'product_price',
'quantity', 'unit_price', 'subtotal'
]
read_only_fields = ['id', 'subtotal']
class OrderSerializer(serializers.ModelSerializer):
"""订单序列化器"""
items = OrderItemSerializer(many=True)
user_username = serializers.CharField(source='user.username', read_only=True)
class Meta:
model = Order
fields = [
'id', 'order_number', 'user', 'user_username',
'total_amount', 'shipping_address', 'status',
'notes', 'created_at', 'updated_at', 'items'
]
read_only_fields = ['id', 'order_number', 'created_at', 'updated_at']
def create(self, validated_data):
"""创建订单及其关联项"""
items_data = validated_data.pop('items')
order = Order.objects.create(**validated_data)
for item_data in items_data:
OrderItem.objects.create(order=order, **item_data)
return order
3.2 高级序列化技巧
# serializers/advanced.py
from rest_framework import serializers
from .models import Product
class DynamicFieldsSerializer(serializers.ModelSerializer):
"""动态字段序列化器"""
def __init__(self, *args, **kwargs):
# 获取请求中指定的字段
fields = kwargs.pop('fields', None)
super().__init__(*args, **kwargs)
if fields:
# 删除未指定的字段
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
class ProductSummarySerializer(DynamicFieldsSerializer):
"""商品摘要序列化器"""
class Meta:
model = Product
fields = ['id', 'name', 'price', 'image', 'status']
class ProductDetailSerializer(ProductSerializer):
"""商品详情序列化器(继承完整功能)"""
# 可以在这里添加额外的字段或覆盖方法
related_products = serializers.SerializerMethodField()
def get_related_products(self, obj):
"""获取相关商品"""
# 获取同一分类的其他商品
from .serializers import ProductSummarySerializer
related = Product.objects.filter(
category=obj.category
).exclude(id=obj.id)[:4]
return ProductSummarySerializer(
related,
many=True,
context=self.context
).data
class Meta(ProductSerializer.Meta):
fields = ProductSerializer.Meta.fields + ['related_products']
第四部分:视图与视图集 - API的核心控制器
4.1 基础APIView视图
# api/views/basic_views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from ..models import Product
from ..serializers import ProductSerializer
class ProductListAPIView(APIView):
"""商品列表API(传统方式)"""
def get(self, request, format=None):
"""获取商品列表"""
products = Product.objects.filter(status='published')
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
def post(self, request, format=None):
"""创建商品"""
serializer = ProductSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ProductDetailAPIView(APIView):
"""商品详情API"""
def get_object(self, pk):
"""获取商品对象"""
try:
return Product.objects.get(pk=pk, status='published')
except Product.DoesNotExist:
return None
def get(self, request, pk, format=None):
"""获取商品详情"""
product = self.get_object(pk)
if product is None:
return Response(
{'detail': '商品不存在'},
status=status.HTTP_404_NOT_FOUND
)
serializer = ProductSerializer(product)
return Response(serializer.data)
4.2 通用视图(Generic Views)
# api/views/generic_views.py
from rest_framework import generics
from ..models import Product, Review
from ..serializers import ProductSerializer, ReviewSerializer
class ProductListCreateView(generics.ListCreateAPIView):
"""商品列表创建视图"""
queryset = Product.objects.filter(status='published')
serializer_class = ProductSerializer
def perform_create(self, serializer):
"""创建时设置创建者"""
serializer.save(created_by=self.request.user)
class ProductRetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
"""商品详情更新删除视图"""
queryset = Product.objects.filter(status='published')
serializer_class = ProductSerializer
lookup_field = 'slug' # 使用slug而不是id
def perform_update(self, serializer):
"""更新时记录操作者"""
serializer.save(updated_by=self.request.user)
class ReviewListCreateView(generics.ListCreateAPIView):
"""评价列表创建视图"""
serializer_class = ReviewSerializer
def get_queryset(self):
"""根据商品过滤评价"""
product_slug = self.kwargs['product_slug']
return Review.objects.filter(
product__slug=product_slug,
is_approved=True
)
def perform_create(self, serializer):
"""创建评价时自动关联商品"""
product_slug = self.kwargs['product_slug']
product = Product.objects.get(slug=product_slug)
serializer.save(product=product, user=self.request.user)
4.3 视图集(ViewSets) - DRF的终极武器
# api/views/viewset_views.py
from rest_framework import viewsets, mixins, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from ..models import Product, Category, Review, Order
from ..serializers import (
ProductSerializer, CategorySerializer,
ReviewSerializer, OrderSerializer,
ProductDetailSerializer
)
from .permissions import IsOwnerOrReadOnly, IsAdminOrReadOnly
class CategoryViewSet(viewsets.ModelViewSet):
"""分类视图集"""
queryset = Category.objects.all()
serializer_class = CategorySerializer
permission_classes = [IsAdminOrReadOnly] # 仅管理员可写
@action(detail=True, methods=['get'])
def products(self, request, pk=None):
"""获取分类下的商品"""
category = self.get_object()
products = category.products.filter(status='published')
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
class ProductViewSet(viewsets.ModelViewSet):
"""商品视图集 - 完整CRUD功能"""
queryset = Product.objects.filter(status='published')
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['category', 'status']
search_fields = ['name', 'description']
ordering_fields = ['price', 'created_at', 'stock_quantity']
ordering = ['-created_at']
lookup_field = 'slug'
def get_serializer_class(self):
"""动态选择序列化器"""
if self.action == 'retrieve':
return ProductDetailSerializer
return ProductSerializer
def get_queryset(self):
"""优化查询性能"""
queryset = super().get_queryset()
# 预取关联数据
return queryset.select_related(
'category', 'created_by'
).prefetch_related(
'images', 'reviews'
)
@action(detail=True, methods=['get'])
def reviews(self, request, slug=None):
"""获取商品的评价"""
product = self.get_object()
reviews = product.reviews.filter(is_approved=True)
page = self.paginate_queryset(reviews)
if page is not None:
serializer = ReviewSerializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = ReviewSerializer(reviews, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def add_review(self, request, slug=None):
"""添加评价"""
product = self.get_object()
serializer = ReviewSerializer(data=request.data)
if serializer.is_valid():
# 检查是否已评价过
if product.reviews.filter(user=request.user).exists():
return Response(
{'detail': '您已经评价过此商品'},
status=status.HTTP_400_BAD_REQUEST
)
serializer.save(product=product, user=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@action(detail=True, methods=['get'])
def similar_products(self, request, slug=None):
"""获取相似商品"""
product = self.get_object()
similar = Product.objects.filter(
category=product.category
).exclude(id=product.id).filter(status='published')[:6]
serializer = ProductSerializer(similar, many=True)
return Response(serializer.data)
class ReviewViewSet(viewsets.ModelViewSet):
"""评价视图集"""
serializer_class = ReviewSerializer
def get_queryset(self):
"""只返回当前用户的评价"""
if self.request.user.is_staff:
return Review.objects.all()
return Review.objects.filter(user=self.request.user)
def perform_create(self, serializer):
"""创建时自动设置用户"""
serializer.save(user=self.request.user)
class OrderViewSet(viewsets.ModelViewSet):
"""订单视图集"""
serializer_class = OrderSerializer
def get_queryset(self):
"""用户只能查看自己的订单"""
if self.request.user.is_staff:
return Order.objects.all()
return Order.objects.filter(user=self.request.user)
def perform_create(self, serializer):
"""创建订单时自动生成订单号"""
import uuid
from django.utils import timezone
order_number = f"ORD-{timezone.now().strftime('%Y%m%d')}-{str(uuid.uuid4())[:8]}"
serializer.save(user=self.request.user, order_number=order_number)
@action(detail=True, methods=['post'])
def cancel(self, request, pk=None):
"""取消订单"""
order = self.get_object()
if order.status not in ['pending', 'paid']:
return Response(
{'detail': '只有待支付或已支付的订单可以取消'},
status=status.HTTP_400_BAD_REQUEST
)
order.status = 'cancelled'
order.save()
return Response({'detail': '订单已取消'})
第五部分:权限与认证 - 保护你的API
5.1 自定义权限类
# api/permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
对象级权限,只允许对象的所有者编辑它。
假设模型实例有一个 `user` 属性。
"""
def has_object_permission(self, request, view, obj):
# 读取权限允许任何请求,
# 所以我们总是允许 GET, HEAD 或 OPTIONS 请求。
if request.method in permissions.SAFE_METHODS:
return True
# 实例必须有一个名为 `user` 的属性。
# 如果没有,回退到检查其他常见属性名
if hasattr(obj, 'user'):
return obj.user == request.user
elif hasattr(obj, 'owner'):
return obj.owner == request.user
elif hasattr(obj, 'created_by'):
return obj.created_by == request.user
return False
class IsAdminOrReadOnly(permissions.BasePermission):
"""管理员可写,其他人只读"""
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return request.user and request.user.is_staff
class IsStaffOrOwner(permissions.BasePermission):
"""员工可访问所有,普通用户只能访问自己的"""
def has_object_permission(self, request, view, obj):
if request.user.is_staff:
return True
# 检查各种可能的用户关联属性
if hasattr(obj, 'user'):
return obj.user == request.user
elif hasattr(obj, 'owner'):
return obj.owner == request.user
elif hasattr(obj, 'created_by'):
return obj.created_by == request.user
return False
class IsReviewAuthorOrAdmin(permissions.BasePermission):
"""评价作者或管理员可修改"""
def has_object_permission(self, request, view, obj):
# 安全方法允许任何人访问
if request.method in permissions.SAFE_METHODS:
return True
# 只有评价作者或管理员可以修改/删除
return obj.user == request.user or request.user.is_staff
class IsProductOwnerOrAdmin(permissions.BasePermission):
"""商品创建者或管理员可修改"""
def has_object_permission(self, request, view, obj):
# 安全方法允许任何人访问
if request.method in permissions.SAFE_METHODS:
return True
# 检查是否是创建者或管理员
if hasattr(obj, 'created_by'):
return obj.created_by == request.user or request.user.is_staff
return request.user.is_staff
5.2 认证配置
# api/authentication.py
from rest_framework.authentication import TokenAuthentication
from rest_framework.exceptions import AuthenticationFailed
from django.utils import timezone
from datetime import timedelta
class ExpiringTokenAuthentication(TokenAuthentication):
"""带过期时间的Token认证"""
def authenticate_credentials(self, key):
model = self.get_model()
try:
token = model.objects.select_related('user').get(key=key)
except model.DoesNotExist:
raise AuthenticationFailed('无效的Token')
if not token.user.is_active:
raise AuthenticationFailed('用户已禁用')
# 检查Token是否过期(例如7天)
if token.created < timezone.now() - timedelta(days=7):
token.delete()
raise AuthenticationFailed('Token已过期')
return (token.user, token)
5.3 JWT认证配置
# settings.py 中添加
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(hours=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=7),
'SLIDING_TOKEN_LIFETIME': timedelta(hours=1),
'SLIDING_TOKEN_REFRESH_LIFETIME_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME_CLAIM': 'exp',
}
第六部分:路由配置 - 连接一切
6.1 主路由配置
# bookstore_api/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
from api.views import (
ProductViewSet, CategoryViewSet,
ReviewViewSet, OrderViewSet
)
# 创建路由器并注册视图集
router = DefaultRouter()
router.register(r'products', ProductViewSet, basename='product')
router.register(r'categories', CategoryViewSet, basename='category')
router.register(r'reviews', ReviewViewSet, basename='review')
router.register(r'orders', OrderViewSet, basename='order')
urlpatterns = [
# Django后台
path('admin/', admin.site.urls),
# API路由
path('api/', include(router.urls)),
# JWT认证
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
# API文档
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
path('api/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
# DRF内置的登录视图(用于可浏览API)
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
6.2 嵌套路由示例
# 如果需要嵌套路由(如 /api/categories/1/products/)
# 可以使用 drf-nested-routers 库
# 安装
# pip install drf-nested-routers
# 配置示例
from rest_framework_nested import routers
router = routers.DefaultRouter()
router.register(r'categories', CategoryViewSet, basename='category')
# 创建嵌套路由器
categories_router = routers.NestedSimpleRouter(
router, r'categories', lookup='category'
)
categories_router.register(
r'products', ProductViewSet, basename='category-products'
)
urlpatterns = [
path('api/', include(router.urls)),
path('api/', include(categories_router.urls)),
]
第七部分:过滤器与分页 - 优化数据查询
7.1 自定义过滤器
# api/filters.py
import django_filters
from .models import Product
class ProductFilter(django_filters.FilterSet):
"""商品过滤器"""
min_price = django_filters.NumberFilter(
field_name='price',
lookup_expr='gte'
)
max_price = django_filters.NumberFilter(
field_name='price',
lookup_expr='lte'
)
category_name = django_filters.CharFilter(
field_name='category__name',
lookup_expr='icontains'
)
search = django_filters.CharFilter(method='filter_search')
class Meta:
model = Product
fields = ['category', 'status']
def filter_search(self, queryset, name, value):
"""综合搜索过滤器"""
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) |
Q(category__name__icontains=value)
)
7.2 自定义分页器
# api/pagination.py
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class ProductPagination(PageNumberPagination):
"""商品分页器"""
page_size = 12 # 每页显示数量
page_size_query_param = 'page_size' # 允许客户端指定每页数量
max_page_size = 100 # 每页最大数量
def get_paginated_response(self, data):
"""自定义分页响应格式"""
return Response({
'links': {
'next': self.get_next_link(),
'previous': self.get_previous_link()
},
'count': self.page.paginator.count,
'total_pages': self.page.paginator.num_pages,
'current_page': self.page.number,
'results': data
})
class ReviewPagination(PageNumberPagination):
"""评价分页器"""
page_size = 10
page_size_query_param = 'page_size'
max_page_size = 50
第八部分:API文档与测试
8.1 自动生成OpenAPI文档
# settings.py 中添加
SPECTACULAR_SETTINGS = {
'TITLE': '电商平台API文档',
'DESCRIPTION': '完整的电商平台后端API接口文档',
'VERSION': '1.0.0',
'SERVE_INCLUDE_SCHEMA': False,
# 可选配置
'SCHEMA_PATH_PREFIX': r'/api/',
'COMPONENT_SPLIT_REQUEST': True,
'SWAGGER_UI_SETTINGS': {
'deepLinking': True,
'persistAuthorization': True,
},
}
8.2 使用DRF的测试客户端
python
# tests/test_api.py
from django.test import TestCase
from django.contrib.auth.models import User
from rest_framework.test import APIClient
from rest_framework import status
from api.models import Category, Product
class ProductAPITestCase(TestCase):
def setUp(self):
"""测试前置设置"""
self.client = APIClient()
# 创建测试用户
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
)
# 创建测试分类
self.category = Category.objects.create(
name='测试分类',
description='测试分类描述'
)
# 创建测试商品
self.product = Product.objects.create(
name='测试商品',
slug='test-product',
description='测试商品描述',
price=99.99,
category=self.category,
created_by=self.user
)
def test_unauthenticated_access(self):
"""测试未认证访问"""
response = self.client.get('/api/products/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_authenticated_access(self):
"""测试认证访问"""
self.client.force_authenticate(user=self.user)
response = self.client.get('/api/products/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_create_product(self):
"""测试创建商品"""
self.client.force_authenticate(user=self.user)
data = {
'name': '新商品',
'description': '新商品描述',
'price': 199.99,
'category': self.category.id,
'stock_quantity': 50
}
response = self.client.post('/api/products/', data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Product.objects.count(), 2)
def test_update_product(self):
"""测试更新商品"""
self.client.force_authenticate(user=self.user)
data = {
'name': '更新后的商品',
'price': 299.99
}
response = self.client.patch(
f'/api/products/{self.product.slug}/',
data,
format='json'
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
# 刷新数据库对象
self.product.refresh_from_db()
self.assertEqual(self.product.name, '更新后的商品')
self.assertEqual(float(self.product.price), 299.99)
def test_delete_product(self):
"""测试删除商品"""
self.client.force_authenticate(user=self.user)
response = self.client.delete(
f'/api/products/{self.product.slug}/'
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(Product.objects.count(), 0)
第九部分:性能优化与部署
9.1 查询优化技巧
# 查询优化示例
class OptimizedProductViewSet(viewsets.ModelViewSet):
"""优化后的商品视图集"""
def get_queryset(self):
"""优化查询性能"""
queryset = Product.objects.filter(status='published')
# 使用select_related减少查询次数
queryset = queryset.select_related('category', 'created_by')
# 使用prefetch_related优化多对多关系
queryset = queryset.prefetch_related(
'images',
'reviews'
)
# 使用annotate添加计算字段
from django.db.models import Avg, Count
queryset = queryset.annotate(
average_rating=Avg('reviews__rating'),
review_count=Count('reviews')
)
return queryset
@action(detail=False, methods=['get'])
def expensive_operation(self, request):
"""优化复杂查询示例"""
# 不好的做法:N+1查询
# products = Product.objects.all()
# 每个product都会触发一次数据库查询获取category
# 好的做法:预取关联数据
products = Product.objects.select_related('category').all()
# 如果需要统计信息,使用aggregate
from django.db.models import Sum
stats = Product.objects.aggregate(
total_value=Sum('price'),
average_price=Avg('price')
)
return Response({
'products': ProductSerializer(products, many=True).data,
'stats': stats
})
9.2 缓存策略
# api/caching.py
from django.core.cache import cache
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework import viewsets
class CachedProductViewSet(viewsets.ModelViewSet):
"""带缓存的商品视图集"""
@method_decorator(cache_page(60 * 15)) # 缓存15分钟
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
def get_object(self):
"""缓存单个商品"""
cache_key = f'product_{self.kwargs["slug"]}'
product = cache.get(cache_key)
if product is None:
product = super().get_object()
# 缓存1小时
cache.set(cache_key, product, 60 * 60)
return product
第十部分:实战项目 - 完整电商API系统
10.1 项目结构
bookstore_api/
├── api/
│ ├── migrations/
│ ├── filters.py # 自定义过滤器
│ ├── models.py # 数据模型
│ ├── permissions.py # 权限类
│ ├── serializers.py # 序列化器
│ ├── views/
│ │ ├── __init__.py
│ │ ├── basic_views.py # 基础视图
│ │ ├── generic_views.py # 通用视图
│ │ └── viewset_views.py # 视图集
│ └── tests/
│ └── test_api.py # API测试
├── accounts/
│ └── views.py # 用户认证视图
├── bookstore_api/
│ ├── settings.py # 项目设置
│ ├── urls.py # 主路由
│ └── wsgi.py
├── manage.py
└── requirements.txt
10.2 核心功能清单
- 用户管理:注册、登录、Token认证、JWT支持
- 商品管理:CRUD操作、状态控制、图片上传
- 分类管理:树状分类、商品统计
- 评价系统:评分、评论、审核机制
- 订单系统:创建订单、状态流转、取消退款
- 搜索过滤:关键词搜索、价格范围、分类筛选
- 分页支持:自定义分页、每页数量控制
- 权限控制:对象级权限、管理员权限
- 性能优化:查询优化、缓存策略
- API文档:自动生成OpenAPI文档
第十一部分:总结与进阶
11.1 DRF的核心优势总结
- 开发效率:5行代码实现完整CRUD,减少70%重复代码
- 功能完备:认证、权限、过滤、分页、限流一应俱全
- 性能优越:智能查询优化,支持缓存策略
- 生态丰富:300+第三方插件,企业级解决方案完善
- 文档友好:自动生成可交互API文档,降低协作成本
11.2 常见问题解决方案
问题1:如何实现复杂的业务逻辑?
- 解决方案:在视图中重写
perform_create(),perform_update()等方法
问题2:如何处理大量数据的性能问题?
- 解决方案:使用
select_related(),prefetch_related()优化查询,配合缓存
问题3:如何保证API的安全性?
- 解决方案:使用DRF内置的认证、权限系统,配合HTTPS传输
11.3 进阶学习路径
-
DRF高级特性:
- 自定义认证后端
- 复杂序列化场景
- 嵌套路由与视图集
-
性能优化:
- 数据库查询优化
- 缓存策略设计
- 异步任务处理
-
微服务架构:
- API网关设计
- 服务发现与注册
- 分布式事务处理
-
生产部署:
- Docker容器化
- Kubernetes编排
- 监控与日志收集
行动号召:立即开始你的DRF之旅!
看到这里,你已经掌握了Django REST Framework的核心知识和实战技能。现在,是时候动手实践了:
三步立即开始:
- 复制项目代码:将本文的完整代码保存到本地
- 运行项目:按照环境搭建步骤,5分钟内启动项目
- 扩展功能:尝试添加购物车、支付、物流等模块
学习资源推荐:
立即行动,用DRF打造你的第一个企业级API项目!
本文基于Django REST Framework 3.16和Django 5.1编写,所有代码经过实际测试,可直接运行。
如果你在实践过程中遇到任何问题,欢迎在评论区留言,我会及时解答。