本文目录导读:
- 环境搭建与项目初始化
- 配置 settings.py
- 定义模型 (Models)
- 序列化器 (Serializers)
- 视图 (Views)
- 路由配置 (URLs)
- 认证与权限
- 权限控制进阶
- 高级特性
- 测试你的 API
- 十一、部署前检查清单
- 十二、快速启动模板(完整版)
环境搭建与项目初始化
# 1. 创建虚拟环境 python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate # 2. 安装依赖 pip install django djangorestframework django-filter django-cors-headers # 3. 创建项目 django-admin startproject myapi cd myapi # 4. 创建应用 python manage.py startapp blog
配置 settings.py
# myapi/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
# ... 其他默认应用
'rest_framework',
'django_filters',
'corsheaders',
'blog', # 你的应用
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # 放在最前面
# ... 其他中间件
]
# CORS 配置
CORS_ALLOW_ALL_ORIGINS = True # 开发环境开放,生产环境请限制域名
# DRF 全局配置
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],
}
定义模型 (Models)
# blog/models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Article(models.Model):
STATUS_CHOICES = [
('draft', '草稿'),
('published', '已发布'),
]
= models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='articles')
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='articles')
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
# blog/admin.py from django.contrib import admin from .models import Category, Article admin.site.register(Category) admin.site.register(Article) # 创建迁移并执行 # python manage.py makemigrations # python manage.py migrate
序列化器 (Serializers)
# blog/serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import Category, Article
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name']
class CategorySerializer(serializers.ModelSerializer):
article_count = serializers.SerializerMethodField()
class Meta:
model = Category
fields = '__all__'
read_only_fields = ['created_at']
def get_article_count(self, obj):
return obj.articles.count()
class ArticleSerializer(serializers.ModelSerializer):
author_name = serializers.CharField(source='author.username', read_only=True)
category_name = serializers.CharField(source='category.name', read_only=True)
class Meta:
model = Article
fields = '__all__'
# 只读字段
read_only_fields = ['author', 'created_at', 'updated_at']
# 自定义验证
def validate_title(self, value):
if len(value) < 5:
raise serializers.ValidationError("标题至少5个字符")
return value
def validate(self, data):
if data['status'] == 'published' and not data.get('content'):
raise serializers.ValidationError("发布状态必须有内容")
return data
# 嵌套序列化器
class ArticleDetailSerializer(serializers.ModelSerializer):
author = UserSerializer(read_only=True)
category = CategorySerializer(read_only=True)
class Meta:
model = Article
fields = '__all__'
视图 (Views)
基于函数的视图 (FBV)
# blog/views_fbv.py
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from .models import Article
from .serializers import ArticleSerializer
@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticatedOrReadOnly])
def article_list(request):
if request.method == 'GET':
articles = Article.objects.filter(status='published')
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
serializer.save(author=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET', 'PUT', 'PATCH', 'DELETE'])
@permission_classes([IsAuthenticated])
def article_detail(request, pk):
try:
article = Article.objects.get(pk=pk)
except Article.DoesNotExist:
return Response({'error': '文章不存在'}, status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = ArticleSerializer(article)
return Response(serializer.data)
elif request.method in ['PUT', 'PATCH']:
partial = request.method == 'PATCH'
serializer = ArticleSerializer(article, data=request.data, partial=partial)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
article.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
基于类的视图 (CBV)
# blog/views.py
from rest_framework import generics, viewsets, filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAuthenticated
from .models import Article, Category
from .serializers import ArticleSerializer, ArticleDetailSerializer, CategorySerializer
# 1. 通用视图 (推荐新手使用)
class ArticleListCreateView(generics.ListCreateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
class ArticleRetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
queryset = Article.objects.all()
serializer_class = ArticleDetailSerializer
permission_classes = [IsAuthenticated]
# 2. ViewSet (推荐进阶使用)
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['name']
search_fields = ['name', 'description']
ordering_fields = ['name', 'created_at']
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['status', 'category', 'author']
search_fields = ['title', 'content']
ordering_fields = ['created_at', 'updated_at']
def get_serializer_class(self):
if self.action in ['retrieve', 'update', 'partial_update']:
return ArticleDetailSerializer
return ArticleSerializer
def perform_create(self, serializer):
serializer.save(author=self.request.user)
# 自定义动作
def get_queryset(self):
queryset = Article.objects.all()
if self.request.user.is_authenticated:
# 作者可以看到自己的草稿
return queryset.filter(status='published') | queryset.filter(author=self.request.user)
return queryset.filter(status='published')
路由配置 (URLs)
# blog/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ArticleListCreateView, ArticleRetrieveUpdateDestroyView
from .views import ArticleViewSet, CategoryViewSet
# 方式一:通用视图路由
urlpatterns_generic = [
path('articles/', ArticleListCreateView.as_view(), name='article-list'),
path('articles/<int:pk>/', ArticleRetrieveUpdateDestroyView.as_view(), name='article-detail'),
]
# 方式二:ViewSet 自动路由(推荐)
router = DefaultRouter()
router.register(r'articles', ArticleViewSet, basename='article')
router.register(r'categories', CategoryViewSet, basename='category')
urlpatterns = [
path('api/', include(router.urls)), # 或者使用 urlpatterns_generic
path('api-auth/', include('rest_framework.urls')), # DRF 登录页面
]
# myapi/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
]
认证与权限
Token 认证(推荐用于前后端分离)
pip install djangorestframework-simplejwt
# myapi/settings.py
INSTALLED_APPS = [
# ...
'rest_framework_simplejwt',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'AUTH_HEADER_TYPES': ('Bearer',),
}
# myapi/urls.py
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
# ...
]
权限控制进阶
# blog/permissions.py
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
"""只有作者才能编辑自己的文章"""
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user
class IsAdminUserOrSuperUser(permissions.BasePermission):
"""管理员或超级管理员"""
def has_permission(self, request, view):
return request.user.is_staff or request.user.is_superuser
高级特性
分页自定义
# blog/pagination.py
from rest_framework.pagination import PageNumberPagination
class CustomPagination(PageNumberPagination):
page_size = 20
page_size_query_param = 'page_size'
max_page_size = 100
page_query_param = 'page'
过滤与搜索
# blog/filters.py
import django_filters
from .models import Article
class ArticleFilter(django_filters.FilterSet):= django_filters.CharFilter(lookup_expr='icontains')
created_after = django_filters.DateTimeFilter(field_name='created_at', lookup_expr='gte')
created_before = django_filters.DateTimeFilter(field_name='created_at', lookup_expr='lte')
author_name = django_filters.CharFilter(field_name='author__username', lookup_expr='icontains')
class Meta:
model = Article
fields = ['status', 'category', 'title', 'author_name']
限流 (Throttling)
# myapi/settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day',
'burst': '60/min',
'sustained': '1000/day'
}
}
测试你的 API
# blog/tests.py
from django.test import TestCase
from rest_framework.test import APIClient
from django.contrib.auth.models import User
from .models import Article
class ArticleAPITestCase(TestCase):
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user(username='testuser', password='testpass123')
self.article = Article.objects.create(
title='测试文章',
content='测试内容',
author=self.user,
status='published'
)
def test_get_articles(self):
response = self.client.get('/api/articles/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['results']), 1)
def test_create_article_authenticated(self):
self.client.force_authenticate(user=self.user)
data = {
'title': '新文章',
'content': '内容',
'status': 'draft'
}
response = self.client.post('/api/articles/', data)
self.assertEqual(response.status_code, 201)
def test_create_article_unauthenticated(self):
data = {'title': '新文章', 'content': '内容'}
response = self.client.post('/api/articles/', data)
self.assertEqual(response.status_code, 401)
运行测试:
python manage.py test blog
十一、部署前检查清单
- 关闭 Debug 模式:
DEBUG = False - 配置允许的 Host:
ALLOWED_HOSTS = ['yourdomain.com'] - 使用 HTTPS:配置 SSL 证书
- 限制 CORS:
CORS_ALLOWED_ORIGINS = [...] - 性能优化:使用缓存、数据库索引、select_related/prefetch_related
- 错误处理:全局异常中间件
- 日志记录:配置 logging 模块
十二、快速启动模板(完整版)
# blog/serializers.py (精简版)
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
read_only_fields = ['author', 'created_at', 'updated_at']
# blog/views.py (精简版)
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.filter(status='published')
serializer_class = ArticleSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
# blog/urls.py (精简版)
router = DefaultRouter()
router.register('articles', ArticleViewSet)
urlpatterns = router.urls
这是 Django 接口开发的标准实操流程,根据项目需求,选择适合的视图方式(推荐 ViewSet),配置认证和权限,添加高级特性即可。
标签: Django接口开发