Django接口开发实操方法?

访客 全栈框架 1

本文目录导读:

  1. 环境搭建与项目初始化
  2. 配置 settings.py
  3. 定义模型 (Models)
  4. 序列化器 (Serializers)
  5. 视图 (Views)
  6. 路由配置 (URLs)
  7. 认证与权限
  8. 权限控制进阶
  9. 高级特性
  10. 测试你的 API
  11. 十一、部署前检查清单
  12. 十二、快速启动模板(完整版)

环境搭建与项目初始化

# 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

十一、部署前检查清单

  1. 关闭 Debug 模式DEBUG = False
  2. 配置允许的 HostALLOWED_HOSTS = ['yourdomain.com']
  3. 使用 HTTPS:配置 SSL 证书
  4. 限制 CORSCORS_ALLOWED_ORIGINS = [...]
  5. 性能优化:使用缓存、数据库索引、select_related/prefetch_related
  6. 错误处理:全局异常中间件
  7. 日志记录:配置 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接口开发

抱歉,评论功能暂时关闭!