1 基础介绍

1.1 快速开始

01.环境安装配置
    a.基础环境要求
        Python版本要求
        Django版本兼容性
        系统依赖说明
        环境要求详情:
        ---
        DRF环境要求:

        Python版本:
        - Python 3.8, 3.9, 3.10, 3.11, 3.12
        - 推荐Python 3.9+

        Django版本:
        - Django 4.2, 5.0, 5.1
        - 推荐Django 4.2 LTS

        系统要求:
        - 内存:最少512MB,推荐2GB+
        - 磁盘:最少1GB可用空间
        - 操作系统:Linux, macOS, Windows

        可选依赖:
        - PostgreSQL: psycopg2-binary
        - MySQL: mysqlclient
        - Redis: redis-py
        - Celery: celery
        - Pillow: 图像处理
        ---
    b.虚拟环境创建
        venv和conda使用
        依赖管理最佳实践
        开发环境配置
        虚拟环境示例:
        ---
        # 使用venv创建虚拟环境
        python -m venv drf_env

        # 激活虚拟环境
        # Linux/macOS:
        source drf_env/bin/activate
        # Windows:
        drf_env\Scripts\activate

        # 升级pip
        pip install --upgrade pip

        # 安装Django和DRF
        pip install django>=4.2,<5.1
        pip install djangorestframework>=3.15.0

        # 安装常用扩展
        pip install djangorestframework-simplejwt
        pip install django-cors-headers
        pip install django-filter
        pip install drf-spectacular

        # 创建项目
        django-admin startproject myproject
        cd myproject

        # 创建应用
        python manage.py startapp users
        python manage.py startapp posts
        ---
    c.项目初始化
        DRF配置设置
        基础项目结构
        验证安装成功
        项目初始化示例:
        ---
        # myproject/settings/base.py
        import os
        from pathlib import Path

        BASE_DIR = Path(__file__).resolve().parent.parent.parent

        SECRET_KEY = os.environ.get('SECRET_KEY', 'your-secret-key-here')

        DEBUG = os.environ.get('DEBUG', 'False').lower() == 'true'

        ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',')

        # 应用配置
        DJANGO_APPS = [
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
        ]

        THIRD_PARTY_APPS = [
            'rest_framework',
            'rest_framework_simplejwt',
            'corsheaders',
            'django_filters',
            'drf_spectacular',
        ]

        LOCAL_APPS = [
            'users',
            'posts',
            'core',
        ]

        INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS

        # 中间件配置
        MIDDLEWARE = [
            'corsheaders.middleware.CorsMiddleware',
            'django.middleware.security.SecurityMiddleware',
            'django.contrib.sessions.middleware.SessionMiddleware',
            'django.middleware.common.CommonMiddleware',
            'django.middleware.csrf.CsrfViewMiddleware',
            'django.contrib.auth.middleware.AuthenticationMiddleware',
            'django.contrib.messages.middleware.MessageMiddleware',
            'django.middleware.clickjacking.XFrameOptionsMiddleware',
        ]

        ROOT_URLCONF = 'myproject.urls'

        TEMPLATES = [
            {
                'BACKEND': 'django.template.backends.django.DjangoTemplates',
                'DIRS': [BASE_DIR / 'templates'],
                'APP_DIRS': True,
                'OPTIONS': {
                    'context_processors': [
                        'django.template.context_processors.debug',
                        'django.template.context_processors.request',
                        'django.contrib.auth.context_processors.auth',
                        'django.contrib.messages.context_processors.messages',
                    ],
                },
            },
        ]

        WSGI_APPLICATION = 'myproject.wsgi.application'
        ASGI_APPLICATION = 'myproject.asgi.application'

        # 数据库配置
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.postgresql',
                'NAME': os.environ.get('DB_NAME', 'drf_db'),
                'USER': os.environ.get('DB_USER', 'postgres'),
                'PASSWORD': os.environ.get('DB_PASSWORD'),
                'HOST': os.environ.get('DB_HOST', 'localhost'),
                'PORT': os.environ.get('DB_PORT', '5432'),
            }
        }

        # 密码验证
        AUTH_PASSWORD_VALIDATORS = [
            {
                'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
            },
            {
                'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
            },
            {
                'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
            },
            {
                'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
            },
        ]

        # 国际化
        LANGUAGE_CODE = 'zh-hans'
        TIME_ZONE = 'Asia/Shanghai'
        USE_I18N = True
        USE_TZ = True

        # 静态文件
        STATIC_URL = '/static/'
        STATIC_ROOT = BASE_DIR / 'staticfiles'
        STATICFILES_DIRS = [BASE_DIR / 'static']

        # 媒体文件
        MEDIA_URL = '/media/'
        MEDIA_ROOT = BASE_DIR / 'media'

        # REST Framework配置
        REST_FRAMEWORK = {
            'DEFAULT_AUTHENTICATION_CLASSES': [
                'rest_framework_simplejwt.authentication.JWTAuthentication',
                'rest_framework.authentication.SessionAuthentication',
            ],
            'DEFAULT_PERMISSION_CLASSES': [
                'rest_framework.permissions.IsAuthenticated',
            ],
            'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
            'PAGE_SIZE': 20,
            'DEFAULT_FILTER_BACKENDS': [
                'django_filters.rest_framework.DjangoFilterBackend',
                'rest_framework.filters.SearchFilter',
                'rest_framework.filters.OrderingFilter',
            ],
            'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
            'SPECTACULAR_SETTINGS': {
                'TITLE': 'DRF API',
                'DESCRIPTION': 'Django REST Framework API文档',
                'VERSION': '1.0.0',
                'SERVE_INCLUDE_SCHEMA': False,
            },
        }

        # JWT配置
        from datetime import timedelta
        SIMPLE_JWT = {
            'ACCESS_TOKEN_LIFETIME': timedelta(hours=24),
            'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
            'ROTATE_REFRESH_TOKENS': True,
            'BLACKLIST_AFTER_ROTATION': True,
            'ALGORITHM': 'HS256',
            'SIGNING_KEY': SECRET_KEY,
        }

        # CORS配置
        CORS_ALLOWED_ORIGINS = [
            "http://localhost:3000",
            "http://127.0.0.1:3000",
        ]

        CORS_ALLOW_CREDENTIALS = True
        ---

02.第一个DRF API
    a.创建基础模型
        定义数据模型
        数据库迁移
        基础数据操作
        基础模型示例:
        ---
        # apps/users/models.py
        from django.contrib.auth.models import AbstractUser
        from django.db import models

        class User(AbstractUser):
            """扩展用户模型"""
            email = models.EmailField('邮箱地址', unique=True)
            phone = models.CharField('手机号码', max_length=20, blank=True)
            avatar = models.ImageField('头像', upload_to='avatars/', blank=True)
            bio = models.TextField('个人简介', max_length=500, blank=True)
            is_verified = models.BooleanField('邮箱已验证', default=False)
            created_at = models.DateTimeField('创建时间', auto_now_add=True)
            updated_at = models.DateTimeField('更新时间', auto_now=True)

            USERNAME_FIELD = 'email'
            REQUIRED_FIELDS = ['username']

            class Meta:
                verbose_name = '用户'
                verbose_name_plural = '用户'

        # apps/posts/models.py
        from django.conf import settings

        class Post(models.Model):
            """文章模型"""
            title = models.CharField('标题', max_length=200)
            content = models.TextField('内容')
            author = models.ForeignKey(
                settings.AUTH_USER_MODEL,
                on_delete=models.CASCADE,
                related_name='posts',
                verbose_name='作者'
            )
            created_at = models.DateTimeField('创建时间', auto_now_add=True)
            updated_at = models.DateTimeField('更新时间', auto_now=True)
            is_published = models.BooleanField('已发布', default=False)

            class Meta:
                verbose_name = '文章'
                verbose_name_plural = '文章'
                ordering = ['-created_at']

            def __str__(self):
                return self.title

        # 生成数据库迁移
        ---
        运行以下命令:
        python manage.py makemigrations
        python manage.py migrate
        python manage.py createsuperuser
        ---

        # apps/core/permissions.py
        from rest_framework.permissions import BasePermission

        class IsOwnerOrReadOnly(BasePermission):
            """只有对象所有者才能编辑"""
            def has_object_permission(self, request, view, obj):
                if request.method in ['GET', 'HEAD', 'OPTIONS']:
                    return True
                return obj.author == request.user
        ---
    b.创建序列化器
        ModelSerializer使用
        字段验证和自定义
        嵌套关系处理
        序列化器示例:
        ---
        # apps/users/serializers.py
        from rest_framework import serializers
        from django.contrib.auth import get_user_model

        User = get_user_model()

        class UserSerializer(serializers.ModelSerializer):
            """用户序列化器"""
            posts_count = serializers.SerializerMethodField()

            class Meta:
                model = User
                fields = [
                    'id', 'username', 'email', 'first_name', 'last_name',
                    'phone', 'avatar', 'bio', 'is_verified',
                    'posts_count', 'created_at'
                ]
                read_only_fields = ['id', 'is_verified', 'created_at']

            def get_posts_count(self, obj):
                """获取用户文章数量"""
                return obj.posts.filter(is_published=True).count()

        class UserCreateSerializer(serializers.ModelSerializer):
            """用户创建序列化器"""
            password = serializers.CharField(write_only=True, min_length=8)
            password2 = serializers.CharField(write_only=True, min_length=8)

            class Meta:
                model = User
                fields = [
                    'username', 'email', 'password', 'password2',
                    'first_name', 'last_name', 'phone'
                ]

            def validate(self, data):
                """验证两次密码是否一致"""
                if data['password'] != data['password2']:
                    raise serializers.ValidationError("两次输入的密码不一致")
                return data

            def create(self, validated_data):
                """创建用户"""
                validated_data.pop('password2')
                user = User.objects.create_user(**validated_data)
                return user

        # apps/posts/serializers.py
        from rest_framework import serializers
        from .models import Post

        class PostSerializer(serializers.ModelSerializer):
            """文章序列化器"""
            author_name = serializers.SerializerMethodField()
            author_avatar = serializers.SerializerMethodField()
            comments_count = serializers.SerializerMethodField()

            class Meta:
                model = Post
                fields = [
                    'id', 'title', 'content', 'author', 'author_name',
                    'author_avatar', 'comments_count', 'created_at',
                    'updated_at', 'is_published'
                ]
                read_only_fields = ['id', 'author', 'created_at', 'updated_at']

            def get_author_name(self, obj):
                """获取作者姓名"""
                return obj.author.get_full_name() or obj.author.username

            def get_author_avatar(self, obj):
                """获取作者头像"""
                if obj.author.avatar:
                    return obj.author.avatar.url
                return None

            def get_comments_count(self, obj):
                """获取评论数量"""
                return obj.comments.count()

        class PostCreateSerializer(serializers.ModelSerializer):
            """文章创建序列化器"""
            class Meta:
                model = Post
                fields = ['title', 'content', 'is_published']

            def create(self, validated_data):
                """创建文章"""
                validated_data['author'] = self.context['request'].user
                return super().create(validated_data)
        ---
    c.创建视图和URL
        APIView和GenericAPIView使用
        路由配置
        响应格式化
        视图和URL示例:
        ---
        # apps/users/views.py
        from rest_framework import status, generics, permissions
        from rest_framework.decorators import api_view, permission_classes
        from rest_framework.response import Response
        from django.contrib.auth import get_user_model
        from .serializers import UserSerializer, UserCreateSerializer
        from .permissions import IsOwnerOrReadOnly

        User = get_user_model()

        @api_view(['GET'])
        @permission_classes([permissions.AllowAny])
        def api_root(request):
            """API根路径"""
            return Response({
                'users': '/api/v1/users/',
                'posts': '/api/v1/posts/',
                'auth': '/api/v1/auth/',
            })

        class UserListCreateView(generics.ListCreateAPIView):
            """用户列表和创建视图"""
            queryset = User.objects.all()
            permission_classes = [permissions.IsAuthenticatedOrReadOnly]

            def get_serializer_class(self):
                """根据请求方法返回不同的序列化器"""
                if self.request.method == 'POST':
                    return UserCreateSerializer
                return UserSerializer

            def get_queryset(self):
                """获取查询集"""
                queryset = User.objects.all()
                # 支持按用户名搜索
                username = self.request.query_params.get('username')
                if username:
                    queryset = queryset.filter(username__icontains=username)
                return queryset

        class UserDetailView(generics.RetrieveUpdateDestroyAPIView):
            """用户详情视图"""
            queryset = User.objects.all()
            serializer_class = UserSerializer
            permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]

        class UserMeView(generics.RetrieveUpdateAPIView):
            """当前用户信息视图"""
            serializer_class = UserSerializer
            permission_classes = [permissions.IsAuthenticated]

            def get_object(self):
                """返回当前用户对象"""
                return self.request.user

        # apps/posts/views.py
        from rest_framework import generics, permissions, filters
        from django_filters.rest_framework import DjangoFilterBackend
        from .models import Post
        from .serializers import PostSerializer, PostCreateSerializer
        from core.permissions import IsOwnerOrReadOnly

        class PostListCreateView(generics.ListCreateAPIView):
            """文章列表和创建视图"""
            queryset = Post.objects.filter(is_published=True)
            permission_classes = [permissions.IsAuthenticatedOrReadOnly]
            filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
            filterset_fields = ['author', 'is_published']
            search_fields = ['title', 'content']
            ordering_fields = ['created_at', 'updated_at', 'title']
            ordering = ['-created_at']

            def get_serializer_class(self):
                """根据请求方法返回不同的序列化器"""
                if self.request.method == 'POST':
                    return PostCreateSerializer
                return PostSerializer

            def get_queryset(self):
                """获取查询集"""
                queryset = Post.objects.all()

                # 如果用户未认证,只显示已发布的文章
                if not self.request.user.is_authenticated:
                    queryset = queryset.filter(is_published=True)

                # 支持按作者过滤
                author_id = self.request.query_params.get('author')
                if author_id:
                    queryset = queryset.filter(author_id=author_id)

                return queryset

            def perform_create(self, serializer):
                """创建文章时设置作者"""
                serializer.save(author=self.request.user)

        class PostDetailView(generics.RetrieveUpdateDestroyAPIView):
            """文章详情视图"""
            queryset = Post.objects.all()
            permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]

            def get_serializer_class(self):
                """根据请求方法返回不同的序列化器"""
                if self.request.method in ['PUT', 'PATCH']:
                    return PostCreateSerializer
                return PostSerializer

        # apps/users/urls.py
        from django.urls import path
        from . import views

        urlpatterns = [
            path('me/', views.UserMeView.as_view(), name='user-me'),
            path('', views.UserListCreateView.as_view(), name='user-list-create'),
            path('<int:pk>/', views.UserDetailView.as_view(), name='user-detail'),
        ]

        # apps/posts/urls.py
        from django.urls import path
        from . import views

        urlpatterns = [
            path('', views.PostListCreateView.as_view(), name='post-list-create'),
            path('<int:pk>/', views.PostDetailView.as_view(), name='post-detail'),
        ]

        # myproject/urls.py
        from django.contrib import admin
        from django.urls import path, include
        from django.conf import settings
        from django.conf.urls.static import static
        from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView

        urlpatterns = [
            path('admin/', admin.site.urls),
            path('api/v1/', include([
                path('', include('core.urls')),
                path('users/', include('users.urls')),
                path('posts/', include('posts.urls')),
                path('auth/', include('apps.authentication.urls')),
            ])),
            # API文档
            path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
            path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
        ]

        # 开发环境静态文件服务
        if settings.DEBUG:
            urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
            urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

        # apps/core/urls.py
        from django.urls import path
        from users.views import api_root

        urlpatterns = [
            path('', api_root, name='api-root'),
        ]
        ---

03.验证和测试
    a.运行开发服务器
        启动Django开发服务器
        测试API端点
        调试工具使用
        运行和测试示例:
        ---
        # 启动开发服务器
        python manage.py runserver

        # 创建超级用户
        python manage.py createsuperuser

        # 运行测试
        python manage.py test

        # 生成API文档
        python manage.py spectacular --file schema.yml
        ---
    b.测试API端点
        使用curl测试
        Postman测试
        DRF Browsable API
        API测试示例:
        ---
        # 1. 测试API根路径
        curl -X GET http://localhost:8000/api/v1/

        # 2. 注册用户
        curl -X POST http://localhost:8000/api/v1/users/ \
          -H "Content-Type: application/json" \
          -d '{
            "username": "testuser",
            "email": "[email protected]",
            "password": "password123",
            "password2": "password123",
            "first_name": "Test",
            "last_name": "User"
          }'

        # 3. 获取JWT token
        curl -X POST http://localhost:8000/api/v1/auth/login/ \
          -H "Content-Type: application/json" \
          -d '{
            "email": "[email protected]",
            "password": "password123"
          }'

        # 4. 创建文章(需要认证)
        curl -X POST http://localhost:8000/api/v1/posts/ \
          -H "Content-Type: application/json" \
          -H "Authorization: Bearer YOUR_JWT_TOKEN" \
          -d '{
            "title": "My First Post",
            "content": "This is the content of my first post.",
            "is_published": true
          }'

        # 5. 获取文章列表
        curl -X GET "http://localhost:8000/api/v1/posts/?search=first"

        # 6. 获取用户文章
        curl -X GET "http://localhost:8000/api/v1/posts/?author=1"

        # 7. 更新文章
        curl -X PUT http://localhost:8000/api/v1/posts/1/ \
          -H "Content-Type: application/json" \
          -H "Authorization: Bearer YOUR_JWT_TOKEN" \
          -d '{
            "title": "Updated Post Title",
            "content": "Updated content",
            "is_published": true
          }'

        # 8. 删除文章
        curl -X DELETE http://localhost:8000/api/v1/posts/1/ \
          -H "Authorization: Bearer YOUR_JWT_TOKEN"
        ---
    c.访问API文档
        Swagger UI界面
        API Schema查看
        交互式API测试
        文档访问地址:
        ---
        DRF API文档访问地址:

        Swagger UI(交互式文档)
        - URL: http://localhost:8000/api/docs/
        - 功能:可以直接在浏览器中测试API
        - 支持认证、请求参数配置

        Schema API(机器可读)
        - URL: http://localhost:8000/api/schema/
        - 格式:OpenAPI 3.0
        - 用于代码生成和文档工具

        DRF Browsable API
        - 访问任何API端点即可看到
        - 提供表单化的API测试界面
        - 支持GET、POST、PUT、DELETE等操作

        Redoc文档(可选)
        - 需要安装drf-spectacular[redoc]
        - 更美观的API文档界面
        ---

1.2 核心概念

01.序列化器基础
    Serializer类定义和使用
    字段类型和验证
    数据转换和格式化
    基础序列化器示例:
    ---
    # serializers.py
    from rest_framework import serializers
    from .models import User, Post

    class UserSerializer(serializers.Serializer):
        """用户序列化器"""
        id = serializers.IntegerField(read_only=True)
        username = serializers.CharField(max_length=150)
        email = serializers.EmailField()
        first_name = serializers.CharField(max_length=30, required=False)
        last_name = serializers.CharField(max_length=30, required=False)
        is_active = serializers.BooleanField(read_only=True)
        created_at = serializers.DateTimeField(read_only=True)

        def validate_email(self, value):
            """自定义邮箱验证"""
            if 'example.com' in value:
                raise serializers.ValidationError("不允许使用example.com域名")
            return value

        def validate(self, data):
            """整体验证"""
            first_name = data.get('first_name')
            last_name = data.get('last_name')

            if not first_name and not last_name:
                raise serializers.ValidationError("姓名和姓氏至少需要填写一个")

            return data

    class PostSerializer(serializers.Serializer):
        """文章序列化器"""
        id = serializers.IntegerField(read_only=True)
        title = serializers.CharField(max_length=200)
        content = serializers.CharField()
        author = UserSerializer(read_only=True)
        author_id = serializers.IntegerField(write_only=True)
        created_at = serializers.DateTimeField(read_only=True)
        updated_at = serializers.DateTimeField(read_only=True)
        is_published = serializers.BooleanField(default=False)

        def create(self, validated_data):
            """创建文章实例"""
            return Post.objects.create(**validated_data)

        def update(self, instance, validated_data):
            """更新文章实例"""
            instance.title = validated_data.get('title', instance.title)
            instance.content = validated_data.get('content', instance.content)
            instance.is_published = validated_data.get('is_published', instance.is_published)
            instance.save()
            return instance

    # 视图中使用序列化器
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import status

    class UserListCreateView(APIView):
        """用户列表和创建视图"""
        def get(self, request):
            users = User.objects.all()
            serializer = UserSerializer(users, many=True)
            return Response(serializer.data)

        def post(self, request):
            serializer = UserSerializer(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)
    ---

02.序列化与反序列化
    将模型实例转换为JSON
    将JSON数据转换为模型实例
    处理嵌套数据结构
    序列化反序列化示例:
    ---
    # 序列化(模型实例 -> JSON)
    user = User.objects.get(id=1)
    serializer = UserSerializer(user)
    json_data = serializer.data
    print(json_data)
    # 输出: {'id': 1, 'username': 'admin', 'email': '[email protected]', ...}

    # 反序列化(JSON -> 模型实例)
    json_data = {
        'username': 'newuser',
        'email': '[email protected]',
        'first_name': '张',
        'last_name': '三'
    }
    serializer = UserSerializer(data=json_data)
    if serializer.is_valid():
        user = serializer.save()  # 创建新用户实例
        print(user.id)  # 新用户的ID

    # 更新现有实例
    user = User.objects.get(id=1)
    json_data = {'first_name': '张', 'last_name': '三丰'}
    serializer = UserSerializer(user, data=json_data, partial=True)
    if serializer.is_valid():
        updated_user = serializer.save()

    # 处理多个对象
    users = User.objects.all()
    serializer = UserSerializer(users, many=True)
    json_data = serializer.data  # 返回用户列表的JSON表示
    ---

03.字段类型详解
    基础字段类型
    关系字段类型
    自定义字段
    字段类型示例:
    ---
    from rest_framework import serializers
    from django.contrib.auth.models import User

    class UserDetailSerializer(serializers.Serializer):
        """用户详细信息序列化器 - 展示各种字段类型"""

        # 基础字段类型
        id = serializers.IntegerField(read_only=True)
        username = serializers.CharField(
            max_length=150,
            help_text="用户名,最多150个字符"
        )
        email = serializers.EmailField(
            help_text="邮箱地址,必须是有效格式"
        )
        age = serializers.IntegerField(
            min_value=0,
            max_value=120,
            help_text="年龄,0-120之间"
        )
        bio = serializers.CharField(
            required=False,
            allow_blank=True,
            help_text="个人简介"
        )

        # 日期时间字段
        birth_date = serializers.DateField(
            required=False,
            help_text="出生日期,格式:YYYY-MM-DD"
        )
        last_login = serializers.DateTimeField(
            read_only=True,
            help_text="最后登录时间"
        )

        # 布尔字段
        is_active = serializers.BooleanField(
            read_only=True,
            help_text="账户是否激活"
        )
        is_staff = serializers.BooleanField(
            default=False,
            help_text="是否为管理员"
        )

        # 选择字段
        gender = serializers.ChoiceField(
            choices=[
                ('M', '男'),
                ('F', '女'),
                ('O', '其他'),
            ],
            required=False,
            help_text="性别选择"
        )

        # 列表字段
        tags = serializers.ListField(
            child=serializers.CharField(max_length=20),
            required=False,
            help_text="标签列表"
        )

        # 字典字段
        profile = serializers.DictField(
            child=serializers.CharField(),
            required=False,
            help_text="用户配置信息"
        )

        # 浮点数字段
        height = serializers.FloatField(
            required=False,
            help_text="身高(米)"
        )

        # 十进制字段
        salary = serializers.DecimalField(
            max_digits=10,
            decimal_places=2,
            required=False,
            help_text="薪资"
        )

        # 文件字段
        avatar = serializers.ImageField(
            required=False,
            help_text="头像图片"
        )

        # 只读和只写字段
        created_at = serializers.DateTimeField(read_only=True)
        password = serializers.CharField(write_only=True, min_length=8)

        # 自定义验证字段
        confirm_password = serializers.CharField(write_only=True)

        def validate_password(self, value):
            """验证密码强度"""
            if len(value) < 8:
                raise serializers.ValidationError("密码长度至少8位")
            if not any(c.isdigit() for c in value):
                raise serializers.ValidationError("密码必须包含数字")
            return value

        def validate(self, data):
            """整体验证"""
            password = data.get('password')
            confirm_password = data.get('confirm_password')

            if password and confirm_password and password != confirm_password:
                raise serializers.ValidationError("两次输入的密码不一致")

            return data
    ---

1.3 版本演进

01.版本历史和特性
    a.DRF 3.x系列演进
        主要版本特性对比
        向后兼容性说明
        迁移指南
        版本特性对比表:
        """
        DRF主要版本特性对比:

        DRF 3.0 (2014年)
        - 重大架构重构,从2.x重写
        - 引入ViewSet和Router概念
        - 改进序列化器设计
        - 引入渲染器和解析器系统
        - 支持元数据API
        - 改进认证和权限系统

        DRF 3.1 (2015年)
        - 引入Schema生成功能
        - 添加过滤和分页系统
        - 改进测试框架集成
        - 支持异步视图(实验性)

        DRF 3.5 (2016年)
        - 改进API文档生成
        - 添加额外动作支持
        - 引入字段级验证
        - 支持自定义异常处理

        DRF 3.8 (2018年)
        - 改进自动URL路由
        - 添加更多序列化器字段
        - 支持视图集自定义操作
        - 改进性能优化

        DRF 3.10 (2019年)
        - 改进ModelSerializer
        - 添加更多内置字段类型
        - 支持自定义元数据类
        - 改进测试工具

        DRF 3.12 (2020年)
        - 改进Schema和API文档
        - 支持异步视图和序列化器
        - 添加更多验证器
        - 改进错误处理

        DRF 3.14 (2022年)
        - 支持Django 4.0
        - 改进安全特性
        - 添加更多渲染器选项
        - 优化性能

        DRF 3.15 (2023年)
        - 支持Django 4.2
        - 改进过滤系统
        - 添加新的序列化器字段
        - 改进文档生成

        向后兼容性:
        - DRF 3.x 内部保持良好向后兼容
        - 主要变更在3.0和3.12版本
        - 从3.14开始支持Python 3.8+
        - 从3.15开始逐步支持Django 5.x
        """
    b.技术发展趋势
        API技术发展趋势
        微服务架构影响
        云原生和容器化
        技术发展趋势分析:
        """
        API技术发展趋势对DRF的影响:

        1. GraphQL和REST API的融合
        - DRF开始支持GraphQL集成
        - 混合API架构的出现
        - 查询语言的标准化

        2. 异步编程模式
        - Django 3.0原生支持async/await
        - DRF逐步支持异步视图
        - 性能提升和并发处理

        3. 微服务架构
        - DRF在微服务中的角色
        - API网关集成
        - 服务间通信优化

        4. 云原生和容器化
        - Docker和Kubernetes支持
        - 自动扩展和负载均衡
        - 监控和日志集成

        5. 安全性增强
        - OAuth 2.0和OpenID Connect
        - JWT标准普及
        - API安全最佳实践

        6. API-first开发
        - API设计优先原则
        - 契约驱动开发
        - 自动化API测试
        """
    c.社区生态系统
        第三方包和扩展
        社区贡献和支持
        学习资源推荐
        生态系统概览:
        """
        DRF社区生态系统:

        1. 核心扩展包
        - djangorestframework-simplejwt: JWT认证
        - djangorestframework-jwt: 旧版JWT支持
        - djangorestframework-filters: 高级过滤
        - django-cors-headers: CORS处理
        - drf-spectacular: OpenAPI文档生成

        2. 认证和授权
        - django-oauth-toolkit: OAuth2服务器
        - drf-social-oauth2: 社交登录
        - django-rest-auth: 认证工具包
        - dj-rest-auth: 简化的认证

        3. 性能优化
        - django-debug-toolbar: 调试工具
        - django-silk: 性能分析
        - drf-extensions: 通用扩展功能
        - django-cacheops: 查询缓存

        4. 文档和测试
        - drf-yasg: Swagger文档生成
        - drf-spectacular: 现代API文档
        - pytest-drf: DRF测试工具
        - factory-boy: 测试数据工厂

        5. 监控和日志
        - django-structlog: 结构化日志
        - drf-tracking: API调用跟踪
        - django-rq: 任务队列监控
        - sentry-sdk: 错误监控

        6. 开发工具
        - drf-generators: 代码生成工具
        - drf-extensions: 常用工具集
        - django-rest-swagger: 文档集成
        - drf-writable-nested: 嵌套序列化

        学习资源:
        - 官方文档:https://www.django-rest-framework.org/
        - GitHub仓库:https://github.com/encode/django-rest-framework
        - 社区论坛:https://stackoverflow.com/questions/tagged/django-rest-framework
        - 教程网站:https://www.django-rest-framework.org/tutorial/
        """

02.新特性介绍
    a.最新版本特性
        DRF 3.15+新功能
        性能优化改进
        安全性增强
        最新特性详解:
        """
        DRF 3.15+ 主要新特性:

        1. 异步支持增强
        - 完整支持async/await语法
        - 异步序列化器和视图
        - 性能大幅提升

        2. 改进的序列化器
        - 新增ListSerializer优化
        - 改进嵌套序列化性能
        - 自定义验证器支持

        3. API文档增强
        - 支持OpenAPI 3.0
        - 自动Schema生成
        - 更好的文档集成

        4. 安全性改进
        - 默认HTTPS支持
        - 改进的CSRF保护
        - 增强的输入验证

        5. 性能优化
        - 查询优化工具
        - 缓存改进
        - 内存使用优化

        6. 开发体验
        - 更好的错误信息
        - 改进的调试工具
        - 类型提示支持
        """
    b.实验性功能
        预览新功能
        实验性API支持
        未来版本规划
        实验性功能介绍:
        """
        DRF实验性功能(需要手动启用):

        1. 异步视图(AsyncAPI)
        - 异步序列化器支持
        - 异步数据库操作
        - 非阻塞IO处理

        2. 自动API路由
        - 智能URL生成
        - 动态视图注册
        - 自动Schema更新

        3. 实时通信
        - WebSocket支持
        - Server-Sent Events
        - 实时数据推送

        4. GraphQL集成
        - GraphQL查询支持
        - REST到GraphQL转换
        - 混合API模式

        启用实验性功能:
        ```python
        # settings.py
        REST_FRAMEWORK = {
            'EXPERIMENTAL_ASYNC_VIEWS': True,
            'EXPERIMENTAL_AUTO_ROUTING': True,
            'EXPERIMENTAL_GRAPHQL': True,
        }
        ```
        """
    c.开发者工具
        调试和测试工具
        代码生成器
        性能分析工具
        开发工具推荐:
        """
        DRF开发者工具推荐:

        1. API调试工具
        - DRF Browsable API: 内置调试界面
        - Postman: API测试工具
        - Insomnia: 现代API客户端
        - HTTPie: 命令行HTTP客户端

        2. 代码生成工具
        - drf-generators: 自动生成代码
        - drf-yasg: 生成API文档
        - drf-spectacular: 现代API文档
        - factory-boy: 测试数据生成

        3. 测试工具
        - pytest-drf: DRF测试插件
        - django-test-plus: 测试工具集
        - responses: HTTP模拟库
        - model-bakery: 模型工厂

        4. 性能分析
        - django-debug-toolbar: 调试工具栏
        - django-silk: 性能分析
        - django-extensions: 扩展工具集
        - py-spy: Python性能分析

        5. 监控和日志
        - sentry-sdk: 错误监控
        - django-structlog: 结构化日志
        - django-prometheus: 指标收集
        - drf-tracking: API跟踪

        6. 开发环境
        - PyCharm: Python IDE
        - VS Code: 轻量级编辑器
        - Jupyter Notebook: 交互式开发
        - Docker: 容器化开发
        """

1.4 与Django

01.Django基础要求
    a.Django版本兼容性
        DRF支持的Django版本
        Python版本要求
        依赖包管理
        版本兼容性说明:
        ---
        DRF版本与Django版本兼容性:

        DRF 3.14.x:
        - Django 3.2, 4.0, 4.1
        - Python 3.8+

        DRF 3.15.x:
        - Django 4.1, 4.2
        - Python 3.8+

        DRF 3.16.x (最新):
        - Django 4.2, 5.0
        - Python 3.8+

        推荐配置:
        - Python 3.9+
        - Django 4.2+
        - DRF 3.15+

        安装命令:
        pip install djangorestframework
        pip install django>=4.2,<5.1

        在requirements.txt中:
        django>=4.2,<5.1
        djangorestframework>=3.15.0
        ---
    b.依赖包管理
        核心依赖包
        可选依赖包
        开发依赖包
        依赖包配置示例:
        ---
        # requirements.txt - 生产环境依赖
        django>=4.2,<5.1
        djangorestframework>=3.15.0
        djangorestframework-simplejwt>=5.3.0
        django-cors-headers>=4.3.0
        django-filter>=23.5
        psycopg2-binary>=2.9.0
        redis>=5.0.0
        celery>=5.3.0
        pillow>=10.0.0

        # requirements-dev.txt - 开发环境依赖
        pytest>=7.4.0
        pytest-django>=4.7.0
        pytest-cov>=4.1.0
        black>=23.9.0
        flake8>=6.1.0
        isort>=5.12.0
        factory-boy>=3.3.0
        model-bakery>=1.12.0

        # 安装开发依赖
        pip install -r requirements.txt
        pip install -r requirements-dev.txt
        ---
    c.项目结构要求
        Django项目标准结构
        DRF应用组织
        配置文件管理
        项目结构示例:
        ---
        myproject/
        ├── manage.py
        ├── requirements.txt
        ├── requirements-dev.txt
        ├── .env.example
        ├── .gitignore
        ├── README.md
        ├── docker-compose.yml
        ├── Dockerfile
        ├── myproject/               # 项目配置目录
        │   ├── __init__.py
        │   ├── settings/
        │   │   ├── __init__.py
        │   │   ├── base.py         # 基础配置
        │   │   ├── development.py  # 开发环境配置
        │   │   ├── production.py   # 生产环境配置
        │   │   └── testing.py      # 测试环境配置
        │   ├── urls.py
        │   ├── wsgi.py
        │   └── asgi.py
        ├── apps/                    # 应用目录
        │   ├── __init__.py
        │   ├── users/              # 用户应用
        │   │   ├── __init__.py
        │   │   ├── models.py
        │   │   ├── serializers.py
        │   │   ├── views.py
        │   │   ├── urls.py
        │   │   ├── permissions.py
        │   │   ├── filters.py
        │   │   ├── tests.py
        │   │   └── migrations/
        │   ├── posts/              # 文章应用
        │   │   ├── __init__.py
        │   │   ├── models.py
        │   │   ├── serializers.py
        │   │   ├── views.py
        │   │   ├── urls.py
        │   │   └── migrations/
        │   └── core/               # 核心应用
        │       ├── __init__.py
        │       ├── pagination.py
        │       ├── exceptions.py
        │       ├── permissions.py
        │       └── utils.py
        ├── api/                     # API配置
        │   ├── __init__.py
        │   ├── urls.py              # API根URL
        │   ├── v1/                  # API版本1
        │   │   ├── __init__.py
        │   │   ├── urls.py
        │   │   └── router.py
        │   └── v2/                  # API版本2
        │       ├── __init__.py
        │       ├── urls.py
        │       └── router.py
        ├── templates/               # 模板文件
        ├── static/                  # 静态文件
        ├── media/                   # 媒体文件
        ├── tests/                   # 测试文件
        │   ├── __init__.py
        │   ├── test_users.py
        │   ├── test_posts.py
        │   └── test_api.py
        └── docs/                    # 文档目录
            ├── api.md
            └── deployment.md
        ---

02.DRF架构设计
    a.请求响应周期
        DRF处理请求的完整流程
        中间件和组件的执行顺序
        响应生成过程
        请求响应周期图解:
        ---
        DRF请求处理流程:

        1. HTTP请求进入
        ↓
        2. Django中间件层处理
        ↓
        3. DRF异常处理器捕获异常
        ↓
        4. 认证组件验证用户身份
        ↓
        5. 权限组件检查访问权限
        ↓
        6. 节流组件限制请求频率
        ↓
        7. 内容协商确定响应格式
        ↓
        8. 解析器解析请求数据
        ↓
        9. 视图方法执行业务逻辑
        ↓
        10. 序列化器处理数据
        ↓
        11. 渲染器生成响应
        ↓
        12. HTTP响应返回
        ---
    b.组件系统架构
        认证、权限、节流组件
        解析器和渲染器
        分页和过滤组件
        组件系统示例:
        ---
        # settings.py - DRF组件配置
        REST_FRAMEWORK = {
            # 默认认证组件
            'DEFAULT_AUTHENTICATION_CLASSES': [
                'rest_framework.authentication.SessionAuthentication',
                'rest_framework.authentication.BasicAuthentication',
                'rest_framework.authentication.TokenAuthentication',
            ],

            # 默认权限组件
            'DEFAULT_PERMISSION_CLASSES': [
                'rest_framework.permissions.IsAuthenticated',
            ],

            # 默认节流组件
            'DEFAULT_THROTTLE_CLASSES': [
                'rest_framework.throttling.AnonRateThrottle',
                'rest_framework.throttling.UserRateThrottle',
            ],
            'DEFAULT_THROTTLE_RATES': {
                'anon': '100/hour',
                'user': '1000/hour',
            },

            # 解析器配置
            'DEFAULT_PARSER_CLASSES': [
                'rest_framework.parsers.JSONParser',
                'rest_framework.parsers.FormParser',
                'rest_framework.parsers.MultiPartParser',
            ],

            # 渲染器配置
            'DEFAULT_RENDERER_CLASSES': [
                'rest_framework.renderers.JSONRenderer',
                'rest_framework.renderers.BrowsableAPIRenderer',
            ],

            # 分页配置
            'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
            'PAGE_SIZE': 20,

            # 过滤器配置
            'DEFAULT_FILTER_BACKENDS': [
                'django_filters.rest_framework.DjangoFilterBackend',
                'rest_framework.filters.SearchFilter',
                'rest_framework.filters.OrderingFilter',
            ],

            # 版本控制
            'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
            'DEFAULT_VERSION': 'v1',
            'ALLOWED_VERSIONS': ['v1', 'v2'],
            'VERSION_PARAM': 'version',
        }
        ---
    c.扩展性设计
        插件化架构
        自定义组件开发
        第三方扩展集成
        扩展性设计示例:
        ---
        # 自定义认证组件
        from rest_framework.authentication import BaseAuthentication
        from rest_framework.exceptions import AuthenticationFailed

        class JWTAuthentication(BaseAuthentication):
            """自定义JWT认证组件"""

            def authenticate(self, request):
                # 从请求头获取token
                token = request.headers.get('Authorization')
                if not token:
                    return None

                # 验证token
                try:
                    payload = self.decode_token(token)
                    user = self.get_user(payload['user_id'])
                    return (user, payload)
                except Exception:
                    raise AuthenticationFailed('Invalid token')

            def decode_token(self, token):
                """解码JWT token"""
                # 实现JWT解码逻辑
                pass

            def get_user(self, user_id):
                """获取用户对象"""
                from django.contrib.auth import get_user_model
                User = get_user_model()
                return User.objects.get(id=user_id)

        # 自定义权限组件
        from rest_framework.permissions import BasePermission

        class IsOwnerOrReadOnly(BasePermission):
            """只有对象所有者才能编辑"""

            def has_object_permission(self, request, view, obj):
                # 安全方法(GET, HEAD, OPTIONS)允许所有用户
                if request.method in ['GET', 'HEAD', 'OPTIONS']:
                    return True

                # 写入方法只允许对象所有者
                return obj.owner == request.user

        # 自定义渲染器
        from rest_framework.renderers import BaseRenderer
        import yaml

        class YAMLRenderer(BaseRenderer):
            """YAML格式渲染器"""

            media_type = 'application/yaml'
            format = 'yaml'
            charset = 'utf-8'

            def render(self, data, accepted_media_type=None, renderer_context=None):
                if data is None:
                    return ''

                return yaml.dump(data, allow_unicode=True)
        ---

2 项目搭建与配置

2.1 环境准备

01.Python环境配置
    a.Python版本选择
        支持的Python版本范围
        版本特性对比
        环境变量配置
        Python版本配置详解:
        """
        DRF Python版本要求:

        官方支持版本:
        - Python 3.8: 基础支持,推荐用于生产环境
        - Python 3.9: 稳定版本,推荐使用
        - Python 3.10: 新特性支持,性能提升
        - Python 3.11: 性能优化,推荐新项目
        - Python 3.12: 最新特性,前瞻性使用

        版本特性对比:
        Python 3.8 (LTS):
        - 位置参数语法 (PEP 570)
        - 赋值表达式 (海象操作符)
        - f-string调试支持
        - 稳定性好,生产环境首选

        Python 3.9:
        - 字典合并操作符
        - 字符串方法增强
        - 类型提示改进
        - 标准库更新

        Python 3.10:
        - 结构化模式匹配
        - 类型联合运算符
        - 上下文管理器改进
        - 错误信息优化

        Python 3.11:
        - 性能提升15-60%
        - 异常组和except*语法
        - 细粒度错误追踪
        - typing系统增强

        Python 3.12:
        - f-string改进
        - 类型系统进一步优化
        - 标准库新功能
        - 性能持续提升

        环境变量配置示例:
        # .bashrc 或 .zshrc
        export PYTHONPATH="/path/to/your/project:$PYTHONPATH"
        export DJANGO_SETTINGS_MODULE="myproject.settings.development"
        export VIRTUAL_ENV_DISABLE_PROMPT=1

        # Windows环境变量
        set PYTHONPATH=C:\path\to\your\project;%PYTHONPATH%
        set DJANGO_SETTINGS_MODULE=myproject.settings.development
        """
    b.依赖包管理
        pip和conda使用
        虚拟环境最佳实践
        依赖冲突解决
        依赖管理工具对比:
        """
        依赖管理工具选择:

        1. pip + requirements.txt (推荐)
        优点:
        - Python标准包管理器
        - 简单易用,文档丰富
        - 与虚拟环境完美集成
        - 支持版本锁定

        缺点:
        - 依赖冲突处理较弱
        - 没有依赖解析缓存

        使用方法:
        # 创建虚拟环境
        python -m venv drf_env
        source drf_env/bin/activate  # Linux/macOS
        drf_env\Scripts\activate     # Windows

        # 升级pip
        pip install --upgrade pip setuptools wheel

        # 安装依赖
        pip install -r requirements.txt

        requirements.txt 示例:
        django>=4.2,<5.1
        djangorestframework>=3.15.0
        djangorestframework-simplejwt>=5.3.0
        django-cors-headers>=4.3.0
        django-filter>=23.5
        psycopg2-binary>=2.9.0
        redis>=5.0.0
        celery>=5.3.0
        pillow>=10.0.0

        2. pip-tools (进阶推荐)
        优点:
        - 依赖锁定和更新管理
        - 编译时依赖解析
        - 生产环境精确控制

        使用方法:
        pip install pip-tools

        # requirements.in
        django>=4.2,<5.1
        djangorestframework>=3.15.0
        # ... 其他依赖

        # 编译requirements.txt
        pip-compile requirements.in

        # 同步安装
        pip-sync requirements.txt

        3. conda (科学计算环境)
        优点:
        - 跨语言环境管理
        - 二进制包分发
        - 环境隔离性好

        缺点:
        - 包生态相对较小
        - 配置复杂度较高

        使用方法:
        conda create -n drf_env python=3.11
        conda activate drf_env
        conda install django djangorestframework
        pip install djangorestframework-simplejwt  # 通过pip安装conda没有的包

        4. Poetry (现代化工具)
        优点:
        - 现代依赖管理
        - 项目配置文件
        - 虚拟环境集成
        - 构建和发布支持

        使用方法:
        pip install poetry
        poetry new drf_project
        cd drf_project
        poetry add django djangorestframework
        poetry install
        """
    c.开发工具配置
        IDE和编辑器设置
        代码格式化工具
        调试工具集成
        开发工具配置示例:
        """
        开发环境工具配置:

        1. VS Code 配置

        .vscode/settings.json:
        {
            "python.defaultInterpreterPath": "./drf_env/bin/python",
            "python.linting.enabled": true,
            "python.linting.pylintEnabled": true,
            "python.linting.flake8Enabled": true,
            "python.formatting.provider": "black",
            "python.formatting.blackArgs": ["--line-length", "88"],
            "python.sortImports.args": ["--profile", "black"],
            "editor.formatOnSave": true,
            "editor.codeActionsOnSave": {
                "source.organizeImports": true
            },
            "files.exclude": {
                "**/__pycache__": true,
                "**/*.pyc": true,
                ".pytest_cache": true
            }
        }

        .vscode/launch.json:
        {
            "version": "0.2.0",
            "configurations": [
                {
                    "name": "Django",
                    "type": "python",
                    "request": "launch",
                    "program": "${workspaceFolder}/manage.py",
                    "args": ["runserver", "--noreload"],
                    "django": true,
                    "env": {
                        "DJANGO_SETTINGS_MODULE": "myproject.settings.development"
                    }
                },
                {
                    "name": "Django Test",
                    "type": "python",
                    "request": "launch",
                    "program": "${workspaceFolder}/manage.py",
                    "args": ["test"],
                    "django": true,
                    "console": "integratedTerminal"
                }
            ]
        }

        2. PyCharm 配置

        项目设置:
        - Project Interpreter: 选择虚拟环境Python
        - Settings → Project → Django:
          - Enable Django Support
          - Django project root: 项目根目录
          - Settings file: myproject/settings/development.py

        代码风格:
        - Settings → Editor → Code Style → Python
        - 导入Black配置文件
        - 配置isort和flake8

        3. 代码格式化工具

        pyproject.toml:
        [tool.black]
        line-length = 88
        target-version = ['py38', 'py39', 'py310', 'py311']
        include = '\.pyi?$'
        extend-exclude = '''
        /(
            migrations
        )/
        '''

        [tool.isort]
        profile = "black"
        line_length = 88
        multi_line_output = 3
        known_first_party = ["myproject", "apps"]

        [tool.flake8]
        max-line-length = 88
        extend-ignore = ["E203", "W503"]
        exclude = [
            ".git",
            "__pycache__",
            "migrations",
            ".venv",
            ".env"
        ]

        4. Git hooks 配置

        pre-commit config:
        .pre-commit-config.yaml:
        repos:
        -   repo: https://github.com/pre-commit/pre-commit-hooks
            rev: v4.4.0
            hooks:
            -   id: trailing-whitespace
            -   id: end-of-file-fixer
            -   id: check-yaml
            -   id: check-added-large-files

        -   repo: https://github.com/psf/black
            rev: 23.7.0
            hooks:
            -   id: black

        -   repo: https://github.com/pycqa/isort
            rev: 5.12.0
            hooks:
            -   id: isort

        -   repo: https://github.com/pycqa/flake8
            rev: 6.0.0
            hooks:
            -   id: flake8

        安装pre-commit:
        pip install pre-commit
        pre-commit install
        """

02.Django项目创建
    a.项目结构规划
        标准项目目录结构
        模块化设计原则
        配置文件组织
        项目结构设计:
        ```
        推荐的Django项目结构:

        drf_project/
        ├── .env.example              # 环境变量示例
        ├── .env                     # 环境变量(不提交到版本控制)
        ├── .gitignore              # Git忽略文件
        ├── .pre-commit-config.yaml # Pre-commit配置
        ├── manage.py               # Django管理脚本
        ├── pyproject.toml          # 项目配置(现代方式)
        ├── requirements.txt        # 生产依赖
        ├── requirements-dev.txt    # 开发依赖
        ├── requirements-test.txt   # 测试依赖
        ├── README.md               # 项目说明文档
        ├── Dockerfile              # Docker配置
        ├── docker-compose.yml      # Docker Compose配置
        ├── myproject/              # 项目配置目录
        │   ├── __init__.py
        │   ├── settings/           # 设置模块化
        │   │   ├── __init__.py
        │   │   ├── base.py         # 基础设置
        │   │   ├── development.py  # 开发环境设置
        │   │   ├── testing.py      # 测试环境设置
        │   │   ├── staging.py      # 预发布环境设置
        │   │   └── production.py   # 生产环境设置
        │   ├── urls.py             # 主URL配置
        │   ├── wsgi.py             # WSGI配置
        │   └── asgi.py             # ASGI配置
        ├── apps/                   # 应用模块目录
        │   ├── __init__.py
        │   ├── core/               # 核心应用(通用功能)
        │   │   ├── __init__.py
        │   │   ├── models.py
        │   │   ├── views.py
        │   │   ├── serializers.py
        │   │   ├── urls.py
        │   │   ├── permissions.py
        │   │   ├── pagination.py
        │   │   ├── exceptions.py
        │   │   ├── utils.py
        │   │   └── tests.py
        │   ├── users/              # 用户管理应用
        │   │   ├── __init__.py
        │   │   ├── models.py
        │   │   ├── views.py
        │   │   ├── serializers.py
        │   │   ├── urls.py
        │   │   ├── permissions.py
        │   │   ├── managers.py
        │   │   ├── signals.py
        │   │   ├── tasks.py
        │   │   └── tests.py
        │   ├── authentication/    # 认证应用
        │   │   ├── __init__.py
        │   │   ├── views.py
        │   │   ├── serializers.py
        │   │   ├── urls.py
        │   │   ├── tokens.py
        │   │   └── tests.py
        │   └── ...                 # 其他业务应用
        ├── api/                    # API专用配置
        │   ├── __init__.py
        │   ├── urls.py             # API根URL
        │   ├── v1/                 # API版本1
        │   │   ├── __init__.py
        │   │   ├── urls.py
        │   │   ├── router.py
        │   │   └── views.py
        │   ├── v2/                 # API版本2
        │   │   ├── __init__.py
        │   │   ├── urls.py
        │   │   ├── router.py
        │   │   └── views.py
        │   └── middleware.py        # API专用中间件
        ├── config/                 # 配置文件目录
        │   ├── __init__.py
        │   ├── logging.py          # 日志配置
        │   ├── celery.py           # Celery配置
        │   ├── storage.py          # 存储配置
        │   └── testing.py          # 测试配置
        ├── scripts/                # 脚本目录
        │   ├── __init__.py
        │   ├── deploy.sh           # 部署脚本
        │   ├── backup.sh           # 备份脚本
        │   ├── init_db.py          # 数据库初始化
        │   └── load_data.py        # 数据加载脚本
        ├── docs/                   # 文档目录
        │   ├── api/                # API文档
        │   ├── deployment/        # 部署文档
        │   ├── development/       # 开发文档
        │   └── user-guide/        # 用户指南
        ├── tests/                  # 测试目录
        │   ├── __init__.py
        │   ├── conftest.py         # pytest配置
        │   ├── factories.py        # 测试数据工厂
        │   ├── fixtures/           # 测试数据
        │   ├── integration/        # 集成测试
        │   ├── unit/               # 单元测试
        │   └── api/                # API测试
        ├── locale/                 # 国际化文件
        │   ├── zh_CN/
        │   │   └── LC_MESSAGES/
        │   │       ├── django.po
        │   │       └── django.mo
        │   └── en/
        │       └── LC_MESSAGES/
        │           ├── django.po
        │           └── django.mo
        ├── static/                 # 静态文件
        │   ├── css/
        │   ├── js/
        │   ├── images/
        │   └── admin/
        ├── media/                  # 用户上传文件
        │   ├── avatars/
        │   ├── uploads/
        │   └── temp/
        ├── templates/              # 模板文件
        │   ├── admin/
        │   ├── registration/
        │   └── emails/
        └── logs/                   # 日志文件目录
            ├── django.log
            ├── drf.log
            ├── celery.log
            └── errors.log

        项目设计原则:

        1. 模块化设计
        - 每个应用专注单一职责
        - 核心功能放在core应用
        - 业务逻辑按领域划分

        2. 配置分离
        - 开发、测试、生产环境配置分离
        - 敏感信息使用环境变量
        - 使用继承方式减少重复

        3. 版本控制
        - API版本通过URL区分
        - 向后兼容性保证
        - 渐进式升级策略

        4. 可扩展性
        - 插件化架构设计
        - 接口标准化
        - 配置驱动开发
        ```
    b.配置文件组织
        多环境配置管理
        环境变量使用
        配置继承机制
        配置文件组织示例:
        ```python
        # myproject/settings/base.py - 基础配置
        import os
        from pathlib import Path
        from decouple import config, Csv

        # 项目根目录
        BASE_DIR = Path(__file__).resolve().parent.parent.parent

        # 安全配置
        SECRET_KEY = config('SECRET_KEY')
        DEBUG = config('DEBUG', default=False, cast=bool)

        # 允许的主机
        ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost,127.0.0.1', cast=Csv())

        # 应用配置
        DJANGO_APPS = [
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
        ]

        THIRD_PARTY_APPS = [
            'rest_framework',
            'rest_framework_simplejwt',
            'corsheaders',
            'django_filters',
            'drf_spectacular',
            'django_extensions',
            'django_celery_beat',
            'django_celery_results',
        ]

        LOCAL_APPS = [
            'apps.core',
            'apps.users',
            'apps.authentication',
            'apps.posts',
            'apps.comments',
        ]

        INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS

        # 中间件
        MIDDLEWARE = [
            'corsheaders.middleware.CorsMiddleware',
            'django.middleware.security.SecurityMiddleware',
            'django.contrib.sessions.middleware.SessionMiddleware',
            'django.middleware.common.CommonMiddleware',
            'django.middleware.csrf.CsrfViewMiddleware',
            'django.contrib.auth.middleware.AuthenticationMiddleware',
            'django.contrib.messages.middleware.MessageMiddleware',
            'django.middleware.clickjacking.XFrameOptionsMiddleware',
            'apps.core.middleware.RequestLoggingMiddleware',
        ]

        # 根URL配置
        ROOT_URLCONF = 'myproject.urls'

        # 模板配置
        TEMPLATES = [
            {
                'BACKEND': 'django.template.backends.django.DjangoTemplates',
                'DIRS': [BASE_DIR / 'templates'],
                'APP_DIRS': True,
                'OPTIONS': {
                    'context_processors': [
                        'django.template.context_processors.debug',
                        'django.template.context_processors.request',
                        'django.contrib.auth.context_processors.auth',
                        'django.contrib.messages.context_processors.messages',
                        'apps.core.context_processors.site_settings',
                    ],
                },
            },
        ]

        # WSGI/ASGI配置
        WSGI_APPLICATION = 'myproject.wsgi.application'
        ASGI_APPLICATION = 'myproject.asgi.application'

        # 数据库配置
        DATABASES = {
            'default': {
                'ENGINE': config('DB_ENGINE', default='django.db.backends.postgresql'),
                'NAME': config('DB_NAME'),
                'USER': config('DB_USER'),
                'PASSWORD': config('DB_PASSWORD'),
                'HOST': config('DB_HOST', default='localhost'),
                'PORT': config('DB_PORT', default='5432', cast=int),
                'OPTIONS': {
                    'charset': 'utf8',
                },
                'CONN_MAX_AGE': config('DB_CONN_MAX_AGE', default=60, cast=int),
            }
        }

        # 缓存配置
        CACHES = {
            'default': {
                'BACKEND': config('CACHE_BACKEND', default='django_redis.cache.RedisCache'),
                'LOCATION': config('REDIS_URL', default='redis://127.0.0.1:6379/1'),
                'OPTIONS': {
                    'CLIENT_CLASS': 'django_redis.client.DefaultClient',
                    'CONNECTION_POOL_KWARGS': {
                        'max_connections': config('REDIS_MAX_CONNECTIONS', default=50, cast=int),
                        'retry_on_timeout': True,
                    }
                },
                'KEY_PREFIX': config('CACHE_KEY_PREFIX', default='drf_cache'),
                'TIMEOUT': config('CACHE_DEFAULT_TIMEOUT', default=300, cast=int),
            }
        }

        # 国际化
        LANGUAGE_CODE = config('LANGUAGE_CODE', default='zh-hans')
        TIME_ZONE = config('TIME_ZONE', default='Asia/Shanghai')
        USE_I18N = True
        USE_TZ = True

        # 静态文件配置
        STATIC_URL = '/static/'
        STATIC_ROOT = BASE_DIR / 'staticfiles'
        STATICFILES_DIRS = [BASE_DIR / 'static']

        # 媒体文件配置
        MEDIA_URL = '/media/'
        MEDIA_ROOT = BASE_DIR / 'media'

        # 用户模型
        AUTH_USER_MODEL = 'users.User'

        # 密码验证
        AUTH_PASSWORD_VALIDATORS = [
            {
                'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
            },
            {
                'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
                'OPTIONS': {
                    'min_length': 8,
                }
            },
            {
                'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
            },
            {
                'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
            },
        ]

        # REST Framework 基础配置
        REST_FRAMEWORK = {
            'DEFAULT_AUTHENTICATION_CLASSES': [
                'apps.authentication.authentication.JWTAuthentication',
                'rest_framework.authentication.SessionAuthentication',
            ],
            'DEFAULT_PERMISSION_CLASSES': [
                'rest_framework.permissions.IsAuthenticated',
            ],
            'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination',
            'PAGE_SIZE': config('PAGE_SIZE', default=20, cast=int),
            'DEFAULT_FILTER_BACKENDS': [
                'django_filters.rest_framework.DjangoFilterBackend',
                'rest_framework.filters.SearchFilter',
                'rest_framework.filters.OrderingFilter',
            ],
            'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
            'DEFAULT_RENDERER_CLASSES': [
                'rest_framework.renderers.JSONRenderer',
                'rest_framework.renderers.BrowsableAPIRenderer',
            ],
            'EXCEPTION_HANDLER': 'apps.core.exceptions.custom_exception_handler',
        }

        # JWT 配置
        from datetime import timedelta
        SIMPLE_JWT = {
            'ACCESS_TOKEN_LIFETIME': timedelta(hours=config('JWT_ACCESS_TOKEN_LIFETIME_HOURS', default=24, cast=int)),
            'REFRESH_TOKEN_LIFETIME': timedelta(days=config('JWT_REFRESH_TOKEN_LIFETIME_DAYS', default=7, cast=int)),
            'ROTATE_REFRESH_TOKENS': config('JWT_ROTATE_REFRESH_TOKENS', default=True, cast=bool),
            'BLACKLIST_AFTER_ROTATION': config('JWT_BLACKLIST_AFTER_ROTATION', default=True, cast=bool),
            'ALGORITHM': 'HS256',
            'SIGNING_KEY': SECRET_KEY,
            'AUTH_HEADER_TYPES': ('Bearer',),
            'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
            'USER_ID_FIELD': 'id',
            'USER_ID_CLAIM': 'user_id',
        }

        # Celery 配置
        CELERY_BROKER_URL = config('CELERY_BROKER_URL', default='redis://localhost:6379/0')
        CELERY_RESULT_BACKEND = config('CELERY_RESULT_BACKEND', default='redis://localhost:6379/0')
        CELERY_ACCEPT_CONTENT = ['json']
        CELERY_TASK_SERIALIZER = 'json'
        CELERY_RESULT_SERIALIZER = 'json'
        CELERY_TIMEZONE = TIME_ZONE
        CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler'

        # 日志配置
        LOGGING = {
            'version': 1,
            'disable_existing_loggers': False,
            'formatters': {
                'verbose': {
                    'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
                    'style': '{',
                },
                'simple': {
                    'format': '{levelname} {message}',
                    'style': '{',
                },
            },
            'handlers': {
                'file': {
                    'level': 'INFO',
                    'class': 'logging.FileHandler',
                    'filename': BASE_DIR / 'logs' / 'django.log',
                    'formatter': 'verbose',
                },
                'console': {
                    'level': 'DEBUG',
                    'class': 'logging.StreamHandler',
                    'formatter': 'simple',
                },
            },
            'root': {
                'handlers': ['console', 'file'],
                'level': config('LOG_LEVEL', default='INFO'),
            },
        }

        # 邮件配置
        EMAIL_BACKEND = config('EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend')
        EMAIL_HOST = config('EMAIL_HOST')
        EMAIL_PORT = config('EMAIL_PORT', default=587, cast=int)
        EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=True, cast=bool)
        EMAIL_HOST_USER = config('EMAIL_HOST_USER')
        EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')
        DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL')

        # 文件存储配置
        DEFAULT_FILE_STORAGE = config('DEFAULT_FILE_STORAGE', default='django.core.files.storage.FileSystemStorage')
        AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID', default='')
        AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY', default='')
        AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME', default='')
        AWS_S3_REGION_NAME = config('AWS_S3_REGION_NAME', default='')

        # myproject/settings/development.py - 开发环境配置
        from .base import *

        DEBUG = True

        # 开发数据库配置
        DATABASES['default'].update({
            'NAME': 'drf_dev_db',
            'USER': 'postgres',
            'PASSWORD': 'postgres',
        })

        # 开发缓存配置
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
            }
        }

        # CORS 开发配置
        CORS_ALLOWED_ORIGINS = [
            "http://localhost:3000",
            "http://127.0.0.1:3000",
            "http://localhost:8080",
        ]
        CORS_ALLOW_CREDENTIALS = True

        # 开发环境中间件
        MIDDLEWARE.insert(1, 'debug_toolbar.middleware.DebugToolbarMiddleware')
        INSTALLED_APPS += ['debug_toolbar']

        # 开发工具配置
        INSTALLED_APPS += ['django_extensions']

        # 邮件后端(开发时使用控制台)
        EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

        # 静态文件服务
        STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'

        # myproject/settings/production.py - 生产环境配置
        from .base import *

        DEBUG = False

        # 生产数据库配置(使用环境变量)
        DATABASES['default'].update({
            'NAME': os.environ.get('DB_NAME'),
            'USER': os.environ.get('DB_USER'),
            'PASSWORD': os.environ.get('DB_PASSWORD'),
            'HOST': os.environ.get('DB_HOST'),
            'PORT': os.environ.get('DB_PORT'),
        })

        # 生产安全配置
        SECURE_BROWSER_XSS_FILTER = True
        SECURE_CONTENT_TYPE_NOSNIFF = True
        X_FRAME_OPTIONS = 'DENY'
        SECURE_HSTS_SECONDS = 31536000
        SECURE_HSTS_INCLUDE_SUBDOMAINS = True
        SECURE_HSTS_PRELOAD = True

        # CORS 生产配置
        CORS_ALLOWED_ORIGINS = os.environ.get('CORS_ALLOWED_ORIGINS', '').split(',')
        CORS_ALLOW_CREDENTIALS = True

        # 静态文件存储(生产环境使用CDN)
        if os.environ.get('USE_AWS_S3', 'False').lower() == 'true':
            DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
            STATICFILES_STORAGE = 'storages.backends.s3boto3.S3StaticStorage'

        # 生产日志配置
        LOGGING['handlers'].update({
            'error_file': {
                'level': 'ERROR',
                'class': 'logging.FileHandler',
                'filename': BASE_DIR / 'logs' / 'django_error.log',
                'formatter': 'verbose',
            },
        })
        LOGGING['loggers']['django']['handlers'].append('error_file')

        # 会话安全配置
        SESSION_COOKIE_SECURE = True
        SESSION_COOKIE_HTTPONLY = True
        SESSION_COOKIE_SAMESITE = 'Lax'
        CSRF_COOKIE_SECURE = True
        CSRF_COOKIE_HTTPONLY = True
        ```
    c.环境变量管理
        .env文件配置
        敏感信息保护
        配置验证机制
        环境变量管理示例:
        ```
        # .env.example - 环境变量示例文件
        # 复制此文件为 .env 并填入实际值

        # 基础配置
        SECRET_KEY=your-secret-key-here
        DEBUG=False
        ALLOWED_HOSTS=localhost,127.0.0.1,your-domain.com

        # 数据库配置
        DB_ENGINE=django.db.backends.postgresql
        DB_NAME=your_database_name
        DB_USER=your_database_user
        DB_PASSWORD=your_database_password
        DB_HOST=localhost
        DB_PORT=5432
        DB_CONN_MAX_AGE=60

        # Redis配置
        REDIS_URL=redis://localhost:6379/0
        REDIS_MAX_CONNECTIONS=50
        CACHE_KEY_PREFIX=drf_cache
        CACHE_DEFAULT_TIMEOUT=300

        # JWT配置
        JWT_ACCESS_TOKEN_LIFETIME_HOURS=24
        JWT_REFRESH_TOKEN_LIFETIME_DAYS=7
        JWT_ROTATE_REFRESH_TOKENS=True
        JWT_BLACKLIST_AFTER_ROTATION=True

        # 邮件配置
        EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
        EMAIL_HOST=smtp.gmail.com
        EMAIL_PORT=587
        EMAIL_USE_TLS=True
        [email protected]
        EMAIL_HOST_PASSWORD=your-app-password
        [email protected]

        # AWS S3配置(可选)
        USE_AWS_S3=False
        AWS_ACCESS_KEY_ID=your-aws-access-key
        AWS_SECRET_ACCESS_KEY=your-aws-secret-key
        AWS_STORAGE_BUCKET_NAME=your-s3-bucket
        AWS_S3_REGION_NAME=us-east-1

        # CORS配置
        CORS_ALLOWED_ORIGINS=https://your-frontend-domain.com,https://www.your-frontend-domain.com

        # 日志配置
        LOG_LEVEL=INFO

        # Celery配置
        CELERY_BROKER_URL=redis://localhost:6379/0
        CELERY_RESULT_BACKEND=redis://localhost:6379/0

        # 分页配置
        PAGE_SIZE=20

        # 缓存配置
        CACHE_BACKEND=django_redis.cache.RedisCache

        # 文件存储
        DEFAULT_FILE_STORAGE=django.core.files.storage.FileSystemStorage

        # 环境变量验证脚本
        # scripts/check_env.py
        import os
        import sys
        from pathlib import Path
        from typing import Dict, List, Optional

        class EnvironmentValidator:
            """环境变量验证器"""

            REQUIRED_VARS = [
                'SECRET_KEY',
                'DB_NAME',
                'DB_USER',
                'DB_PASSWORD',
            ]

            OPTIONAL_VARS = {
                'DEBUG': False,
                'ALLOWED_HOSTS': 'localhost,127.0.0.1',
                'DB_HOST': 'localhost',
                'DB_PORT': '5432',
                'REDIS_URL': 'redis://localhost:6379/0',
                'EMAIL_HOST_USER': '',
                'EMAIL_HOST_PASSWORD': '',
                'CORS_ALLOWED_ORIGINS': '',
            }

            def __init__(self, env_file: str = '.env'):
                self.env_file = Path(env_file)
                self.errors: List[str] = []
                self.warnings: List[str] = []

            def load_env_file(self) -> Dict[str, str]:
                """加载环境变量文件"""
                env_vars = {}
                if self.env_file.exists():
                    with open(self.env_file, 'r') as f:
                        for line in f:
                            line = line.strip()
                            if line and not line.startswith('#') and '=' in line:
                                key, value = line.split('=', 1)
                                env_vars[key] = value
                return env_vars

            def validate_required_vars(self, env_vars: Dict[str, str]):
                """验证必需的环境变量"""
                for var in self.REQUIRED_VARS:
                    if var not in env_vars or not env_vars[var]:
                        self.errors.append(f"缺少必需的环境变量: {var}")

            def validate_optional_vars(self, env_vars: Dict[str, str]):
                """验证可选的环境变量"""
                for var, default_value in self.OPTIONAL_VARS.items():
                    if var in env_vars:
                        # 特殊验证逻辑
                        if var == 'DEBUG':
                            if env_vars[var].lower() not in ['true', 'false']:
                                self.warnings.append(f"DEBUG值应该是 'true' 或 'false'")
                        elif var == 'DB_PORT':
                            try:
                                int(env_vars[var])
                            except ValueError:
                                self.warnings.append(f"DB_PORT应该是数字")
                        elif var == 'ALLOWED_HOSTS':
                            hosts = [h.strip() for h in env_vars[var].split(',')]
                            if not hosts:
                                self.warnings.append("ALLOWED_HOSTS不能为空")

            def validate_security(self, env_vars: Dict[str, str]):
                """验证安全相关配置"""
                if env_vars.get('DEBUG', '').lower() == 'true':
                    self.warnings.append("生产环境不应设置 DEBUG=True")

                secret_key = env_vars.get('SECRET_KEY', '')
                if secret_key and len(secret_key) < 50:
                    self.warnings.append("SECRET_KEY长度应该至少50个字符")

                if secret_key == 'your-secret-key-here':
                    self.errors.append("请设置真实的 SECRET_KEY")

            def validate_database(self, env_vars: Dict[str, str]):
                """验证数据库配置"""
                db_engine = env_vars.get('DB_ENGINE', '')
                if 'postgresql' in db_engine:
                    db_host = env_vars.get('DB_HOST', '')
                    if db_host == 'localhost' and 'production' in os.environ.get('DJANGO_SETTINGS_MODULE', ''):
                        self.warnings.append("生产环境不应使用本地数据库")

            def validate_email(self, env_vars: Dict[str, str]):
                """验证邮件配置"""
                email_backend = env_vars.get('EMAIL_BACKEND', '')
                if 'smtp' in email_backend:
                    required_email_vars = ['EMAIL_HOST', 'EMAIL_PORT', 'EMAIL_HOST_USER']
                    for var in required_email_vars:
                        if not env_vars.get(var):
                            self.warnings.append(f"SMTP配置需要 {var}")

            def validate(self) -> bool:
                """执行所有验证"""
                print("🔍 开始验证环境变量...")

                env_vars = self.load_env_file()

                # 检查 .env 文件是否存在
                if not self.env_file.exists():
                    self.errors.append(f"环境变量文件不存在: {self.env_file}")
                    return False

                self.validate_required_vars(env_vars)
                self.validate_optional_vars(env_vars)
                self.validate_security(env_vars)
                self.validate_database(env_vars)
                self.validate_email(env_vars)

                # 输出结果
                if self.errors:
                    print("❌ 发现错误:")
                    for error in self.errors:
                        print(f"  - {error}")

                if self.warnings:
                    print("⚠️  警告:")
                    for warning in self.warnings:
                        print(f"  - {warning}")

                if not self.errors and not self.warnings:
                    print("✅ 环境变量验证通过!")
                    return True
                elif not self.errors:
                    print("✅ 环境变量验证通过,但有警告")
                    return True
                else:
                    print("❌ 环境变量验证失败!")
                    return False

        if __name__ == '__main__':
            validator = EnvironmentValidator()
            success = validator.validate()
            sys.exit(0 if success else 1)
        ```

2.2 应用配置

01.DRF安装和设置
    pip包安装
    settings.py配置
    应用注册顺序
    DRF基础配置示例:
    ```python
    # requirements.txt - DRF相关依赖
    # 核心DRF
    djangorestframework>=3.15.0,<4.0.0

    # 认证相关
    djangorestframework-simplejwt>=5.3.0
    django-oauth-toolkit>=1.7.0
    dj-rest-auth>=5.0.0

    # 过滤和分页
    django-filter>=23.5
    django-pagination>=1.0.0

    # 文档生成
    drf-spectacular>=0.27.0
    drf-yasg>=1.21.0
    drf-spectacular-sidecar>=2024.10.1

    # CORS支持
    django-cors-headers>=4.3.0

    # 缓存和性能
    django-redis>=5.4.0
    drf-extensions>=0.7.0

    # 开发工具
    drf-generators>=0.3.0
    drf-writable-nested>=0.6.0

    # 测试工具
    pytest-drf>=3.6.0
    factory-boy>=3.3.0

    # 监控和日志
    drf-tracking>=0.3.0
    django-structlog>=3.1.0

    # myproject/settings/base.py - DRF基础配置
    # 应用注册 - 推荐的注册顺序
    INSTALLED_APPS = [
        # Django 内置应用(必须在前)
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',

        # DRF 核心应用
        'rest_framework',

        # DRF 扩展应用(按功能分组)
        # 认证相关
        'rest_framework_simplejwt',
        'oauth2_provider',
        'dj_rest_auth',

        # 过滤和分页
        'django_filters',

        # 文档生成
        'drf_spectacular',
        'drf_spectacular_sidecar',

        # CORS 和中间件
        'corsheaders',

        # 缓存
        'django_extensions',

        # 第三方扩展
        'drf_extensions',

        # 项目应用
        'apps.core',
        'apps.users',
        'apps.posts',
        'apps.comments',
    ]

    # DRF 核心配置
    REST_FRAMEWORK = {
        # 认证配置
        'DEFAULT_AUTHENTICATION_CLASSES': [
            # JWT 认证(推荐用于API)
            'rest_framework_simplejwt.authentication.JWTAuthentication',
            # Session 认证(用于 DRF Browsable API)
            'rest_framework.authentication.SessionAuthentication',
            # 基础认证(用于开发调试)
            'rest_framework.authentication.BasicAuthentication',
        ],

        # 权限配置
        'DEFAULT_PERMISSION_CLASSES': [
            # 默认要求认证
            'rest_framework.permissions.IsAuthenticated',
        ],

        # 分页配置
        'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination',
        'PAGE_SIZE': 20,
        'PAGINATE_BY_PARAM': 'page_size',
        'MAX_PAGINATE_BY': 100,

        # 过滤配置
        'DEFAULT_FILTER_BACKENDS': [
            # Django ORM 过滤器
            'django_filters.rest_framework.DjangoFilterBackend',
            # 搜索过滤器
            'rest_framework.filters.SearchFilter',
            # 排序过滤器
            'rest_framework.filters.OrderingFilter',
        ],

        # 渲染器配置
        'DEFAULT_RENDERER_CLASSES': [
            # JSON 渲染器(主要用于API)
            'rest_framework.renderers.JSONRenderer',
            # 可浏览 API 渲染器(用于开发调试)
            'rest_framework.renderers.BrowsableAPIRenderer',
        ],

        # 解析器配置
        'DEFAULT_PARSER_CLASSES': [
            # JSON 解析器
            'rest_framework.parsers.JSONParser',
            # 表单解析器
            'rest_framework.parsers.FormParser',
            # 多部分解析器(文件上传)
            'rest_framework.parsers.MultiPartParser',
            # 文件解析器
            'rest_framework.parsers.FileUploadParser',
        ],

        # 版本控制配置
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
        'DEFAULT_VERSION': 'v1',
        'ALLOWED_VERSIONS': ['v1', 'v2'],
        'VERSION_PARAM': 'version',

        # 元数据配置
        'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',

        # 内容类型协商
        'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',

        # 异常处理
        'EXCEPTION_HANDLER': 'apps.core.exceptions.custom_exception_handler',

        # 测试配置
        'TEST_REQUEST_DEFAULT_FORMAT': 'json',
        'TEST_REQUEST_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.MultiPartRenderer',
        ],

        # 限制配置
        'DEFAULT_THROTTLE_CLASSES': [
            'rest_framework.throttling.AnonRateThrottle',
            'rest_framework.throttling.UserRateThrottle',
            'rest_framework.throttling.ScopedRateThrottle',
        ],
        'DEFAULT_THROTTLE_RATES': {
            'anon': '100/hour',
            'user': '1000/hour',
            'upload': '10/hour',
            'export': '5/hour',
        },

        # 文档配置
        'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
        'DEFAULT_AUTO_FIELD_CLASS': 'django.db.models.BigAutoField',

        # 其他配置
        'NUM_PROXIES': 0,
        'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
        'UNAUTHENTICATED_TOKEN_CLASS': None,
        'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
        'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',
    }

    # JWT 配置
    from datetime import timedelta
    SIMPLE_JWT = {
        'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),  # 访问令牌1小时
        'REFRESH_TOKEN_LIFETIME': timedelta(days=7),   # 刷新令牌7天
        'ROTATE_REFRESH_TOKENS': True,                   # 启用令牌轮换
        'BLACKLIST_AFTER_ROTATION': True,                 # 轮换后加入黑名单
        'UPDATE_LAST_LOGIN': True,                       # 更新最后登录时间

        'ALGORITHM': 'HS256',                            # 算法
        'SIGNING_KEY': SECRET_KEY,                        # 签名密钥
        'VERIFYING_KEY': None,                            # 验证密钥

        'AUDIENCE': None,                                # 受众
        'ISSUER': None,                                  # 发行者
        'JWK_URL': None,                                 # JWK URL
        'LEEWAY': 0,                                     # 容差时间

        'AUTH_HEADER_TYPES': ('Bearer',),                # 认证头类型
        'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',        # 认证头名称
        'USER_ID_FIELD': 'id',                          # 用户ID字段
        'USER_ID_CLAIM': 'user_id',                     # 用户ID声明
        'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',

        'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
        'TOKEN_TYPE_CLAIM': 'token_type',
        'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',

        'JTI_CLAIM': 'jti',
        'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
        'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
        'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),

        # 自定义配置
        'CUSTOM_RESPONSE_PAYLOAD_EXP': {
            'error': 'Token is expired',
            'code': 'token_not_valid'
        },
        'CUSTOM_RESPONSE_PAYLOAD_DISABLED': {
            'error': 'Token is disabled',
            'code': 'token_not_valid'
        },
        'CUSTOM_RESPONSE_PAYLOAD_INVALID': {
            'error': 'Token is invalid',
            'code': 'token_not_valid'
        },
    }

    # CORS 配置
    CORS_ALLOWED_ORIGINS = [
        "http://localhost:3000",
        "http://127.0.0.1:3000",
        "http://localhost:8080",
        "http://127.0.0.1:8080",
    ]

    CORS_ALLOWED_ORIGIN_REGEXES = [
        r'^https://.*\.yourdomain\.com$',
    ]

    CORS_ALLOW_CREDENTIALS = True

    CORS_ALLOW_ALL_ORIGINS = False  # 生产环境必须为 False

    CORS_ALLOW_HEADERS = [
        'accept',
        'accept-encoding',
        'authorization',
        'content-type',
        'dnt',
        'origin',
        'user-agent',
        'x-csrftoken',
        'x-requested-with',
    ]

    CORS_EXPOSE_HEADERS = [
        'content-type',
        'x-csrftoken',
    ]

    # 文档配置
    SPECTACULAR_SETTINGS = {
        'TITLE': 'DRF API Documentation',
        'DESCRIPTION': 'Django REST Framework API 文档',
        'VERSION': '1.0.0',
        'SERVE_INCLUDE_SCHEMA': False,
        'COMPONENT_SPLIT_REQUEST': True,
        'SCHEMA_PATH_PREFIX': r'/api/v[0-9]',
        'SERVERS': [
            {'url': 'http://localhost:8000', 'description': 'Development server'},
            {'url': 'https://api.yourdomain.com', 'description': 'Production server'},
        ],
        'TAGS': [
            {'name': 'Users', 'description': '用户管理'},
            {'name': 'Posts', 'description': '文章管理'},
            {'name': 'Comments', 'description': '评论管理'},
            {'name': 'Authentication', 'description': '认证相关'},
        ],
        'OPERATION_ID_FRIENDLY': True,
        'PREPROCESSING_HOOKS': [],
        'POSTPROCESSING_HOOKS': [],
        'UI_HIDE_DOCS': False,
        'SWAGGER_UI_SETTINGS': {
            'deepLinking': True,
            'persistAuthorization': True,
            'displayRequestDuration': True,
        },
        'REDOC_UI_SETTINGS': {
            'hideDownloadButton': True,
            'hideHostname': True,
        },
        'GENERATE_TESTS': False,
        'GENERATE_COMMAND_DOCS': True,
    }
    ```

02.应用模块化设计
    按功能域划分应用
    核心应用设计
    业务应用设计
    模块化设计原则:
    """
    DRF 应用模块化设计原则:

    1. 单一职责原则
    - 每个应用专注一个业务域
    - 避免应用间过度耦合
    - 明确的边界和接口

    2. 依赖倒置原则
    - 高层模块不依赖低层模块
    - 都依赖抽象接口
    - 便于测试和维护

    3. 开放封闭原则
    - 对扩展开放
    - 对修改封闭
    - 插件化架构设计

    4. 接口隔离原则
    - 客户端不应依赖不需要的接口
    - 小而专注的接口设计
    - 避免臃肿的API

    推荐的应用结构:

    apps/
    ├── core/                   # 核心功能应用
    │   ├── models.py           # 基础模型
    │   ├── views.py            # 通用视图
    │   ├── serializers.py      # 通用序列化器
    │   ├── permissions.py      # 权限类
    │   ├── pagination.py       # 分页类
    │   ├── exceptions.py       # 异常处理
    │   ├── filters.py          # 过滤器
    │   ├── utils.py            # 工具函数
    │   └── middleware.py       # 中间件
    │
    ├── users/                  # 用户管理应用
    │   ├── models.py           # 用户模型
    │   ├── views.py            # 用户视图
    │   ├── serializers.py      # 用户序列化器
    │   ├── urls.py             # 用户URL
    │   ├── permissions.py      # 用户权限
    │   ├── managers.py         # 管理器
    │   ├── signals.py          # 信号
    │   ├── tasks.py            # 异步任务
    │   └── tests.py            # 测试
    │
    ├── authentication/         # 认证专用应用
    │   ├── views.py            # 认证视图
    │   ├── serializers.py      # 认证序列化器
    │   ├── urls.py             # 认证URL
    │   ├── tokens.py           # Token管理
    │   ├── backends.py         # 认证后端
    │   └── permissions.py      # 认证权限
    │
    └── content/                # 内容管理应用
        ├── models.py           # 内容模型
        ├── views.py            # 内容视图
        ├── serializers.py      # 内容序列化器
        ├── urls.py             # 内容URL
        ├── permissions.py      # 内容权限
        └── filters.py          # 内容过滤器

    核心应用设计示例:

    apps/core/models.py:
    from django.db import models
    from django.contrib.auth import get_user_model
    import uuid

    User = get_user_model()

    class TimeStampedModel(models.Model):
        """时间戳抽象模型"""
        id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
        created_at = models.DateTimeField('创建时间', auto_now_add=True)
        updated_at = models.DateTimeField('更新时间', auto_now=True)

        class Meta:
            abstract = True

    class SoftDeleteModel(TimeStampedModel):
        """软删除抽象模型"""
        is_deleted = models.BooleanField('已删除', default=False)
        deleted_at = models.DateTimeField('删除时间', null=True, blank=True)
        deleted_by = models.ForeignKey(
            User,
            on_delete=models.SET_NULL,
            null=True,
            blank=True,
            verbose_name='删除者'
        )

        class Meta:
            abstract = True

        def delete(self, using=None, keep_parents=False):
            """软删除"""
            from django.utils import timezone
            self.is_deleted = True
            self.deleted_at = timezone.now()
            self.save()

        def hard_delete(self, using=None, keep_parents=False):
            """物理删除"""
            super().delete(using=using, keep_parents=keep_parents)

    apps/core/views.py:
    from rest_framework import status
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from rest_framework.generics import GenericAPIView
    from rest_framework import mixins

    class BaseAPIView(APIView):
        """基础 API 视图"""

        def get_success_response(self, data=None, message="操作成功", status_code=status.HTTP_200_OK):
            """统一的成功响应格式"""
            response_data = {
                'success': True,
                'message': message,
                'data': data,
                'timestamp': timezone.now().isoformat(),
            }
            return Response(response_data, status=status_code)

        def get_error_response(self, message="操作失败", errors=None, status_code=status.HTTP_400_BAD_REQUEST):
            """统一的错误响应格式"""
            response_data = {
                'success': False,
                'message': message,
                'errors': errors,
                'timestamp': timezone.now().isoformat(),
            }
            return Response(response_data, status=status_code)

    class BaseGenericAPIView(GenericAPIView):
        """基础通用视图"""

        def get_serializer_context(self):
            context = super().get_serializer_context()
            context.update({
                'request': self.request,
                'format': self.format_kwarg,
                'view': self
            })
            return context

        def get_queryset(self):
            queryset = super().get_queryset()
            # 自动过滤已删除的记录
            if hasattr(queryset.model, 'is_deleted'):
                queryset = queryset.filter(is_deleted=False)
            return queryset

    class BaseListModelMixin(mixins.ListModelMixin):
        """基础列表混入"""

        def list(self, request, *args, **kwargs):
            queryset = self.filter_queryset(self.get_queryset())

            page = self.paginate_queryset(queryset)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)

            serializer = self.get_serializer(queryset, many=True)
            return self.get_success_response(
                data=serializer.data,
                message="获取列表成功"
            )

    apps/core/pagination.py:
    from rest_framework.pagination import PageNumberPagination
    from rest_framework.response import Response
    from collections import OrderedDict

    class StandardResultsSetPagination(PageNumberPagination):
        """标准分页器"""

        page_size = 20
        page_size_query_param = 'page_size'
        page_query_param = 'page'
        max_page_size = 100

        def get_paginated_response(self, data):
            return Response(OrderedDict([
                ('count', self.page.paginator.count),
                ('next', self.get_next_link()),
                ('previous', self.get_previous_link()),
                ('current_page', self.page.number),
                ('total_pages', self.page.paginator.num_pages),
                ('page_size', self.page_size),
                ('results', data)
            ]))

    class LargeResultsSetPagination(PageNumberPagination):
        """大结果集分页器"""

        page_size = 50
        page_size_query_param = 'page_size'
        max_page_size = 1000

    apps/core/exceptions.py:
    from rest_framework.views import exception_handler
    from rest_framework.response import Response
    from rest_framework import status
    from django.http import Http404
    import logging

    logger = logging.getLogger(__name__)

    def custom_exception_handler(exc, context):
        """自定义异常处理器"""

        # 调用 DRF 默认异常处理器
        response = exception_handler(exc, context)

        if response is not None:
            # 自定义响应格式
            custom_response_data = {
                'success': False,
                'message': '请求处理失败',
                'error': {
                    'code': exc.__class__.__name__,
                    'detail': response.data.get('detail', str(exc)),
                    'fields': response.data if isinstance(response.data, dict) else None
                },
                'timestamp': timezone.now().isoformat(),
            }

            # 记录错误日志
            logger.error(f"API Error: {exc.__class__.__name__}: {exc}")

            response.data = custom_response_data

        return response

    # 自定义异常类
    class APIException(Exception):
        """API 基础异常类"""

        def __init__(self, message, status_code=status.HTTP_400_BAD_REQUEST):
            self.message = message
            self.status_code = status_code
            super().__init__(message)

    class ValidationAPIException(APIException):
        """验证异常类"""

        def __init__(self, message, field_errors=None):
            super().__init__(message, status.HTTP_400_BAD_REQUEST)
            self.field_errors = field_errors or {}

    class NotFoundAPIException(APIException):
        """资源未找到异常类"""

        def __init__(self, message="请求的资源不存在"):
            super().__init__(message, status.HTTP_404_NOT_FOUND)

    class PermissionAPIException(APIException):
        """权限异常类"""

        def __init__(self, message="权限不足"):
            super().__init__(message, status.HTTP_403_FORBIDDEN)

    class AuthenticationAPIException(APIException):
        """认证异常类"""

        def __init__(self, message="认证失败"):
            super().__init__(message, status.HTTP_401_UNAUTHORIZED)

    class RateLimitAPIException(APIException):
        """限流异常类"""

        def __init__(self, message="请求频率超限"):
            super().__init__(message, status.HTTP_429_TOO_MANY_REQUESTS)
    ```

03.中间件配置
    自定义中间件开发
    请求响应处理
    全局中间件应用
    中间件配置示例:
    ```python
    # apps/core/middleware.py
    import time
    import uuid
    import logging
    from django.utils.deprecation import MiddlewareMixin
    from django.http import JsonResponse
    from django.conf import settings
    from rest_framework import status

    logger = logging.getLogger('api')

    class RequestLoggingMiddleware(MiddlewareMixin):
        """请求日志中间件"""

        def process_request(self, request):
            """处理请求"""
            # 生成唯一请求ID
            request.request_id = str(uuid.uuid4())

            # 记录请求开始时间
            request.start_time = time.time()

            # 记录请求信息
    user_info = "Anonymous"
    if hasattr(request, 'user') and request.user.is_authenticated:
        user_info = f"{request.user.username}({request.user.id})"

    logger.info(
        f"Request started - ID: {request.request_id}, "
        f"Method: {request.method}, Path: {request.path}, "
        f"User: {user_info}, IP: {self.get_client_ip(request)}"
    )

    def process_response(self, request, response):
        """处理响应"""
        if hasattr(request, 'start_time'):
            duration = time.time() - request.start_time

            # 记录响应信息
            logger.info(
                f"Request finished - ID: {request.request_id}, "
                f"Status: {response.status_code}, "
                f"Duration: {duration:.3f}s"
            )

            # 添加响应头
            response['X-Request-ID'] = request.request_id
            response['X-Response-Time'] = f"{duration:.3f}"

        return response

    def get_client_ip(self, request):
        """获取客户端IP"""
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip

        class SecurityMiddleware(MiddlewareMixin):
            """安全中间件"""

        def process_request(self, request):
            """处理请求安全检查"""
            # 检查请求大小
            content_length = request.META.get('CONTENT_LENGTH', 0)
            max_content_length = getattr(settings, 'MAX_CONTENT_LENGTH', 10 * 1024 * 1024)  # 10MB

            if int(content_length) > max_content_length:
                return JsonResponse(
                    {
                        'success': False,
                        'message': '请求体过大',
                        'error': {
                            'code': 'CONTENT_TOO_LARGE',
                            'max_size': max_content_length
                        }
                    },
                    status=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE
                )

            # 检查可疑请求
            suspicious_headers = [
                'HTTP_X_FORWARDED_HOST',
                'HTTP_X_ORIGINATING_IP',
                'HTTP_X_REAL_IP',
            ]

            for header in suspicious_headers:
                if header in request.META:
                    logger.warning(
                        f"Suspicious header detected: {header} - Request ID: {getattr(request, 'request_id', 'unknown')}"
                    )

        class RateLimitMiddleware(MiddlewareMixin):
            """限流中间件"""

        def process_request(self, request):
            """处理请求限流"""
            # 这里可以实现基于IP的简单限流
            # 复杂限流建议使用DRF的throttling
            pass

        class CORSMiddleware(MiddlewareMixin):
            """CORS中间件(简化版,推荐使用 django-cors-headers)"""

        def process_response(self, request, response):
            """处理响应CORS头"""
            origin = request.META.get('HTTP_ORIGIN')

            # 开发环境允许所有来源
            if getattr(settings, 'DEBUG', False):
                if origin:
                    response['Access-Control-Allow-Origin'] = origin
                response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
                response['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
                response['Access-Control-Allow-Credentials'] = 'true'

            return response

        # API 中间件类
        class APIMiddleware:
            """API专用中间件"""

        def __init__(self, get_response):
            self.get_response = get_response

        def __call__(self, request):
            # 添加API版本检查
            if request.path.startswith('/api/'):
                return self.process_api_request(request)

            return self.get_response(request)

        def process_api_request(self, request):
            """处理API请求"""
            # 添加自定义处理逻辑
            response = self.get_response(request)
            return response

        # myproject/settings/base.py - 中间件配置
        MIDDLEWARE = [
            # 安全中间件
            'django.middleware.security.SecurityMiddleware',

            # CORS中间件(需要安装 django-cors-headers)
            'corsheaders.middleware.CorsMiddleware',

            # 会话中间件
            'django.contrib.sessions.middleware.SessionMiddleware',

            # 通用中间件
            'django.middleware.common.CommonMiddleware',

            # CSRF中间件
            'django.middleware.csrf.CsrfViewMiddleware',

            # 认证中间件
            'django.contrib.auth.middleware.AuthenticationMiddleware',

            # 消息中间件
            'django.contrib.messages.middleware.MessageMiddleware',

            # 点击劫持防护
            'django.middleware.clickjacking.XFrameOptionsMiddleware',

            # 自定义中间件
            'apps.core.middleware.RequestLoggingMiddleware',
            'apps.core.middleware.SecurityMiddleware',
        ]

        # 开发环境额外中间件
        if DEBUG:
            MIDDLEWARE += [
                'debug_toolbar.middleware.DebugToolbarMiddleware',
            ]
        ```

2.3 DRF核心配置

01.认证系统配置
    a.多种认证方式
        JWT认证实现
        Session认证配置
        Token认证使用
    b.认证系统配置示例
        # apps/authentication/authentication.py
        from rest_framework_simplejwt.authentication import JWTAuthentication
        from rest_framework.authentication import SessionAuthentication, BasicAuthentication
        from rest_framework.exceptions import AuthenticationFailed
        from django.contrib.auth import get_user_model
        import logging

        User = get_user_model()
        logger = logging.getLogger('authentication')

        class CustomJWTAuthentication(JWTAuthentication):
            """自定义JWT认证"""

            def authenticate(self, request):
                """自定义认证逻辑"""
                # 调用父类方法
                auth_result = super().authenticate(request)

                if auth_result is not None:
                    user, token = auth_result

                    # 检查用户状态
                    if not user.is_active:
                        raise AuthenticationFailed('用户账户已被禁用')

                    # 记录认证日志
                    logger.info(f"User {user.username} authenticated via JWT")

                    return (user, token)

                return None

            def get_validated_token(self, raw_token):
                """验证token并返回有效token"""
                # 可以添加自定义token验证逻辑
                try:
                    return super().get_validated_token(raw_token)
                except Exception as e:
                    logger.error(f"JWT validation failed: {str(e)}")
                    raise

        class APIKeyAuthentication:
            """API密钥认证"""

            def authenticate(self, request):
                """API密钥认证"""
                api_key = request.headers.get('X-API-Key')

                if not api_key:
                    return None

                try:
                    from .models import APIKey
                    api_key_obj = APIKey.objects.select_related('user').get(
                        key=api_key,
                        is_active=True
                    )

                    if not api_key_obj.user.is_active:
                        raise AuthenticationFailed('用户账户已被禁用')

                    # 更新API密钥使用统计
                    api_key_obj.usage_count += 1
                    api_key_obj.last_used_at = timezone.now()
                    api_key_obj.save(update_fields=['usage_count', 'last_used_at'])

                    return (api_key_obj.user, api_key_obj)

                except APIKey.DoesNotExist:
                    raise AuthenticationFailed('无效的API密钥')

            def authenticate_header(self, request):
                return 'API-Key'

        # apps/authentication/models.py
        from django.db import models
        from django.conf import settings
        from django.utils import timezone
        import secrets

        class APIKey(models.Model):
            """API密钥模型"""
            user = models.ForeignKey(
                settings.AUTH_USER_MODEL,
                on_delete=models.CASCADE,
                related_name='api_keys',
                verbose_name='用户'
            )
            name = models.CharField('名称', max_length=100)
            key = models.CharField('密钥', max_length=64, unique=True)
            prefix = models.CharField('前缀', max_length=8)
            is_active = models.BooleanField('是否激活', default=True)
            expires_at = models.DateTimeField('过期时间', null=True, blank=True)
            usage_count = models.PositiveIntegerField('使用次数', default=0)
            last_used_at = models.DateTimeField('最后使用时间', null=True, blank=True)
            created_at = models.DateTimeField('创建时间', auto_now_add=True)

            class Meta:
                verbose_name = 'API密钥'
                verbose_name_plural = 'API密钥'
                ordering = ['-created_at']

            def __str__(self):
                return f"{self.name} ({self.prefix}...)"

            def save(self, *args, **kwargs):
                if not self.key:
                    self.key = self.generate_key()
                super().save(*args, **kwargs)

            def generate_key(self):
                """生成API密钥"""
                while True:
                    key = secrets.token_urlsafe(48)
                    prefix = key[:8]
                    if not APIKey.objects.filter(key=key).exists():
                        self.prefix = prefix
                        return key

            def is_valid(self):
                """检查密钥是否有效"""
                if not self.is_active:
                    return False
                if self.expires_at and self.expires_at < timezone.now():
                    return False
                return True

        # apps/authentication/views.py
        from rest_framework import status, generics, permissions
        from rest_framework.response import Response
        from rest_framework.decorators import api_view, permission_classes
        from rest_framework_simplejwt.tokens import RefreshToken
        from django.contrib.auth import authenticate
        from django.contrib.auth import get_user_model
        from .serializers import (
            LoginSerializer,
            RegisterSerializer,
            UserSerializer,
            APIKeySerializer,
            PasswordChangeSerializer,
            PasswordResetSerializer
        )

        User = get_user_model()

        @api_view(['POST'])
        @permission_classes([permissions.AllowAny])
        def login_view(request):
            """登录视图"""
            serializer = LoginSerializer(data=request.data)
            if serializer.is_valid():
                user = serializer.validated_data['user']
                refresh = RefreshToken.for_user(user)

                return Response({
                    'success': True,
                    'message': '登录成功',
                    'data': {
                        'refresh': str(refresh),
                        'access': str(refresh.access_token),
                        'user': UserSerializer(user).data
                    }
                }, status=status.HTTP_200_OK)

            return Response({
                'success': False,
                'message': '登录失败',
                'errors': serializer.errors
            }, status=status.HTTP_400_BAD_REQUEST)

        @api_view(['POST'])
        @permission_classes([permissions.AllowAny])
        def register_view(request):
            """注册视图"""
            serializer = RegisterSerializer(data=request.data)
            if serializer.is_valid():
                user = serializer.save()
                return Response({
                    'success': True,
                    'message': '注册成功',
                    'data': UserSerializer(user).data
                }, status=status.HTTP_201_CREATED)

            return Response({
                'success': False,
                'message': '注册失败',
                'errors': serializer.errors
            }, status=status.HTTP_400_BAD_REQUEST)

        @api_view(['POST'])
        def logout_view(request):
            """登出视图"""
            try:
                refresh_token = request.data.get('refresh')
                if refresh_token:
                    token = RefreshToken(refresh_token)
                    token.blacklist()

                return Response({
                    'success': True,
                    'message': '登出成功'
                }, status=status.HTTP_200_OK)
            except Exception as e:
                return Response({
                    'success': False,
                    'message': '登出失败',
                    'error': str(e)
                }, status=status.HTTP_400_BAD_REQUEST)

        @api_view(['GET'])
        def user_profile_view(request):
            """用户资料视图"""
            if request.user.is_authenticated:
                return Response({
                    'success': True,
                    'data': UserSerializer(request.user).data
                }, status=status.HTTP_200_OK)
            else:
                return Response({
                    'success': False,
                    'message': '用户未认证'
                }, status=status.HTTP_401_UNAUTHORIZED)

        class APIKeyListCreateView(generics.ListCreateAPIView):
            """API密钥列表和创建视图"""
            serializer_class = APIKeySerializer
            permission_classes = [permissions.IsAuthenticated]

            def get_queryset(self):
                return self.request.user.api_keys.filter(is_active=True)

            def perform_create(self, serializer):
                serializer.save(user=self.request.user)

        # apps/authentication/serializers.py
        from rest_framework import serializers
        from django.contrib.auth import authenticate
        from django.contrib.auth import get_user_model
        from django.contrib.auth.password_validation import validate_password
        from django.core.exceptions import ValidationError

        User = get_user_model()

        class LoginSerializer(serializers.Serializer):
            """登录序列化器"""
            email = serializers.EmailField()
            password = serializers.CharField(write_only=True)

            def validate(self, attrs):
                email = attrs.get('email')
                password = attrs.get('password')

                if email and password:
                    user = authenticate(
                        request=self.context.get('request'),
                        username=email,
                        password=password
                    )

                    if not user:
                        raise serializers.ValidationError('邮箱或密码错误')

                    if not user.is_active:
                        raise serializers.ValidationError('用户账户已被禁用')

                    attrs['user'] = user
                    return attrs

                raise serializers.ValidationError('邮箱和密码都是必填项')

        class RegisterSerializer(serializers.ModelSerializer):
            """注册序列化器"""
            password = serializers.CharField(write_only=True, min_length=8)
            password2 = serializers.CharField(write_only=True)

            class Meta:
                model = User
                fields = ['email', 'username', 'password', 'password2', 'first_name', 'last_name']

            def validate_email(self, value):
                """验证邮箱唯一性"""
                if User.objects.filter(email__iexact=value).exists():
                    raise serializers.ValidationError('该邮箱已被注册')
                return value

            def validate_username(self, value):
                """验证用户名唯一性"""
                if User.objects.filter(username__iexact=value).exists():
                    raise serializers.ValidationError('该用户名已被使用')
                return value

            def validate_password(self, value):
                """验证密码强度"""
                validate_password(value)
                return value

            def validate(self, attrs):
                """验证两次密码是否一致"""
                if attrs['password'] != attrs['password2']:
                    raise serializers.ValidationError('两次输入的密码不一致')
                return attrs

            def create(self, validated_data):
                """创建用户"""
                validated_data.pop('password2')
                user = User.objects.create_user(**validated_data)
                return user

        class PasswordChangeSerializer(serializers.Serializer):
            """密码修改序列化器"""
            old_password = serializers.CharField(write_only=True)
            new_password = serializers.CharField(write_only=True, min_length=8)
            new_password2 = serializers.CharField(write_only=True)

            def validate_old_password(self, value):
                """验证旧密码"""
                user = self.context['request'].user
                if not user.check_password(value):
                    raise serializers.ValidationError('旧密码不正确')
                return value

            def validate(self, attrs):
                """验证新密码"""
                if attrs['new_password'] != attrs['new_password2']:
                    raise serializers.ValidationError('两次输入的新密码不一致')
                validate_password(attrs['new_password'])
                return attrs

            def save(self):
                """保存新密码"""
                user = self.context['request'].user
                user.set_password(self.validated_data['new_password'])
                user.save()
                return user

        class APIKeySerializer(serializers.ModelSerializer):
            """API密钥序列化器"""
            key_preview = serializers.SerializerMethodField()

            class Meta:
                model = APIKey
                fields = ['id', 'name', 'key_preview', 'is_active', 'expires_at', 'usage_count', 'last_used_at', 'created_at']
                read_only_fields = ['key_preview', 'usage_count', 'last_used_at', 'created_at']

            def get_key_preview(self, obj):
                """获取密钥预览(只显示前8位)"""
                if obj.key:
                    return f"{obj.prefix}..."
                return None

        # myproject/settings/base.py - 认证配置更新
        REST_FRAMEWORK.update({
            # 自定义认证类
            'DEFAULT_AUTHENTICATION_CLASSES': [
                'apps.authentication.authentication.CustomJWTAuthentication',
                'apps.authentication.authentication.APIKeyAuthentication',
                'rest_framework.authentication.SessionAuthentication',
                'rest_framework.authentication.BasicAuthentication',
            ],

            # 认证配置选项
            'AUTHENTICATION_CLASSES': {
                'jwt': 'apps.authentication.authentication.CustomJWTAuthentication',
                'api_key': 'apps.authentication.authentication.APIKeyAuthentication',
                'session': 'rest_framework.authentication.SessionAuthentication',
                'basic': 'rest_framework.authentication.BasicAuthentication',
            },

            # 认证优先级
            'AUTHENTICATION_ORDER': [
                'jwt',
                'api_key',
                'session',
                'basic',
            ],

            # 未认证用户处理
            'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
            'UNAUTHENTICATED_TOKEN_CLASS': None,

            # Token认证配置(如果使用)
            'DEFAULT_TOKEN_CLASS': 'rest_framework.authtoken.models.Token',
            'TOKEN_EXPIRE_SECONDS': getattr(settings, 'TOKEN_EXPIRE_SECONDS', 86400),

            # 权限缓存
            'DEFAULT_PERMISSION_CLASSES': [
                'rest_framework.permissions.IsAuthenticated',
            ],
            'DEFAULT_PERMISSION_EXEMPT_CLASSES': [
                'rest_framework.permissions.AllowAny',
            ],
        })

02.权限系统配置
    a.权限类设计
        基础权限类使用
        自定义权限类
        对象级权限控制
    b.权限系统示例
        # apps/core/permissions.py
        from rest_framework import permissions
        from django.contrib.auth import get_user_model

        User = get_user_model()

        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

        class IsAdminOrReadOnly(permissions.BasePermission):
            """只有管理员才能编辑"""

        def has_permission(self, request, view):
            # 读取权限对所有请求开放
            if request.method in permissions.SAFE_METHODS:
                return True

            # 写入权限只允许管理员
            return request.user.is_staff

        def has_object_permission(self, request, view, obj):
            # 读取权限对所有请求开放
            if request.method in permissions.SAFE_METHODS:
                return True

            # 写入权限只允许管理员
            return request.user.is_staff

        class IsAuthenticatedOrCreateOnly(permissions.BasePermission):
            """认证用户可以创建,其他用户只读"""

        def has_permission(self, request, view):
            if request.method == 'POST':
                return True
            return request.user.is_authenticated

        class IsOwnerOrAdmin(permissions.BasePermission):
            """只有对象所有者或管理员可以访问"""

        def has_object_permission(self, request, view, obj):
            return obj.author == request.user or request.user.is_staff

        class IsVerifiedUser(permissions.BasePermission):
            """只有已验证用户才能访问"""

        def has_permission(self, request, view):
            return (
                request.user.is_authenticated and
                getattr(request.user, 'is_verified', False)
            )

        class IsPremiumUser(permissions.BasePermission):
            """只有高级用户才能访问"""

        def has_permission(self, request, view):
            return (
                request.user.is_authenticated and
                getattr(request.user, 'is_premium', False)
            )

        class IsActiveUser(permissions.BasePermission):
            """只有活跃用户才能访问"""

        def has_permission(self, request, view):
            return (
                request.user.is_authenticated and
                request.user.is_active
            )

        class TimeBasedPermission(permissions.BasePermission):
            """基于时间的权限控制"""

        def __init__(self, allowed_hours=None):
            self.allowed_hours = allowed_hours or range(9, 18)  # 默认9-18点

        def has_permission(self, request, view):
            from django.utils import timezone
            current_hour = timezone.now().hour
            return current_hour in self.allowed_hours

        # 使用示例:permission_classes = [TimeBasedPermission(allowed_hours=range(8, 20))]

        class IPWhitelistPermission(permissions.BasePermission):
            """IP白名单权限"""

        def __init__(self, allowed_ips=None):
            self.allowed_ips = allowed_ips or ['127.0.0.1', '192.168.1.0/24']

        def has_permission(self, request, view):
            client_ip = self.get_client_ip(request)
            return self.is_ip_allowed(client_ip, self.allowed_ips)

        def get_client_ip(self, request):
            x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
            if x_forwarded_for:
                ip = x_forwarded_for.split(',')[0]
            else:
                ip = request.META.get('REMOTE_ADDR')
            return ip

        def is_ip_allowed(self, client_ip, allowed_ips):
            import ipaddress
            client_ip_obj = ipaddress.ip_address(client_ip)

            for allowed_ip in allowed_ips:
                try:
                    allowed_ip_obj = ipaddress.ip_network(allowed_ip)
                    if client_ip_obj in allowed_ip_obj:
                        return True
                except ValueError:
                    # 简单IP匹配
                    if client_ip == allowed_ip:
                        return True
            return False

        class RoleBasedPermission(permissions.BasePermission):
            """基于角色的权限控制"""

        role_permissions = {
            'admin': ['create', 'read', 'update', 'delete'],
            'editor': ['create', 'read', 'update'],
            'author': ['create', 'read', 'update'],
            'viewer': ['read'],
        }

        def __init__(self, required_permission=None):
            self.required_permission = required_permission

        def has_permission(self, request, view):
            if not request.user.is_authenticated:
                return False

            user_role = self.get_user_role(request.user)
            if not user_role:
                return False

            allowed_permissions = self.role_permissions.get(user_role, [])
            if not self.required_permission:
                return True

            return self.required_permission in allowed_permissions

        def get_user_role(self, user):
            """获取用户角色"""
            if user.is_superuser:
                return 'admin'
            elif user.is_staff:
                return 'editor'
            elif hasattr(user, 'profile') and user.profile.role:
                return user.profile.role
            return 'viewer'

        class ResourceOwnerPermission(permissions.BasePermission):
            """资源所有者权限"""

        def has_object_permission(self, request, view, obj):
            # 检查对象是否有author字段
            if hasattr(obj, 'author'):
                return obj.author == request.user

            # 检查对象是否有user字段
            if hasattr(obj, 'user'):
                return obj.user == request.user

            # 检查对象是否有owner字段
            if hasattr(obj, 'owner'):
                return obj.owner == request.user

            # 默认拒绝访问
            return False

        class ConditionalPermission(permissions.BasePermission):
            """条件权限"""

        def __init__(self, condition_func=None):
            self.condition_func = condition_func

        def has_permission(self, request, view):
            if not self.condition_func:
                return True
            return self.condition_func(request, view)

        def has_object_permission(self, request, view, obj):
            if not self.condition_func:
                return True
            return self.condition_func(request, view, obj)

        # 权限类工厂函数
        def create_permission_class(check_func):
            """创建权限类的工厂函数"""
            class DynamicPermission(permissions.BasePermission):
                def has_permission(self, request, view):
                    return check_func(request, view)

                def has_object_permission(self, request, view, obj):
                    return check_func(request, view, obj)

            return DynamicPermission

        # 使用示例:
        # IsUserOwner = create_permission_class(lambda request, view: request.user.is_authenticated)
        # CanEditPost = create_permission_class(lambda request, view: request.user.has_perm('blog.change_post'))

        # apps/core/decorators.py
        from functools import wraps
        from rest_framework.response import Response
        from rest_framework import status

        def permission_required(permission_classes):
            """权限装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(self, request, *args, **kwargs):
                    # 检查权限
                    for permission_class in permission_classes:
                        permission = permission_class()

                        # 检查全局权限
                        if not permission.has_permission(request, self):
                            return Response({
                                'success': False,
                                'message': '权限不足',
                                'error': {
                                    'code': 'PERMISSION_DENIED',
                                    'permission': permission_class.__name__
                                }
                            }, status=status.HTTP_403_FORBIDDEN)

                        # 如果有对象权限,也需要检查
                        if hasattr(self, 'get_object'):
                            obj = self.get_object()
                            if not permission.has_object_permission(request, self, obj):
                                return Response({
                                    'success': False,
                                    'message': '权限不足',
                                    'error': {
                                        'code': 'OBJECT_PERMISSION_DENIED',
                                        'permission': permission_class.__name__
                                    }
                                }, status=status.HTTP_403_FORBIDDEN)

                    return view_func(self, request, *args, **kwargs)

                return wrapper
            return decorator

        # 在视图中的使用示例
        from rest_framework import viewsets
        from .permissions import IsOwnerOrReadOnly, IsAdminOrReadOnly

        class PostViewSet(viewsets.ModelViewSet):
            queryset = Post.objects.all()
            serializer_class = PostSerializer

            def get_permissions(self):
                """根据操作类型动态设置权限"""
                if self.action == 'create':
                    permission_classes = [permissions.IsAuthenticated]
                elif self.action in ['update', 'partial_update', 'destroy']:
                    permission_classes = [IsOwnerOrReadOnly]
                elif self.action == 'list':
                    permission_classes = [permissions.AllowAny]
                else:
                    permission_classes = [IsOwnerOrReadOnly]

                return [permission() for permission in permission_classes]

            @permission_required([IsOwnerOrReadOnly])
            def update(self, request, *args, **kwargs):
                return super().update(request, *args, **kwargs)

            @permission_required([IsAdminOrReadOnly])
            def destroy(self, request, *args, **kwargs):
                return super().destroy(request, *args, **kwargs)

        # myproject/settings/base.py - 权限配置更新
        REST_FRAMEWORK.update({
            # 默认权限配置
            'DEFAULT_PERMISSION_CLASSES': [
                'rest_framework.permissions.IsAuthenticated',
            ],

            # 自定义权限类注册
            'CUSTOM_PERMISSION_CLASSES': {
                'is_owner': 'apps.core.permissions.IsOwnerOrReadOnly',
                'is_admin': 'apps.core.permissions.IsAdminOrReadOnly',
                'is_verified': 'apps.core.permissions.IsVerifiedUser',
                'is_premium': 'apps.core.permissions.IsPremiumUser',
                'is_active': 'apps.core.permissions.IsActiveUser',
            },

            # 权限检查缓存
            'PERMISSION_CACHE_TIMEOUT': 300,  # 5分钟

            # 权限检查优化
            'PERMISSION_CHECK_OPTIMIZATION': True,

            # 异常处理
            'PERMISSION_DENIED_EXCEPTION': 'rest_framework.exceptions.PermissionDenied',

            # 权限类查找顺序
            'PERMISSION_CLASSES_ORDER': [
                'rest_framework.permissions.AllowAny',
                'rest_framework.permissions.IsAuthenticated',
                'rest_framework.permissions.IsAdminUser',
                'rest_framework.permissions.IsAuthenticatedOrReadOnly',
                'apps.core.permissions.IsOwnerOrReadOnly',
            ],
        })

2.4 开发环境调试

01.调试工具集成
    a.Django Debug Toolbar
        安装和配置
        面板功能使用
        性能分析工具
        Debug Toolbar配置示例:
        ```python
        # requirements-dev.txt
        django-debug-toolbar>=4.2.0
        django-debug-toolbar-request-history>=1.0.0
        django-silk>=5.1.0

        # myproject/settings/development.py
        DEBUG = True

        INSTALLED_APPS += [
            'debug_toolbar',
            'django_extensions',
            'silk',
        ]

        MIDDLEWARE += [
            'debug_toolbar.middleware.DebugToolbarMiddleware',
            'silk.middleware.SilkyMiddleware',
        ]

        # Debug Toolbar 配置
        DEBUG_TOOLBAR_PANELS = [
            'debug_toolbar.panels.versions.VersionsPanel',
            'debug_toolbar.panels.timer.TimerPanel',
            'debug_toolbar.panels.settings.SettingsPanel',
            'debug_toolbar.panels.headers.HeadersPanel',
            'debug_toolbar.panels.request.RequestPanel',
            'debug_toolbar.panels.sql.SQLPanel',
            'debug_toolbar.panels.staticfiles.StaticFilesPanel',
            'debug_toolbar.panels.templates.TemplatesPanel',
            'debug_toolbar.panels.cache.CachePanel',
            'debug_toolbar.panels.signals.SignalsPanel',
            'debug_toolbar.panels.logging.LoggingPanel',
            'debug_toolbar.panels.redirects.RedirectsPanel',
            'debug_toolbar.panels.profiling.ProfilingPanel',
        ]

        DEBUG_TOOLBAR_CONFIG = {
            'DISABLE_PANELS': ['debug_toolbar.panels.profiling.ProfilingPanel'],
            'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG and request.user.is_staff,
            'INSERT_BEFORE': '</body>',
            'RENDER_PANELS': None,
            'RESULTS_CACHE_SIZE': 100,
            'SQL_WARNING_THRESHOLD': 100,  # SQL查询数量警告阈值
        }

        # Django Silk 配置
        SILKY_PYTHON_PROFILER = True
        SILKY_PYTHON_PROFILER_BUILTIN = True
        SILKY_PYTHON_PROFILER_BINARY = True
        SILKY_PYTHON_PROFILER_SAMPLING_FREQUENCY = 0.1
        SILKY_MAX_RECORDED_REQUESTS = 10000
        SILKY_MAX_REQUEST_BODY_SIZE = 10240
        SILKY_META = True
        SILKY_AUTHENTICATION = True
        SILKY_AUTHORISATION = True
        SILKY_ANALYZE_QUERIES = True

        # 自定义调试中间件
        class APIDebugMiddleware:
            """API调试中间件"""

        def __init__(self, get_response):
            self.get_response = get_response

        def __call__(self, request):
            # 记录请求信息
            print(f"\n=== API Request Debug ===")
            print(f"Method: {request.method}")
            print(f"Path: {request.path}")
            print(f"Headers: {dict(request.headers)}")
            print(f"Body: {request.body}")
            print(f"GET params: {request.GET}")
            print(f"POST params: {request.POST}")
            print(f"User: {request.user if hasattr(request, 'user') and request.user.is_authenticated else 'Anonymous'}")

            response = self.get_response(request)

            # 记录响应信息
            print(f"Status Code: {response.status_code}")
            print(f"Response Headers: {dict(response.items())}")
            if hasattr(response, 'content'):
                try:
                    import json
                    if response['Content-Type'] == 'application/json':
                        print(f"Response Body: {json.loads(response.content.decode())}")
                    else:
                        print(f"Response Body: {response.content[:500]}...")
                except:
                    print(f"Response Body: {response.content[:500]}...")
            print("=== End Debug Info ===\n")

            return response

        # 应用调试中间件
        if DEBUG:
            MIDDLEWARE.append('apps.core.middleware.APIDebugMiddleware')
        ```
    b.API调试工具
        DRF Browsable API使用
        Postman配置
        curl命令测试
        API调试工具使用指南:
        """
        DRF Browsable API 配置:

        1. 启用 Browsable API
        REST_FRAMEWORK = {
            'DEFAULT_RENDERER_CLASSES': [
                'rest_framework.renderers.JSONRenderer',
                'rest_framework.renderers.BrowsableAPIRenderer',
            ],
        }

        2. 自定义 Browsable API
        from rest_framework.renderers import BrowsableAPIRenderer

        class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
            """自定义可浏览API渲染器"""

            def get_default_renderer(self, view):
                """获取默认渲染器"""
                # 根据用户偏好返回不同渲染器
                if hasattr(view.request.user, 'profile') and view.request.user.profile.api_theme == 'dark':
                    return 'dark_api_renderer'
                return super().get_default_renderer(view)

            def get_context(self, data, accepted_media_type, renderer_context):
                """自定义上下文"""
                context = super().get_context(data, accepted_media_type, renderer_context)

                # 添加自定义按钮
                context['extra_actions'] = [
                    {
                        'name': 'Export Data',
                        'url': f"{renderer_context['request'].path}?export=true",
                        'method': 'GET',
                        'description': '导出数据'
                    }
                ]

                return context

        3. Browsable API 功能使用
        - 查看 API 端点列表
        - 测试 GET, POST, PUT, DELETE 请求
        - 查看响应格式
        - 查看请求/响应头
        - 查看API文档

        Postman 配置:

        1. 环境变量配置
        环境名称:Development
        基础URL:http://localhost:8000/api/v1

        变量配置:
        - {{base_url}}: http://localhost:8000/api/v1
        - {{token}}: 用户登录后获取的JWT token

        2. 请求集合示例
        用户认证集合:
        - 登录:POST {{base_url}}/auth/login/
        - 注册:POST {{base_url}}/auth/register/
        - 登出:POST {{base_url}}/auth/logout/
        - 用户资料:GET {{base_url}}/users/me/

        Headers 配置:
        - Content-Type: application/json
        - Authorization: Bearer {{token}}

        3. 测试脚本
        // 登录后获取token脚本
        const loginResponse = pm.response.json();
        if (loginResponse.success && loginResponse.data.access) {
            pm.environment.set("token", loginResponse.data.access);
            console.log("Token saved:", loginResponse.data.access);
        }

        curl 命令示例:

        1. 基础请求
        # 获取文章列表
        curl -X GET http://localhost:8000/api/v1/posts/ \
          -H "Content-Type: application/json"

        # 带认证的请求
        curl -X GET http://localhost:8000/api/v1/posts/ \
          -H "Content-Type: application/json" \
          -H "Authorization: Bearer YOUR_JWT_TOKEN"

        2. 创建资源
        curl -X POST http://localhost:8000/api/v1/posts/ \
          -H "Content-Type: application/json" \
          -H "Authorization: Bearer YOUR_JWT_TOKEN" \
          -d '{
                "title": "Test Post",
                "content": "This is a test post content.",
                "is_published": true
            }'

        3. 更新资源
        curl -X PUT http://localhost:8000/api/v1/posts/1/ \
          -H "Content-Type: application/json" \
          -H "Authorization: Bearer YOUR_JWT_TOKEN" \
          -d '{
                "title": "Updated Post Title",
                "content": "Updated post content."
            }'

        4. 删除资源
        curl -X DELETE http://localhost:8000/api/v1/posts/1/ \
          -H "Authorization: Bearer YOUR_JWT_TOKEN"

        5. 带查询参数的请求
        curl -X GET "http://localhost:8000/api/v1/posts/?page=2&page_size=10&search=test" \
          -H "Content-Type: application/json"

        6. 文件上传
        curl -X POST http://localhost:8000/api/v1/posts/1/upload-image/ \
          -H "Authorization: Bearer YOUR_JWT_TOKEN" \
          -F "image=@/path/to/image.jpg"

        7. 批量操作
        curl -X POST http://localhost:8000/api/v1/posts/bulk-publish/ \
          -H "Content-Type: application/json" \
          -H "Authorization: Bearer YOUR_JWT_TOKEN" \
          -d '{
                "post_ids": [1, 2, 3, 4, 5]
            }'
        """
    c.日志配置和使用
        请求响应日志
        SQL查询日志
        错误日志分析
        日志配置和使用示例:
        ```python
        # myproject/settings/base.py - 完整的日志配置
        import os
        from pathlib import Path

        # 日志配置
        LOGGING = {
            'version': 1,
            'disable_existing_loggers': False,

            # 日志格式化器
            'formatters': {
                'verbose': {
                    'format': '[{levelname}] {asctime} {module} {process:d} {thread:d} {message}',
                    'style': '{',
                },
                'simple': {
                    'format': '{levelname} {message}',
                    'style': '{',
                },
                'detailed': {
                    'format': '[{levelname}] {asctime} {module} {funcName} {lineno:d} {message}',
                    'style': '{',
                },
                'json': {
                    'format': '{"level": "{levelname}", "time": "{asctime}", "module": "{module}", "message": "{message}"}',
                    'style': '{',
                },
                'colored': {
                    '()': 'colorlog.ColoredFormatter',
                    'format': '{log_prefix}[{levelname}] {asctime} {module} {message}',
                    'log_colors': {
                        'DEBUG': 'cyan',
                        'INFO': 'green',
                        'WARNING': 'yellow',
                        'ERROR': 'red',
                        'CRITICAL': 'red,bg_white',
                    },
                    'style': '{',
                },
            },

            # 过滤器
            'filters': {
                'require_debug_true': {
                    '()': 'django.utils.log.RequireDebugTrue',
                },
                'require_debug_false': {
                    '()': 'django.utils.log.RequireDebugFalse',
                },
                'sensitive_fields': {
                    '()': 'apps.core.logging.SensitiveFieldsFilter',
                },
                'request_id': {
                    '()': 'apps.core.logging.RequestIDFilter',
                },
                'user_filter': {
                    '()': 'apps.core.logging.UserFilter',
                },
            },

            # 处理器
            'handlers': {
                # 控制台处理器(开发环境)
                'console': {
                    'level': 'DEBUG',
                    'class': 'logging.StreamHandler',
                    'formatter': 'colored',
                    'filters': ['require_debug_true'],
                },

                # 文件处理器
                'file': {
                    'level': 'INFO',
                    'class': 'logging.handlers.RotatingFileHandler',
                    'filename': Path(__file__).resolve().parent.parent / 'logs' / 'django.log',
                    'maxBytes': 10 * 1024 * 1024,  # 10MB
                    'backupCount': 5,
                    'formatter': 'verbose',
                    'encoding': 'utf-8',
                },

                # 错误文件处理器
                'error_file': {
                    'level': 'ERROR',
                    'class': 'logging.handlers.RotatingFileHandler',
                    'filename': Path(__file__).resolve().parent.parent / 'logs' / 'django_error.log',
                    'maxBytes': 10 * 1024 * 1024,  # 10MB
                    'backupCount': 5,
                    'formatter': 'detailed',
                    'encoding': 'utf-8',
                },

                # API日志处理器
                'api_file': {
                    'level': 'DEBUG',
                    'class': 'logging.handlers.RotatingFileHandler',
                    'filename': Path(__file__).resolve().parent.parent / 'logs' / 'api.log',
                    'maxBytes': 50 * 1024 * 1024,  # 50MB
                    'backupCount': 10,
                    'formatter': 'json',
                    'encoding': 'utf-8',
                    'filters': ['sensitive_fields', 'request_id'],
                },

                # SQL日志处理器
                'sql_file': {
                    'level': 'DEBUG',
                    'class': 'logging.handlers.RotatingFileHandler',
                    'filename': Path(__file__).resolve().parent.parent / 'logs' / 'sql.log',
                    'maxBytes': 20 * 1024 * 1024,  # 20MB
                    'backupCount': 5,
                    'formatter': 'verbose',
                    'encoding': 'utf-8',
                },

                # 邮件处理器(生产环境错误通知)
                'mail_admins': {
                    'level': 'ERROR',
                    'class': 'django.utils.log.AdminEmailHandler',
                    'formatter': 'verbose',
                    'filters': ['require_debug_false'],
                },

                # Sentry处理器(错误监控)
                'sentry': {
                    'level': 'ERROR',
                    'class': 'sentry_sdk.integrations.logging.SentryHandler',
                    'filters': ['require_debug_false'],
                },

                # 结构化日志处理器
                'structlog': {
                    'level': 'DEBUG',
                    'class': 'structlog.stdlib.ProcessorFormatterWrap',
                    'formatter': 'json',
                    'filters': ['sensitive_fields', 'request_id'],
                },
            },

            # 日志记录器
            'loggers': {
                # Django核心日志
                'django': {
                    'handlers': ['console', 'file', 'error_file', 'mail_admins'],
                    'level': 'INFO',
                    'propagate': True,
                },

                # DRF日志
                'rest_framework': {
                    'handlers': ['console', 'api_file'],
                    'level': 'DEBUG',
                    'propagate': True,
                },

                # SQL查询日志
                'django.db.backends': {
                    'handlers': ['console', 'sql_file'],
                    'level': 'DEBUG',
                    'propagate': True,
                },

                # 应用日志
                'apps': {
                    'handlers': ['console', 'api_file', 'file'],
                    'level': 'DEBUG',
                    'propagate': True,
                },

                # 认证日志
                'apps.authentication': {
                    'handlers': ['console', 'api_file', 'file'],
                    'level': 'DEBUG',
                    'propagate': False,
                },

                # 用户操作日志
                'apps.users': {
                    'handlers': ['console', 'api_file', 'file'],
                    'level': 'INFO',
                    'propagate': False,
                },

                # 内容日志
                'apps.posts': {
                    'handlers': ['console', 'api_file', 'file'],
                    'level': 'INFO',
                    'propagate': False,
                },

                # 请求处理日志
                'api_requests': {
                    'handlers': ['api_file'],
                    'level': 'DEBUG',
                    'propagate': False,
                },

                # 安全相关日志
                'security': {
                    'handlers': ['console', 'file', 'error_file', 'mail_admins'],
                    'level': 'WARNING',
                    'propagate': False,
                },

                # 性能监控日志
                'performance': {
                    'handlers': ['console', 'api_file'],
                    'level': 'INFO',
                    'propagate': False,
                },
            },

            # 根日志记录器
            'root': {
                'handlers': ['console', 'file'],
                'level': 'INFO',
            },
        }

        # 自定义日志过滤器
        # apps/core/logging.py
        import logging
        import uuid
        from django.conf import settings
        from rest_framework.request import Request

        class SensitiveFieldsFilter(logging.Filter):
            """敏感信息过滤器"""

            SENSITIVE_FIELDS = [
                'password', 'token', 'secret', 'key', 'credential',
                'authorization', 'session', 'cookie', 'csrf'
            ]

            def filter(self, record):
                """过滤敏感信息"""
                if hasattr(record, 'msg'):
                    msg = record.msg
                    if isinstance(msg, str):
                        for field in self.SENSITIVE_FIELDS:
                            msg = msg.replace(field, f'{field[0]}***')
                        record.msg = msg

                if hasattr(record, 'args') and record.args:
                    args = list(record.args)
                    for i, arg in enumerate(args):
                        if isinstance(arg, str):
                            for field in self.SENSITIVE_FIELDS:
                                if field.lower() in arg.lower():
                                    args[i] = f'{field[0]}***'
                    record.args = tuple(args)

                return True

        class RequestIDFilter(logging.Filter):
            """请求ID过滤器"""

            def filter(self, record):
                """添加请求ID"""
                try:
                    # 尝试从本地线程获取请求ID
                    from django.utils.deprecation import MiddlewareMixin
                    request = getattr(record, 'request', None)
                    if not request:
                        # 从本地线程变量获取
                        import threading
                        request = getattr(threading.current_thread(), 'request', None)

                    if request and hasattr(request, 'request_id'):
                        record.request_id = request.request_id
                    else:
                        record.request_id = str(uuid.uuid4())[:8]
                except Exception:
                    record.request_id = 'unknown'

                return True

        class UserFilter(logging.Filter):
            """用户过滤器"""

            def filter(self, record):
                """添加用户信息"""
                try:
                    request = getattr(record, 'request', None)
                    if request and hasattr(request, 'user') and request.user.is_authenticated:
                        record.user_id = request.user.id
                        record.username = request.user.username
                    else:
                        record.user_id = 'anonymous'
                        record.username = 'anonymous'
                except Exception:
                    record.user_id = 'unknown'
                    record.username = 'unknown'

                return True

        # 自定义日志记录器
        import structlog

        structlog.configure(
            processors=[
                structlog.stdlib.filter_by_level,
                structlog.stdlib.add_logger_name,
                structlog.stdlib.add_log_level,
                structlog.stdlib.PositionalArgumentsFormatter(),
                structlog.processors.TimeStamper(fmt="iso"),
                structlog.processors.StackInfoRenderer(),
                structlog.processors.format_exc_info,
                structlog.processors.UnicodeDecoder(),
                structlog.processors.JSONRenderer()
            ],
            context_class=dict,
            logger_factory=structlog.stdlib.LoggerFactory(),
            wrapper_class=structlog.stdlib.BoundLogger,
            cache_logger_on_first_use=True,
        )

        # 日志记录器使用示例
        import logging
        import structlog

        logger = logging.getLogger('apps.posts')
        structured_logger = structlog.get_logger()

        class PostView:
            """文章视图日志记录示例"""

            def get(self, request, pk=None):
                # 普通日志记录
                logger.info(f"获取文章列表,用户:{request.user.username}")

                # 结构化日志记录
                structured_logger.info(
                    "post_list_request",
                    user_id=request.user.id if request.user.is_authenticated else None,
                    page=request.GET.get('page', 1),
                    page_size=request.GET.get('page_size', 20),
                    request_id=getattr(request, 'request_id', None),
                )

                # 错误日志记录
                try:
                    # 业务逻辑
                    pass
                except Exception as e:
                    logger.error(
                        f"获取文章列表失败:{str(e)}",
                        exc_info=True,
                        extra={'request': request}
                    )
                    structured_logger.error(
                        "post_list_error",
                        error=str(e),
                        error_type=type(e).__name__,
                        user_id=request.user.id if request.user.is_authenticated else None,
                        request_id=getattr(request, 'request_id', None),
                    )

            def create(self, request):
                # 操作日志记录
                logger.info(
                    f"用户 {request.user.username} 创建文章",
                    extra={'request': request, 'action': 'create_post'}
                )

                structured_logger.info(
                    "post_created",
                    user_id=request.user.id,
                    action='create_post',
                    post_data={
                        'title': request.data.get('title'),
                        'is_published': request.data.get('is_published')
                    },
                    request_id=getattr(request, 'request_id', None)
                )

        # 性能监控日志
        import time
        from functools import wraps

        def log_performance(logger_name='performance'):
            """性能监控装饰器"""
            def decorator(func):
                @wraps(func)
                def wrapper(*args, **kwargs):
                    start_time = time.time()
                    result = func(*args, **kwargs)
                    end_time = time.time()
                    duration = end_time - start_time

                    logger = logging.getLogger(logger_name)
                    logger.info(
                        f"{func.__name__} 执行时间:{duration:.3f}秒",
                        extra={
                            'function': func.__name__,
                            'duration': duration,
                            'args_count': len(args),
                            'kwargs_count': len(kwargs)
                        }
                    )

                    return result
                return wrapper
            return decorator

        # 使用示例
        @log_performance('performance')
        def expensive_operation():
            time.sleep(2)  # 模拟耗时操作
            return "completed"

        # 日志分析脚本
        # scripts/analyze_logs.py
        import re
        from collections import defaultdict, Counter
        from datetime import datetime
        import json

        class LogAnalyzer:
            """日志分析器"""

            def __init__(self, log_file):
                self.log_file = log_file

            def analyze_sql_queries(self):
                """分析SQL查询"""
                query_stats = defaultdict(int)
                slow_queries = []

                with open(self.log_file, 'r') as f:
                    for line in f:
                        if 'SELECT' in line or 'INSERT' in line or 'UPDATE' in line or 'DELETE' in line:
                            # 提取SQL语句
                            sql_match = re.search(r'(SELECT|INSERT|UPDATE|DELETE).*?(?=\s*[\n\(])', line, re.IGNORECASE)
                            if sql_match:
                                sql = sql_match.group()
                                query_type = sql.split()[0]
                                query_stats[query_type] += 1

                                # 提取查询时间
                                time_match = re.search(r'time=([\d.]+)', line)
                                if time_match and float(time_match.group(1)) > 1.0:
                                    slow_queries.append({
                                        'sql': sql[:100] + '...',
                                        'time': float(time_match.group(1))
                                    })

                return {
                    'query_types': dict(query_stats),
                    'slow_queries': slow_queries,
                    'total_queries': sum(query_stats.values())
                }

            def analyze_api_errors(self):
                """分析API错误"""
                errors = []

                with open(self.log_file, 'r') as f:
                    for line in f:
                        if 'ERROR' in line and 'api' in line.lower():
                            # 提取错误信息
                            error_match = re.search(r'\[ERROR\].*?([A-Z]\w+Error:.*?)$', line)
                            if error_match:
                                errors.append(error_match.group(1))

                return Counter(errors)

            def analyze_request_patterns(self):
                """分析请求模式"""
                patterns = Counter()
                user_requests = defaultdict(int)

                with open(self.log_file, 'r') as f:
                    for line in f:
                        if 'GET /api/' in line or 'POST /api/' in line:
                            # 提取API路径
                            path_match = re.search(r'(/[^\s]+)', line)
                            if path_match:
                                path = path_match.group(1)
                                patterns[path] += 1

                            # 提取用户信息
                            user_match = re.search(r'user_id[=:]\s*(\d+)', line)
                            if user_match:
                                user_id = user_match.group(1)
                                user_requests[user_id] += 1

                return {
                    'patterns': patterns,
                    'user_requests': dict(user_requests)
                }

        # 使用示例
        analyzer = LogAnalyzer('/path/to/logs/api.log')
        sql_stats = analyzer.analyze_sql_queries()
        error_stats = analyzer.analyze_api_errors()
        request_patterns = analyzer.analyze_request_patterns()
        ```

02.测试环境配置
    a.单元测试设置
        pytest配置
        测试数据库配置
        Mock对象使用
        测试环境配置示例:
        ```python
        # requirements-test.txt
        pytest>=7.4.0
        pytest-django>=4.7.0
        pytest-cov>=4.1.0
        pytest-mock>=3.11.0
        pytest-env>=1.1.0
        pytest-xdist>=3.3.0
        factory-boy>=3.3.0
        model-bakery>=1.12.0
        faker>=20.0.0
        freezegun>=1.4.0
        responses>=0.24.0
        testfixtures>=7.2.0

        # myproject/settings/testing.py
        import os
        from .base import *

        # 测试配置
        DEBUG = False
        TESTING = True
        SERVE_STATIC = False  # 测试时不服务静态文件

        # 测试数据库配置
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.sqlite3',
                'NAME': ':memory:',  # 内存数据库,测试速度快
            }
        }

        # 禁用迁移
        class DisableMigrations:
            def __contains__(self, item):
                return False

            def __getitem__(self, item):
                return None

        MIGRATION_MODULES = DisableMigrations()

        # 缓存配置(测试时使用内存缓存)
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
                'LOCATION': 'unique-snowflake',
            }
        }

        # 邮件后端(测试时使用内存后端)
        EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

        # 静态文件配置
        STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
        MEDIA_ROOT = '/tmp/test_media'

        # 密码验证(测试时降低要求)
        AUTH_PASSWORD_VALIDATORS = [
            {
                'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
            },
            {
                'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
                'OPTIONS': {
                    'min_length': 1,  # 测试时降低最小长度要求
                }
            },
        ]

        # REST Framework 测试配置
        REST_FRAMEWORK.update({
            'TEST_REQUEST_DEFAULT_FORMAT': 'json',
            'TEST_REQUEST_RENDERER_CLASSES': [
                'rest_framework.renderers.JSONRenderer',
                'rest_framework.renderers.MultiPartRenderer',
            ],
            'DEFAULT_AUTHENTICATION_CLASSES': [],  # 测试时禁用认证
            'DEFAULT_PERMISSION_CLASSES': [],  # 测试时禁用权限
        })

        # JWT 测试配置
        SIMPLE_JWT.update({
            'ACCESS_TOKEN_LIFETIME': timedelta(minutes=1),  # 测试时缩短token有效期
            'REFRESH_TOKEN_LIFETIME': timedelta(minutes=5),
        })

        # 测试专用设置
        PASSWORD_HASHERS = [
            'django.contrib.auth.hashers.MD5PasswordHasher',  # 使用快速哈希算法
        ]

        # 日志配置(测试时简化)
        LOGGING = {
            'version': 1,
            'disable_existing_loggers': False,
            'handlers': {
                'console': {
                    'class': 'logging.StreamHandler',
                },
            },
            'root': {
                'handlers': ['console'],
                'level': 'WARNING',
            },
            'loggers': {
                'django': {
                    'handlers': ['console'],
                    'level': 'WARNING',
                    'propagate': False,
                },
                'rest_framework': {
                    'handlers': ['console'],
                    'level': 'WARNING',
                    'propagate': False,
                },
            },
        }

        # pytest.ini
        [pytest]
        DJANGO_SETTINGS_MODULE = myproject.settings.testing
        python_files = tests.py test_*.py *_tests.py
        python_classes = Test*
        python_functions = test_*
        addopts =
            --nomigrations
            --reuse-db
            --tb=short
            --strict-markers
            --disable-warnings
            --cov=apps
            --cov-report=term-missing
            --cov-report=html
            --cov-report=xml
            --cov-config=.coveragerc
            -vv
        markers =
            slow: marks tests as slow (deselect with '-m "not slow"')
            integration: marks tests as integration tests
            unit: marks tests as unit tests
            api: marks tests as API tests
            db: marks tests that require database
            requires_authenticated_user: marks tests that require an authenticated user
        filterwarnings =
            ignore::rest_framework.exceptions.NotAcceptable
            ignore::rest_framework.exceptions.UnsupportedMediaType
            ignore::rest_framework.exceptions.PermissionDenied
            ignore::django.utils.deprecation.RemovedInDjango40Warning
            ignore::django.utils.deprecation.RemovedInDjango41Warning

        # conftest.py - pytest配置
        import pytest
        import django
        from django.conf import settings
        from django.test.utils import setup_test_environment
        from django.core.management import call_command
        from django.db import connections
        import tempfile
        import shutil

        @pytest.fixture(scope='session')
        def django_db_setup():
            """Django数据库设置"""
            settings.DATABASES = {
                'default': {
                    'ENGINE': 'django.db.backends.sqlite3',
                    'NAME': ':memory:',
                }
            }
            django.setup()
            setup_test_environment()
            yield
            for connection in connections.all():
                connection.close()

        @pytest.fixture(scope='function')
        def media_root():
            """临时媒体目录"""
            temp_dir = tempfile.mkdtemp()
            settings.MEDIA_ROOT = temp_dir
            yield temp_dir
            shutil.rmtree(temp_dir)

        @pytest.fixture
        def user(django_user_model):
            """测试用户"""
            return django_user_model.objects.create_user(
                username='testuser',
                email='[email protected]',
                password='testpass123'
            )

        @pytest.fixture
        def admin_user(django_user_model):
            """测试管理员用户"""
            return django_user_model.objects.create_superuser(
                username='admin',
                email='[email protected]',
                password='adminpass123'
            )

        @pytest.fixture
        def auth_client(client, user):
            """认证客户端"""
            client.force_authenticate(user)
            client.user = user
            return client

        @pytest.fixture
        def admin_auth_client(client, admin_user):
            """认证管理员客户端"""
            client.force_authenticate(admin_user)
            client.user = admin_user
            return client

        @pytest.fixture
        def api_client():
            """API客户端"""
            from rest_framework.test import APIClient
            return APIClient()

        @pytest.fixture
        def authenticated_api_client(api_client, user):
            """认证API客户端"""
            from rest_framework_simplejwt.tokens import RefreshToken
            refresh = RefreshToken.for_user(user)
            api_client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')
            api_client.user = user
            return api_client

        # tests/conftest.py - 应用级测试配置
        import factory
        from django.test import TestCase
        from rest_framework.test import APITestCase
        from apps.users.models import User
        from apps.posts.models import Post

        # 测试数据工厂
        class UserFactory(factory.django.DjangoModelFactory):
            """用户工厂"""
            class Meta:
                model = User
                django_get_or_create = ('email',)

            username = factory.Faker('user_name')
            email = factory.Faker('email')
            first_name = factory.Faker('first_name')
            last_name = factory.Faker('last_name')
            is_active = True

        class PostFactory(factory.django.DjangoModelFactory):
            """文章工厂"""
            class Meta:
                model = Post

            title = factory.Faker('sentence', nb_words=5)
            content = factory.Faker('text', max_nb_chars=500)
            is_published = True
            author = factory.SubFactory(UserFactory)

        # 测试基类
        class BaseTestCase(APITestCase):
            """API测试基类"""

            @classmethod
            def setUpTestData(cls):
                """设置测试数据"""
                super().setUpTestData()
                cls.user = UserFactory()
                cls.admin_user = UserFactory(is_staff=True)
                cls.posts = PostFactory.create_batch(5, author=cls.user)

            def setUp(self):
                """设置测试客户端"""
                super().setUp()
                self.client = APIClient()
                self.authenticate_client()

            def authenticate_client(self, user=None):
                """认证客户端"""
                if user is None:
                    user = self.user
                from rest_framework_simplejwt.tokens import RefreshToken
                refresh = RefreshToken.for_user(user)
                self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')

            def assertSuccessResponse(self, response, message=None):
                """断言成功响应"""
                self.assertEqual(response.status_code, 200)
                if message:
                    self.assertIn('success', response.data)
                    self.assertEqual(response.data['success'], True)
                    self.assertEqual(response.data['message'], message)

            def assertErrorResponse(self, response, status_code=400, error_code=None):
                """断言错误响应"""
                self.assertEqual(response.status_code, status_code)
                self.assertFalse(response.data.get('success', True))
                if error_code:
                    self.assertIn('error', response.data)
                    self.assertEqual(response.data['error'].get('code'), error_code)

        # 自定义测试工具
        class TestUtil:
            """测试工具类"""

            @staticmethod
            def create_test_user(**kwargs):
                """创建测试用户"""
                defaults = {
                    'username': 'testuser',
                    'email': '[email protected]',
                    'password': 'testpass123',
                    'is_active': True
                }
                defaults.update(kwargs)
                return User.objects.create_user(**defaults)

            @staticmethod
            def create_test_post(author=None, **kwargs):
                """创建测试文章"""
                if author is None:
                    author = TestUtil.create_test_user()
                defaults = {
                    'title': 'Test Post',
                    'content': 'Test content',
                    'author': author,
                    'is_published': True
                }
                defaults.update(kwargs)
                return Post.objects.create(**defaults)

            @staticmethod
            def assert_json_response(response, expected_data):
                """断言JSON响应"""
                self.assertEqual(response['Content-Type'], 'application/json')
                response_data = response.json()
                self.assertEqual(response_data, expected_data)

            @staticmethod
            def assert_response_schema(response, schema):
                """断言响应符合schema"""
                import jsonschema
                try:
                    jsonschema.validate(response.json(), schema)
                except jsonschema.ValidationError as e:
                    self.fail(f"Response schema validation failed: {e}")

        # 性能测试装饰器
        from functools import wraps
        import time

        def performance_test(max_time=1.0):
            """性能测试装饰器"""
            def decorator(test_func):
                @wraps(test_func)
                def wrapper(self, *args, **kwargs):
                    start_time = time.time()
                    result = test_func(self, *args, **kwargs)
                    end_time = time.time()
                    duration = end_time - start_time

                    self.assertLess(
                        duration, max_time,
                        f"Test exceeded maximum time of {max_time}s: {duration:.3f}s"
                    )
                    return result
                return wrapper
            return decorator

        # 并发测试工具
        import threading
        import queue

        def concurrent_test(test_func, num_threads=10, iterations=100):
            """并发测试装饰器"""
            def decorator(test_method):
                @wraps(test_method)
                def wrapper(self, *args, **kwargs):
                    results = queue.Queue()
                    errors = queue.Queue()

                    def worker():
                        try:
                            for _ in range(iterations // num_threads):
                                result = test_func(self, *args, **kwargs)
                                results.put(result)
                        except Exception as e:
                            errors.put(e)

                    threads = []
                    for _ in range(num_threads):
                        thread = threading.Thread(target=worker)
                        threads.append(thread)
                        thread.start()

                    for thread in threads:
                        thread.join()

                    self.assertEqual(errors.qsize(), 0, f"Errors in concurrent test: {errors.qsize()}")
                    self.assertEqual(results.qsize(), iterations, f"Not all iterations completed: {results.qsize()}/{iterations}")

                    return None
                return wrapper
            return decorator
        ```
    b.API测试框架
        DRF测试客户端
        HTTP状态码断言
        JSON响应验证
        API测试框架示例:
        ```python
        # tests/test_api_users.py
        from rest_framework import status
        from rest_framework.test import APITestCase
        django.urls import reverse
        django.contrib.auth import get_user_model
        .conftest import BaseTestCase, UserFactory, create_test_user, performance_test
        from .utils import TestUtil

        User = get_user_model()

        class UserAPITestCase(BaseTestCase):
            """用户API测试用例"""

            def test_user_registration_success(self):
                """测试用户注册成功"""
                url = reverse('user-register')
                data = {
                    'username': 'newuser',
                    'email': '[email protected]',
                    'password': 'newpass123',
                    'password2': 'newpass123',
                    'first_name': 'New',
                    'last_name': 'User'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_201_CREATED)
                self.assertTrue(response.data['success'])
                self.assertEqual(response.data['message'], '注册成功')
                self.assertIn('data', response.data)

                # 验证用户创建成功
                user = User.objects.get(email='[email protected]')
                self.assertEqual(user.username, 'newuser')
                self.assertEqual(user.first_name, 'New')
                self.assertEqual(user.last_name, 'User')

            def test_user_registration_invalid_email(self):
                """测试用户注册无效邮箱"""
                url = reverse('user-register')
                data = {
                    'username': 'newuser',
                    'email': 'invalid-email',
                    'password': 'newpass123',
                    'password2': 'newpass123',
                    'first_name': 'New',
                    'last_name': 'User'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
                self.assertFalse(response.data['success'])
                self.assertIn('email', response.data['errors'])

            def test_user_registration_duplicate_email(self):
                """测试用户注册重复邮箱"""
                url = reverse('user-register')
                data = {
                    'username': 'newuser',
                    'email': self.user.email,  # 使用已存在的邮箱
                    'password': 'newpass123',
                    'password2': 'newpass123',
                    'first_name': 'New',
                    'last_name': 'User'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
                self.assertFalse(response.data['success'])
                self.assertIn('email', response.data['errors'])

            def test_user_login_success(self):
                """测试用户登录成功"""
                url = reverse('user-login')
                data = {
                    'email': self.user.email,
                    'password': 'testpass123'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertTrue(response.data['success'])
                self.assertIn('data', response.data)
                self.assertIn('access', response.data['data'])
                self.assertIn('refresh', response.data['data'])
                self.assertIn('user', response.data['data'])

            def test_user_login_invalid_credentials(self):
                """测试用户登录无效凭据"""
                url = reverse('user-login')
                data = {
                    'email': self.user.email,
                    'password': 'wrongpassword'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
                self.assertFalse(response.data['success'])
                self.assertEqual(response.data['message'], '邮箱或密码错误')

            @performance_test(max_time=0.5)
            def test_user_profile_view(self):
                """测试用户资料视图性能"""
                self.authenticate_client(self.user)
                url = reverse('user-profile')

                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertTrue(response.data['success'])
                self.assertEqual(response.data['data']['email'], self.user.email)
                self.assertEqual(response.data['data']['username'], self.user.username)

            def test_user_profile_unauthenticated(self):
                """测试未认证用户访问资料"""
                url = reverse('user-profile')

                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
                self.assertFalse(response.data['success'])

            def test_user_list_view_requires_auth(self):
                """测试用户列表视图需要认证"""
                url = reverse('user-list')

                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

            def test_user_list_view_authenticated(self):
                """测试认证用户访问列表"""
                self.authenticate_client()
                url = reverse('user-list')

                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertTrue(response.data['success'])
                self.assertIn('data', response.data)
                self.assertIn('pagination', response.data)

            def test_user_detail_view_owner(self):
                """测试用户查看自己的详情"""
                self.authenticate_client(self.user)
                url = reverse('user-detail', kwargs={'pk': self.user.pk})

                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertTrue(response.data['success'])
                self.assertEqual(response.data['data']['id'], self.user.id)

            def test_user_detail_view_other_user(self):
                """测试用户查看其他用户详情"""
                other_user = UserFactory()
                self.authenticate_client(self.user)
                url = reverse('user-detail', kwargs={'pk': other_user.pk})

                response = self.client.get(url, format='json')

                # 根据权限配置,可能允许或禁止查看其他用户信息
                if response.status_code == status.HTTP_200_OK:
                    # 如果允许查看,检查返回数据
                    self.assertEqual(response.data['data']['id'], other_user.id)
                else:
                    # 如果不允许查看,检查权限错误
                    self.assertIn(status.HTTP_403_FORBIDDEN, status.HTTP_404_NOT_FOUND)

        # tests/test_api_posts.py
        from rest_framework import status
        from django.urls import reverse
        from apps.posts.models import Post
        .conftest import BaseTestCase, PostFactory

        class PostAPITestCase(BaseTestCase):
            """文章API测试用例"""

            def setUpTestData(cls):
                super().setUpTestData()
                cls.posts = PostFactory.create_batch(10, author=cls.user)

            def test_post_list_unauthenticated(self):
                """测试未认证用户获取文章列表"""
                url = reverse('post-list')

                response = self.client.get(url, format='json')

                # 根据权限配置,未认证用户可能允许或禁止访问
                if response.status_code == status.HTTP_200_OK:
                    self.assertIsInstance(response.data['data'], list)
                else:
                    self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

            def test_post_list_authenticated(self):
                """测试认证用户获取文章列表"""
                self.authenticate_client()
                url = reverse('post-list')

                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertTrue(response.data['success'])
                self.assertIsInstance(response.data['data'], list)
                self.assertEqual(len(response.data['data']), len(self.posts))

            def test_post_create_success(self):
                """测试创建文章成功"""
                self.authenticate_client()
                url = reverse('post-list')
                data = {
                    'title': 'New Test Post',
                    'content': 'This is a test post content.',
                    'is_published': True
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_201_CREATED)
                self.assertTrue(response.data['success'])
                self.assertEqual(response.data['data']['title'], data['title'])
                self.assertEqual(response.data['data']['content'], data['content'])
                self.assertEqual(response.data['data']['author'], self.user.id)

                # 验证数据库中的记录
                post = Post.objects.get(pk=response.data['data']['id'])
                self.assertEqual(post.title, data['title'])
                self.assertEqual(post.author, self.user)

            def test_post_create_unauthenticated(self):
                """测试未认证用户创建文章"""
                url = reverse('post-list')
                data = {
                    'title': 'New Test Post',
                    'content': 'This is a test post content.',
                    'is_published': True
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

            def test_post_detail_view(self):
                """测试文章详情视图"""
                post = self.posts[0]
                url = reverse('post-detail', kwargs={'pk': post.pk})

                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertTrue(response.data['success'])
                self.assertEqual(response.data['data']['id'], post.id)
                self.assertEqual(response.data['data']['title'], post.title)

            def test_post_update_owner(self):
                """测试文章所有者更新"""
                post = self.posts[0]
                self.authenticate_client(self.user)
                url = reverse('post-detail', kwargs={'pk': post.pk})
                data = {
                    'title': 'Updated Title',
                    'content': 'Updated content'
                }

                response = self.client.put(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertTrue(response.data['success'])
                self.assertEqual(response.data['data']['title'], data['title'])
                self.assertEqual(response.data['data']['content'], data['content'])

            def test_post_update_non_owner(self):
                """测试非所有者更新文章"""
                other_user = UserFactory()
                post = PostFactory(author=other_user)
                self.authenticate_client(self.user)
                url = reverse('post-detail', kwargs={'pk': post.pk})
                data = {
                    'title': 'Updated Title',
                    'content': 'Updated content'
                }

                response = self.client.put(url, data, format='json')

                # 根据权限配置,可能禁止或允许
                if response.status_code == status.HTTP_403_FORBIDDEN:
                    self.assertFalse(response.data['success'])
                    self.assertIn('message', response.data)
                else:
                    # 如果允许更新(如管理员),检查更新结果
                    self.assertEqual(response.status_code, status.HTTP_200_OK)

            def test_post_delete_owner(self):
                """测试文章所有者删除"""
                post = self.posts[0]
                self.authenticate_client(self.user)
                url = reverse('post-detail', kwargs={'pk': post.pk})

                response = self.client.delete(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
                self.assertFalse(Post.objects.filter(pk=post.pk).exists())

            def test_post_delete_non_owner(self):
                """测试非所有者删除文章"""
                other_user = UserFactory()
                post = PostFactory(author=other_user)
                self.authenticate_client(self.user)
                url = reverse('post-detail', kwargs={'pk': post.pk})

                response = self.client.delete(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
                self.assertTrue(Post.objects.filter(pk=post.pk).exists())

            def test_post_search(self):
                """测试文章搜索功能"""
                # 创建测试数据
                PostFactory.create_batch(5, title='Test Search Post', author=self.user)

                self.authenticate_client()
                url = reverse('post-list')

                response = self.client.get(url, {'search': 'Test Search'}, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertTrue(response.data['success'])

                # 检查搜索结果
                posts = response.data['data']
                for post in posts:
                    self.assertIn('Test Search', post['title'])

            def test_post_pagination(self):
                """测试文章分页"""
                self.authenticate_client()
                url = reverse('post-list')

                # 测试第一页
                response = self.client.get(url, {'page': 1, 'page_size': 5}, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertTrue(response.data['success'])
                self.assertIn('pagination', response.data)
                self.assertEqual(response.data['pagination']['current_page'], 1)
                self.assertEqual(response.data['pagination']['page_size'], 5)
                self.assertLessEqual(len(response.data['data']), 5)

            def test_post_ordering(self):
                """测试文章排序"""
                self.authenticate_client()
                url = reverse('post-list')

                # 测试按创建时间降序
                response = self.client.get(url, {'ordering': '-created_at'}, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                posts = response.data['data']
                for i in range(len(posts) - 1):
                    self.assertGreaterEqual(
                        posts[i]['created_at'],
                        posts[i + 1]['created_at']
                    )
        ```

3 序列化器

3.1 基础序列化器

01.Serializer基础用法
    a.说明
        基本字段定义
        数据验证和清理
        序列化和反序列化
    b.示例
        # apps/users/serializers.py
        from rest_framework import serializers
        from django.contrib.auth import get_user_model
        from django.core.exceptions import ValidationError
        from datetime import datetime
        import re

        User = get_user_model()

        class UserRegistrationSerializer(serializers.Serializer):
            """用户注册序列化器"""
            # 基础字段定义
            username = serializers.CharField(
                max_length=150,
                min_length=3,
                error_messages={
                    'required': '用户名是必填项',
                    'min_length': '用户名至少3个字符',
                    'max_length': '用户名不能超过150个字符',
                }
            )

            email = serializers.EmailField(
                error_messages={
                    'required': '邮箱是必填项',
                    'invalid': '请输入有效的邮箱地址',
                }
            )

            password = serializers.CharField(
                write_only=True,  # 只写入,不返回
                min_length=8,
                error_messages={
                    'required': '密码是必填项',
                    'min_length': '密码至少8个字符',
                }
            )

            password2 = serializers.CharField(
                write_only=True,
                error_messages={
                    'required': '确认密码是必填项',
                }
            )

            first_name = serializers.CharField(
                max_length=30,
                required=False,
                allow_blank=True,
                error_messages={
                    'max_length': '姓氏不能超过30个字符',
                }
            )

            last_name = serializers.CharField(
                max_length=30,
                required=False,
                allow_blank=True,
                error_messages={
                    'max_length': '名字不能超过30个字符',
                }
            )

            phone = serializers.CharField(
                max_length=20,
                required=False,
                allow_blank=True,
                error_messages={
                    'max_length': '手机号不能超过20个字符',
                }
            )

            birth_date = serializers.DateField(
                required=False,
                error_messages={
                    'invalid': '请输入有效的日期格式(YYYY-MM-DD)',
                }
            )

            is_active = serializers.BooleanField(
                default=True,
                read_only=True,  # 只读,通过update方法更新
                error_messages={
                    'invalid': 'is_active必须是布尔值',
                }
            )

            # 自定义字段
            age = serializers.SerializerMethodField(
                read_only=True,
                help_text="年龄(通过出生日期计算)"
            )

            def validate_username(self, value):
                """用户名验证"""
                # 检查用户名格式
                if not re.match(r'^[a-zA-Z0-9_]+$', value):
                    raise serializers.ValidationError(
                        "用户名只能包含字母、数字和下划线"
                    )

                # 检查用户名唯一性
                if User.objects.filter(username__iexact=value).exists():
                    raise serializers.ValidationError("该用户名已被使用")

                return value.lower()

            def validate_email(self, value):
                """邮箱验证"""
                # 检查邮箱格式
                email_regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
                if not re.match(email_regex, value):
                    raise serializers.ValidationError("请输入有效的邮箱地址")

                # 检查邮箱唯一性
                if User.objects.filter(email__iexact=value).exists():
                    raise serializers.ValidationError("该邮箱已被注册")

                return value.lower()

            def validate_password(self, value):
                """密码验证"""
                if len(value) < 8:
                    raise serializers.ValidationError("密码至少8个字符")

                if not re.search(r'[A-Za-z]', value):
                    raise serializers.ValidationError("密码必须包含字母")

                if not re.search(r'\d', value):
                    raise serializers.ValidationError("密码必须包含数字")

                if not re.search(r'[!@#$%^&*(),.?":{}|<>]', value):
                    raise serializers.ValidationError("密码必须包含特殊字符")

                return value

            def validate_phone(self, value):
                """手机号验证"""
                if value:
                    phone_regex = r'^1[3-9]\d{9}$'
                    if not re.match(phone_regex, value.replace('-', '').replace(' ', '')):
                        raise serializers.ValidationError("请输入有效的手机号码")

                return value

            def validate_birth_date(self, value):
                """出生日期验证"""
                if value:
                    today = datetime.now().date()
                    age = today.year - value.year - ((today.month, today.day) < (value.month, value.day))

                    if age < 13:
                        raise serializers.ValidationError("用户年龄必须大于13岁")
                    elif age > 120:
                        raise serializers.ValidationError("请输入有效的出生日期")

                return value

            def validate(self, attrs):
                """整体验证"""
                # 检查密码一致性
                password = attrs.get('password')
                password2 = attrs.get('password2')

                if password and password2 and password != password2:
                    raise serializers.ValidationError("两次输入的密码不一致")

                # 检查手机号格式
                phone = attrs.get('phone')
                if phone:
                    # 检查是否为虚拟号段
                    virtual_prefixes = ['170', '171', '167']
                    if phone.replace('-', '').startswith(tuple(virtual_prefixes)):
                        raise serializers.ValidationError("不支持虚拟手机号段")

                return attrs

            def get_age(self, obj):
                """计算年龄"""
                if obj.birth_date:
                    today = datetime.now().date()
                    age = today.year - obj.birth_date.year - (
                        (today.month, today.day) < (obj.birth_date.month, obj.birth_date.day)
                    )
                    return age
                return None

            def create(self, validated_data):
                """创建用户实例"""
                # 移除确认密码字段
                validated_data.pop('password2', None)

                # 创建用户
                user = User.objects.create_user(**validated_data)
                return user

            def update(self, instance, validated_data):
                """更新用户实例"""
                instance.username = validated_data.get('username', instance.username)
                instance.email = validated_data.get('email', instance.email)
                instance.first_name = validated_data.get('first_name', instance.first_name)
                instance.last_name = validated_data.get('last_name', instance.last_name)
                instance.phone = validated_data.get('phone', instance.phone)
                instance.birth_date = validated_data.get('birth_date', instance.birth_date)

                # 更新密码
                password = validated_data.get('password')
                if password:
                    instance.set_password(password)

                instance.save()
                return instance

            def to_representation(self, instance):
                """序列化输出格式"""
                data = super().to_representation(instance)
                # 添加额外字段
                data['full_name'] = instance.get_full_name()
                data['display_name'] = instance.get_full_name() or instance.username
                return data

        # 视图中使用基础序列化器
        from rest_framework.views import APIView
        from rest_framework.response import Response
        from rest_framework import status
        from django.contrib.auth import authenticate

        class UserRegistrationView(APIView):
            """用户注册视图"""

            def post(self, request):
                serializer = UserRegistrationSerializer(data=request.data)
                if serializer.is_valid():
                    user = serializer.save()
                    return Response({
                        'success': True,
                        'message': '用户注册成功',
                        'data': {
                            'id': user.id,
                            'username': user.username,
                            'email': user.email,
                            'full_name': user.get_full_name()
                        }
                    }, status=status.HTTP_201_CREATED)
                else:
                    return Response({
                        'success': False,
                        'message': '用户注册失败',
                        'errors': serializer.errors
                    }, status=status.HTTP_400_BAD_REQUEST)

        class UserLoginView(APIView):
            """用户登录视图"""

            def post(self, request):
                email = request.data.get('email')
                password = request.data.get('password')

                if not email or not password:
                    return Response({
                        'success': False,
                        'message': '邮箱和密码都是必填项'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 认证用户
                user = authenticate(request, username=email, password=password)
                if user and user.is_active:
                    # 生成JWT token(需要配置JWT)
                    from rest_framework_simplejwt.tokens import RefreshToken
                    refresh = RefreshToken.for_user(user)

                    return Response({
                        'success': True,
                        'message': '登录成功',
                        'data': {
                            'refresh': str(refresh),
                            'access': str(refresh.access_token),
                            'user': {
                                'id': user.id,
                                'username': user.username,
                                'email': user.email,
                                'full_name': user.get_full_name(),
                                'is_staff': user.is_staff,
                                'is_active': user.is_active
                            }
                        }
                    }, status=status.HTTP_200_OK)
                else:
                    return Response({
                        'success': False,
                        'message': '邮箱或密码错误'
                    }, status=status.HTTP_401_UNAUTHORIZED)

02.字段类型详解
    a.说明
        基础字段类型使用
        字段验证器配置
        自定义字段开发
    b.示例
        # apps/core/serializers.py
        from rest_framework import serializers
        from django.core.validators import RegexValidator
        from django.core.exceptions import ValidationError
        from decimal import Decimal
        from datetime import datetime, date
        import json
        import re

        class CustomFieldSerializer(serializers.Serializer):
            """自定义字段演示序列化器"""

        # 1. 字符串字段
        name = serializers.CharField(
            max_length=100,
            min_length=2,
            required=True,
            allow_blank=False,
            trim_whitespace=True,
            error_messages={
                'required': '名称是必填项',
                'min_length': '名称至少2个字符',
                'max_length': '名称不能超过100个字符',
                'blank': '名称不能为空',
            },
            help_text="请输入您的姓名"
        )

        # 2. 整数字段
        age = serializers.IntegerField(
            min_value=1,
            max_value=120,
            default=None,
            error_messages={
                'invalid': '年龄必须是整数',
                'min_value': '年龄不能小于1岁',
                'max_value': '年龄不能大于120岁',
            }
        )

        # 3. 浮点数字段
        height = serializers.FloatField(
            min_value=0.1,
            max_value=3.0,
            error_messages={
                'invalid': '身高必须是数字',
                'min_value': '身高不能小于0.1米',
                'max_value': '身高不能大于3.0米',
            },
            help_text="身高(米)"
        )

        # 4. 高精度小数字段
        price = serializers.DecimalField(
            max_digits=10,
            decimal_places=2,
            min_value=Decimal('0.01'),
            error_messages={
                'invalid': '价格必须是数字',
                'min_value': '价格不能小于0.01',
                'max_digits': '价格总位数不能超过10',
                'decimal_places': '小数位数必须为2',
            },
            help_text="价格(元)"
        )

        # 5. 布尔字段
        is_active = serializers.BooleanField(
            default=True,
            error_messages={
                'invalid': '必须是布尔值',
            },
            help_text="是否激活"
        )

        # 6. 邮箱字段
        email = serializers.EmailField(
            required=True,
            error_messages={
                'required': '邮箱是必填项',
                'invalid': '请输入有效的邮箱地址',
            }
        )

        # 7. URL字段
        website = serializers.URLField(
            required=False,
            allow_blank=True,
            max_length=200,
            error_messages={
                'invalid': '请输入有效的URL',
            }
        )

        # 8. IP地址字段
        ip_address = serializers.IPAddressField(
            required=False,
            protocol='both',
            error_messages={
                'invalid': '请输入有效的IP地址',
            }
        )

        # 9. 正则表达式字段
        phone = serializers.RegexField(
            regex=r'^1[3-9]\d{9}$',
            required=False,
            allow_blank=True,
            error_messages={
                'invalid': '请输入有效的11位手机号码',
            },
            help_text="手机号码格式:1开头11位数字"
        )

        # 10. 日期字段
        birth_date = serializers.DateField(
            required=False,
            input_formats=['%Y-%m-%d', '%Y/%m/%d'],
            error_messages={
                'invalid': '请输入有效的日期格式(YYYY-MM-DD)',
                'invalid_datetime': '请输入有效的日期',
            },
            help_text="出生日期"
        )

        # 11. 日期时间字段
        created_at = serializers.DateTimeField(
            read_only=True,
            input_formats=['%Y-%m-%d %H:%M:%S', '%Y-%m-%dT%H:%M:%S'],
            error_messages={
                'invalid': '请输入有效的日期时间格式',
            }
        )

        # 12. 时间字段
        start_time = serializers.TimeField(
            input_formats=['%H:%M:%S', '%H:%M'],
            error_messages={
                'invalid': '请输入有效的时间格式(HH:MM:SS)',
            }
        )

        # 13. 选择字段
        gender = serializers.ChoiceField(
            choices=[
                ('M', '男'),
                ('F', '女'),
                ('O', '其他'),
            ],
            default='O',
            error_messages={
                'invalid_choice': '请选择有效的性别选项',
            },
            help_text="性别:男、女、其他"
        )

        # 14. 多选字段
        hobbies = serializers.MultipleChoiceField(
            choices=[
                ('reading', '阅读'),
                ('music', '音乐'),
                ('sports', '运动'),
                ('travel', '旅行'),
                ('photography', '摄影'),
            ],
            required=False,
            allow_blank=True,
            error_messages={
                'invalid_choice': '请选择有效的爱好',
                'not_a_list': '爱好必须是列表格式',
            }
        )

        # 15. 列表字段
        tags = serializers.ListField(
            child=serializers.CharField(max_length=20),
            required=False,
            allow_empty=True,
            error_messages={
                'not_a_list': '标签必须是列表格式',
                'child': '标签不能超过20个字符',
            }
        )

        # 16. 字典字段
        settings = serializers.DictField(
            child=serializers.CharField(),
            required=False,
            allow_empty=True,
            error_messages={
                'not_a_dict': '设置必须是字典格式',
            }
        )

        # 17. 文件字段
        avatar = serializers.ImageField(
            required=False,
            allow_empty_file=True,
            max_length=100,
            use_url=False,  # 不使用URL,返回文件名
            error_messages={
                'invalid_image': '请上传有效的图片文件',
                'required': '头像是必填项',
                'no_file': '没有上传文件',
            },
            help_text="头像图片"
        )

        # 18. UUID字段
        identifier = serializers.UUIDField(
            read_only=True,
            format='hex_verbose',
            error_messages={
                'invalid': '请输入有效的UUID格式',
            }
        )

        # 19. 模型字段
        user_id = serializers.PrimaryKeyRelatedField(
            queryset=User.objects.all(),
            error_messages={
                'does_not_exist': '用户不存在',
                'incorrect_type': '用户ID格式不正确',
            }
        )

        # 20. 超链接字段
        user_url = serializers.HyperlinkedRelatedField(
            view_name='user-detail',
            lookup_field='pk',
            read_only=True,
            error_messages={
                'no_match': '用户不存在',
            }
        )

        # 21. 只读字段
        created_at = serializers.DateTimeField(
            read_only=True
        )

        # 22. 只写字段
        password = serializers.CharField(
            write_only=True,
            min_length=8,
            error_messages={
                'min_length': '密码至少8个字符',
            }
        )

        # 23. 方法字段
        full_name = serializers.SerializerMethodField(
            read_only=True,
            help_text="全名"
        )

        def get_full_name(self, obj):
            """获取全名"""
            return f"{obj.first_name} {obj.last_name}".strip()

        # 24. 自定义字段验证
        def validate_name(self, value):
            """自定义姓名验证"""
            if not value.replace(' ', '').replace('-', '').isalpha():
                raise serializers.ValidationError("姓名只能包含字母、空格和连字符")
            return value

        def validate_age(self, value):
            """自定义年龄验证"""
            if value is not None and value < 18:
                raise serializers.ValidationError("年龄必须大于等于18岁")
            return value

        def validate_price(self, value):
            """自定义价格验证"""
            if value is not None and value < 0:
                raise serializers.ValidationError("价格不能为负数")
            return value

        def validate_hobbies(self, value):
            """自定义爱好验证"""
            if value and len(value) > 10:
                raise serializers.ValidationError("最多选择10个爱好")
            return value

        def validate(self, attrs):
            """整体验证"""
            age = attrs.get('age')
            birth_date = attrs.get('birth_date')

            # 如果同时提供了年龄和出生日期,验证一致性
            if age and birth_date:
                today = datetime.now().date()
                calculated_age = today.year - birth_date.year - (
                    (today.month, today.day) < (birth_date.month, birth_date.day)
                )

                if abs(age - calculated_age) > 1:
                    raise serializers.ValidationError(
                        f"提供的年龄({age})与根据出生日期计算的年龄({calculated_age})不一致"
                    )

            return attrs

        # 视图中使用字段验证器
        from rest_framework.views import APIView
        from rest_framework.response import Response

        class CustomFieldValidationView(APIView):
            """自定义字段验证演示视图"""

            def post(self, request):
                serializer = CustomFieldSerializer(data=request.data)
                if serializer.is_valid():
                    return Response({
                        'success': True,
                        'message': '验证成功',
                        'data': serializer.validated_data
                    })
                else:
                    return Response({
                        'success': False,
                        'message': '验证失败',
                        'errors': serializer.errors
                    })

03.验证器和自定义验证
    a.说明
        内置验证器使用
        自定义验证器开发
        错误处理机制
    b.示例
        # apps/core/validators.py
        from django.core.exceptions import ValidationError
        from django.core.validators import RegexValidator
        from django.utils.translation import gettext_lazy as _
        import re
        from datetime import date

        # 自定义验证器类
        class PasswordValidator:
            """密码验证器类"""
            message = _('密码必须包含大写字母、小写字母、数字和特殊字符')

            def __init__(self, min_length=8):
                self.min_length = min_length
                self.message = _(f'密码长度至少{min_length}个字符,且必须包含大写字母、小写字母、数字和特殊字符')

            def validate(self, password, user=None):
                """验证密码"""
                if len(password) < self.min_length:
                    raise ValidationError(self.message)

                if not any(c.isupper() for c in password):
                    raise ValidationError('密码必须包含至少一个大写字母')

                if not any(c.islower() for c in password):
                    raise ValidationError('密码必须包含至少一个小写字母')

                if not any(c.isdigit() for c in password):
                    raise ValidationError('密码必须包含至少一个数字')

                if not any(not c.isalnum() for c in password):
                    raise ValidationError('密码必须包含至少一个特殊字符')

                # 检查是否包含用户名
                if user and user.username.lower() in password.lower():
                    raise ValidationError('密码不能包含用户名')

                # 检查是否包含常见弱密码
                weak_passwords = [
                    'password', '123456', 'qwerty', 'abc123',
                    'admin', 'root', 'test', 'user',
                ]
                if password.lower() in weak_passwords:
                    raise ValidationError('密码不能使用常见的弱密码')

            def get_help_text(self):
                return self.message

        # 注册自定义验证器
        from django.contrib.auth.password_validation import register_password_validator

        register_password_validator(PasswordValidator)

        # 自定义验证器函数
        def validate_phone_number(value):
            """验证手机号码格式"""
            phone_regex = r'^1[3-9]\d{9}$'
            if not re.match(phone_regex, value):
                raise ValidationError('请输入有效的11位手机号码')

        def validate_chinese_name(value):
            """验证中文姓名"""
            if not re.match(r'^[\u4e00-\u9fa5]+$', value):
                raise ValidationError('请输入中文姓名')

        def validate_credit_card(number):
            """验证信用卡号码"""
            # 移除所有非数字字符
            number = re.sub(r'\D', '', number)

            # Luhn算法验证
            def luhn_checksum(card_number):
                digits = [int(d) for d in card_number]
                odd_digits = digits[-1::-2]
                even_digits = digits[-2::-2]
                checksum = 0
                checksum += sum(odd_digits)
                for d in even_digits:
                    d *= 2
                    checksum += sum(divmod(d, 10))
                return checksum % 10 == 0

            if len(number) not in [13, 15, 16]:
                raise ValidationError('信用卡号码必须是13、15或16位')

            if not luhn_checksum(number):
                raise ValidationError('信用卡号码无效')

        # 在模型中使用自定义验证器
        # apps/users/models.py
        from django.db import models
        from django.core.validators import RegexValidator
        from .validators import validate_phone_number, validate_chinese_name

        class UserProfile(models.Model):
            user = models.OneToOneField(
                'auth.User',
                on_delete=models.CASCADE,
                related_name='profile'
            )

            # 使用内置验证器
            chinese_name = models.CharField(
                '中文名',
                max_length=10,
                validators=[validate_chinese_name],
                error_messages={
                    'invalid': '请输入有效的中文姓名',
                }
            )

            phone_number = models.CharField(
                '手机号码',
                max_length=11,
                validators=[validate_phone_number],
                error_messages={
                    'invalid': '请输入有效的手机号码',
                }
            )

            # 自定义验证器
            credit_card = models.CharField(
                '信用卡号',
                max_length=16,
                blank=True,
                validators=[
                    RegexValidator(
                        regex=r'^\d{13,16}$',
                        message='信用卡号必须是13-16位数字'
                    )
                ]
            )

            def clean_credit_card(self):
                """模型级别的验证"""
                credit_card = self.cleaned_data.get('credit_card', '')
                if credit_card:
                    from .validators import validate_credit_card
                    validate_credit_card(credit_card)

            class Meta:
                verbose_name = '用户资料'
                verbose_name_plural = '用户资料'

        # 序列化器中使用自定义验证器
        # apps/users/serializers.py
        from rest_framework import serializers
        from .validators import validate_phone_number

        class UserProfileSerializer(serializers.Serializer):
            phone_number = serializers.CharField(
                max_length=11,
                validators=[validate_phone_number]
            )

            # 自定义验证方法
            def validate_credit_card(self, value):
                """验证信用卡"""
                if value:
                    from .validators import validate_credit_card
                    validate_credit_card(value)
                return value

            def validate(self, attrs):
                """整体验证"""
                phone = attrs.get('phone_number')
                credit_card = attrs.get('credit_card')

                # 检查手机号和信用卡的关联性
                if phone and credit_card:
                    # 简单的验证逻辑
                    if phone.startswith('1') and credit_card.startswith('4'):
                        raise ValidationError('手机号1开头通常不匹配以4开头的信用卡')

                return attrs

        # 全局错误处理
        # apps/core/exceptions.py
        from rest_framework.views import exception_handler
        from rest_framework.response import Response
        from rest_framework import status
        from rest_framework.exceptions import ValidationError as DRFValidationError
        from django.http import Http404
        import logging

        logger = logging.getLogger('validation')

        def custom_exception_handler(exc, context):
            """自定义异常处理器"""

            # 调用DRF默认异常处理器
            response = exception_handler(exc, context)

            if response is not None:
                # 自定义验证错误响应格式
                if isinstance(exc, DRFValidationError):
                    response.data = {
                        'success': False,
                        'message': '数据验证失败',
                        'error': {
                            'code': 'VALIDATION_ERROR',
                            'details': response.data,
                            'fields': self.extract_field_errors(response.data)
                        },
                        'timestamp': timezone.now().isoformat(),
                    }
                elif isinstance(exc, Http404):
                    response.data = {
                        'success': False,
                        'message': '请求的资源不存在',
                        'error': {
                            'code': 'NOT_FOUND',
                            'details': str(exc),
                        },
                        'timestamp': timezone.now().isoformat(),
                    }
                elif response.status_code == 400:
                    response.data = {
                        'success': False,
                        'message': '请求参数错误',
                        'error': {
                            'code': 'BAD_REQUEST',
                            'details': response.data,
                        },
                        'timestamp': timezone.now().isoformat(),
                    }
                elif response.status_code == 401:
                    response.data = {
                        'success': False,
                        'message': '认证失败',
                        'error': {
                            'code': 'AUTHENTICATION_FAILED',
                            'details': response.data,
                        },
                        'timestamp': timezone.now().isoformat(),
                    }
                elif response.status_code == 403:
                    response.data = {
                        'success': False,
                        'message': '权限不足',
                        'error': {
                            'code': 'PERMISSION_DENIED',
                            'details': response.data,
                        },
                        'timestamp': timezone.now().isoformat(),
                    }
                elif response.status_code == 422:
                    response.data = {
                        'success': False,
                        'message': '请求无法处理',
                        'error': {
                            'code': 'UNPROCESSABLE_ENTITY',
                            'details': response.data,
                        },
                        'timestamp': timezone.now().isoformat(),
                    }
                elif response.status_code >= 500:
                    response.data = {
                        'success': False,
                        'message': '服务器内部错误',
                        'error': {
                            'code': 'INTERNAL_SERVER_ERROR',
                            'details': response.data,
                        },
                        'timestamp': timezone.now().isoformat(),
                    }

                # 记录错误日志
                logger.error(
                    f"API Error: {exc.__class__.__name__}: {str(exc)}",
                    exc_info=True,
                    extra={
                        'status_code': response.status_code,
                        'path': context['request'].path,
                        'method': context['request'].method,
                        'user': getattr(context['request'].user, 'username', 'Anonymous'),
                    }
                )

            return response

        def extract_field_errors(self, errors):
            """提取字段错误"""
            field_errors = {}
            if isinstance(errors, dict):
                for field, messages in errors.items():
                    if isinstance(messages, list):
                        field_errors[field] = messages
                    else:
                        field_errors[field] = [messages]
            elif isinstance(errors, list):
                # 检查是否是字段级别的错误
                for error in errors:
                    if isinstance(error, dict):
                        field_errors.update(error)
            return field_errors

        # 在settings.py中注册自定义异常处理器
        REST_FRAMEWORK = {
            # ... 其他配置
            'EXCEPTION_HANDLER': 'apps.core.exceptions.custom_exception_handler',
        }

3.2 ModelSerializer

01.自动字段生成
    a.说明
        Meta类配置
        字段映射规则
        默认验证器
    b.示例
        # apps/blog/models.py
        from django.db import models
        from django.contrib.auth import get_user_model
        from django.urls import reverse
        from django.utils.text import slugify

        User = get_user_model()

        class Category(models.Model):
            """文章分类模型"""
            name = models.CharField('分类名称', max_length=100, unique=True)
            slug = models.SlugField('URL别名', max_length=100, unique=True, blank=True)
            description = models.TextField('分类描述', blank=True)
            parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
            is_active = models.BooleanField('是否激活', default=True)
            created_at = models.DateTimeField('创建时间', auto_now_add=True)
            updated_at = models.DateTimeField('更新时间', auto_now=True)

            class Meta:
                verbose_name = '分类'
                verbose_name_plural = '分类'
                ordering = ['name']

            def save(self, *args, **kwargs):
                if not self.slug:
                    self.slug = slugify(self.name)
                super().save(*args, **kwargs)

            def __str__(self):
                return self.name

            def get_absolute_url(self):
                return reverse('category-detail', kwargs={'slug': self.slug})

        class Tag(models.Model):
            """标签模型"""
            name = models.CharField('标签名称', max_length=50, unique=True)
            slug = models.SlugField('URL别名', max_length=50, unique=True, blank=True)
            color = models.CharField('颜色', max_length=7, default='#007bff')  # 十六进制颜色
            description = models.TextField('标签描述', blank=True)
            created_at = models.DateTimeField('创建时间', auto_now_add=True)

            class Meta:
                verbose_name = '标签'
                verbose_name_plural = '标签'
                ordering = ['name']

            def save(self, *args, **kwargs):
                if not self.slug:
                    self.slug = slugify(self.name)
                super().save(*args, **kwargs)

            def __str__(self):
                return self.name

        class Article(models.Model):
            """文章模型"""
            STATUS_CHOICES = [
                ('draft', '草稿'),
                ('published', '已发布'),
                ('archived', '已归档'),
            ]

            title = models.CharField('标题', max_length=200)
            slug = models.SlugField('URL别名', max_length=200, unique=True, blank=True)
            author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='articles', verbose_name='作者')
            category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True, related_name='articles', verbose_name='分类')
            tags = models.ManyToManyField(Tag, blank=True, related_name='articles', verbose_name='标签')
            content = models.TextField('内容')
            excerpt = models.TextField('摘要', max_length=500, blank=True)
            status = models.CharField('状态', max_length=20, choices=STATUS_CHOICES, default='draft')
            featured_image = models.ImageField('特色图片', upload_to='articles/images/', null=True, blank=True)
            view_count = models.PositiveIntegerField('浏览次数', default=0)
            like_count = models.PositiveIntegerField('点赞次数', default=0)
            comment_count = models.PositiveIntegerField('评论次数', default=0)
            is_featured = models.BooleanField('是否推荐', default=False)
            is_top = models.BooleanField('是否置顶', default=False)
            published_at = models.DateTimeField('发布时间', null=True, blank=True)
            created_at = models.DateTimeField('创建时间', auto_now_add=True)
            updated_at = models.DateTimeField('更新时间', auto_now=True)

            class Meta:
                verbose_name = '文章'
                verbose_name_plural = '文章'
                ordering = ['-created_at']
                indexes = [
                    models.Index(fields=['status', 'published_at']),
                    models.Index(fields=['author', 'status']),
                    models.Index(fields=['category', 'status']),
                ]

            def save(self, *args, **kwargs):
                if not self.slug:
                    self.slug = slugify(self.title)

                # 自动生成摘要
                if not self.excerpt and self.content:
                    self.excerpt = self.content[:200] + '...' if len(self.content) > 200 else self.content

                # 设置发布时间
                if self.status == 'published' and not self.published_at:
                    self.published_at = timezone.now()

                super().save(*args, **kwargs)

            def __str__(self):
                return self.title

            def get_absolute_url(self):
                return reverse('article-detail', kwargs={'slug': self.slug})

            def increment_view_count(self):
                """增加浏览次数"""
                self.view_count += 1
                self.save(update_fields=['view_count'])

        # apps/blog/serializers.py
        from rest_framework import serializers
        from .models import Article, Category, Tag
        from django.contrib.auth import get_user_model

        User = get_user_model()

        class CategorySerializer(serializers.ModelSerializer):
            """分类序列化器 - 基础版本"""

            # 自动生成的字段:
            # - id (ReadOnlyField)
            # - name (CharField)
            # - slug (SlugField)
            # - description (CharField)
            # - parent (PrimaryKeyRelatedField)
            # - is_active (BooleanField)
            # - created_at (DateTimeField)
            # - updated_at (DateTimeField)

            # 自定义字段
            article_count = serializers.SerializerMethodField(
                read_only=True,
                help_text="文章数量"
            )

            children_count = serializers.SerializerMethodField(
                read_only=True,
                help_text="子分类数量"
            )

            class Meta:
                model = Category
                # fields = '__all__'  # 包含所有字段
                fields = [
                    'id', 'name', 'slug', 'description', 'parent',
                    'is_active', 'article_count', 'children_count',
                    'created_at', 'updated_at'
                ]
                # exclude = ['parent']  # 排除特定字段
                read_only_fields = ['id', 'created_at', 'updated_at']  # 只读字段
                # extra_kwargs = {
                #     'name': {'required': True, 'min_length': 2},
                #     'description': {'required': False, 'allow_blank': True},
                # }

            def get_article_count(self, obj):
                """获取文章数量"""
                return obj.articles.filter(status='published').count()

            def get_children_count(self, obj):
                """获取子分类数量"""
                return obj.children.filter(is_active=True).count()

        class TagSerializer(serializers.ModelSerializer):
            """标签序列化器"""

            article_count = serializers.SerializerMethodField(
                read_only=True,
                help_text="文章数量"
            )

            class Meta:
                model = Tag
                fields = [
                    'id', 'name', 'slug', 'color', 'description',
                    'article_count', 'created_at'
                ]
                read_only_fields = ['id', 'created_at']

            def get_article_count(self, obj):
                """获取文章数量"""
                return obj.articles.filter(status='published').count()

        class AuthorSerializer(serializers.ModelSerializer):
            """作者序列化器"""

            # 只显示必要字段,避免敏感信息泄露
            id = serializers.IntegerField(read_only=True)
            username = serializers.CharField(read_only=True)
            email = serializers.EmailField(read_only=True)
            first_name = serializers.CharField(read_only=True)
            last_name = serializers.CharField(read_only=True)

            # 计算字段
            full_name = serializers.SerializerMethodField(read_only=True)
            display_name = serializers.SerializerMethodField(read_only=True)
            article_count = serializers.SerializerMethodField(read_only=True)

            class Meta:
                model = User
                fields = [
                    'id', 'username', 'email', 'first_name', 'last_name',
                    'full_name', 'display_name', 'article_count'
                ]

            def get_full_name(self, obj):
                """获取全名"""
                return obj.get_full_name() or obj.username

            def get_display_name(self, obj):
                """获取显示名称"""
                return obj.get_full_name() or obj.username

            def get_article_count(self, obj):
                """获取文章数量"""
                return obj.articles.filter(status='published').count()

        class ArticleListSerializer(serializers.ModelSerializer):
            """文章列表序列化器 - 精简版本"""

            # 嵌套序列化
            author = AuthorSerializer(read_only=True)
            category = CategorySerializer(read_only=True)
            tags = TagSerializer(many=True, read_only=True)

            # 格式化字段
            formatted_created_at = serializers.SerializerMethodField()
            formatted_updated_at = serializers.SerializerMethodField()
            reading_time = serializers.SerializerMethodField()

            # URL字段
            url = serializers.HyperlinkedIdentityField(
                view_name='article-detail',
                lookup_field='slug'
            )

            # 计算字段
            engagement_score = serializers.SerializerMethodField()

            class Meta:
                model = Article
                fields = [
                    'id', 'url', 'title', 'slug', 'author', 'category',
                    'tags', 'excerpt', 'status', 'featured_image',
                    'view_count', 'like_count', 'comment_count',
                    'is_featured', 'is_top', 'formatted_created_at',
                    'formatted_updated_at', 'reading_time', 'engagement_score'
                ]
                read_only_fields = ['id', 'view_count', 'like_count', 'comment_count']

            def get_formatted_created_at(self, obj):
                """格式化创建时间"""
                return obj.created_at.strftime('%Y-%m-%d %H:%M:%S')

            def get_formatted_updated_at(self, obj):
                """格式化更新时间"""
                return obj.updated_at.strftime('%Y-%m-%d %H:%M:%S')

            def get_reading_time(self, obj):
                """估算阅读时间(分钟)"""
                if obj.content:
                    # 假设每分钟阅读200个字符
                    word_count = len(obj.content)
                    reading_time = max(1, word_count // 200)
                    return reading_time
                return 1

            def get_engagement_score(self, obj):
                """计算参与度分数"""
                # 浏览量权重1,点赞权重10,评论权重20
                score = obj.view_count + (obj.like_count * 10) + (obj.comment_count * 20)
                return score

        # 视图中使用ModelSerializer
        from rest_framework import generics
        from rest_framework.permissions import IsAuthenticatedOrReadOnly
        from django_filters.rest_framework import DjangoFilterBackend
        from rest_framework.filters import SearchFilter, OrderingFilter

        class ArticleListView(generics.ListAPIView):
            """文章列表视图"""
            queryset = Article.objects.filter(status='published').select_related('author', 'category').prefetch_related('tags')
            serializer_class = ArticleListSerializer
            permission_classes = [IsAuthenticatedOrReadOnly]

            # 过滤和搜索
            filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
            filterset_fields = ['category', 'tags', 'author', 'status']
            search_fields = ['title', 'content', 'excerpt']
            ordering_fields = ['created_at', 'updated_at', 'view_count', 'like_count']
            ordering = ['-created_at']

            # 分页
            pagination_class = PageNumberPagination
            page_size = 20
            page_size_query_param = 'page_size'
            max_page_size = 100

        class ArticleDetailView(generics.RetrieveAPIView):
            """文章详情视图"""
            queryset = Article.objects.filter(status='published').select_related('author', 'category').prefetch_related('tags')
            serializer_class = ArticleListSerializer
            lookup_field = 'slug'
            permission_classes = [IsAuthenticatedOrReadOnly]

            def retrieve(self, request, *args, **kwargs):
                """重写retrieve方法,增加浏览次数"""
                instance = self.get_object()
                # 增加浏览次数
                instance.increment_view_count()

                serializer = self.get_serializer(instance)
                return Response(serializer.data)

02.自定义字段
    a.说明
        重写字段定义
        添加额外字段
        字段验证配置
    b.示例
        # apps/blog/serializers.py
        from rest_framework import serializers
        from django.contrib.auth import get_user_model
        from django.core.exceptions import ValidationError
        from .models import Article, Category, Tag
        import re

        User = get_user_model()

        class ArticleDetailSerializer(serializers.ModelSerializer):
            """文章详情序列化器 - 完整版本"""

            # 重写字段定义
            title = serializers.CharField(
                max_length=200,
                min_length=5,
                error_messages={
                    'required': '标题是必填项',
                    'min_length': '标题至少5个字符',
                    'max_length': '标题不能超过200个字符',
                },
                help_text="请输入5-200个字符的标题"
            )

            content = serializers.CharField(
                min_length=100,
                error_messages={
                    'required': '内容是必填项',
                    'min_length': '内容至少100个字符',
                },
                help_text="请输入至少100个字符的内容"
            )

            excerpt = serializers.CharField(
                max_length=500,
                required=False,
                allow_blank=True,
                error_messages={
                    'max_length': '摘要不能超过500个字符',
                }
            )

            # 自定义字段类型
            slug = serializers.SlugField(
                max_length=200,
                required=False,
                allow_blank=True,
                help_text="URL别名,留空将自动生成"
            )

            status = serializers.ChoiceField(
                choices=Article.STATUS_CHOICES,
                default='draft',
                error_messages={
                    'invalid_choice': '请选择有效的状态',
                }
            )

            # 关系字段自定义
            author = serializers.PrimaryKeyRelatedField(
                queryset=User.objects.all(),
                error_messages={
                    'does_not_exist': '指定的用户不存在',
                    'incorrect_type': '用户ID格式不正确',
                }
            )

            category = serializers.SlugRelatedField(
                slug_field='slug',
                queryset=Category.objects.filter(is_active=True),
                allow_null=True,
                required=False,
                error_messages={
                    'does_not_exist': '指定的分类不存在',
                    'invalid_slug': '分类slug格式不正确',
                }
            )

            tags = serializers.SlugRelatedField(
                slug_field='slug',
                queryset=Tag.objects.all(),
                many=True,
                required=False,
                allow_empty=True
            )

            # 文件字段自定义
            featured_image = serializers.ImageField(
                required=False,
                allow_null=True,
                max_length=255,
                use_url=True,  # 返回完整URL
                error_messages={
                    'invalid_image': '请上传有效的图片文件',
                    'required': '特色图片是必填项',
                }
            )

            # 嵌套序列化字段
            author_detail = AuthorSerializer(source='author', read_only=True)
            category_detail = CategorySerializer(source='category', read_only=True)
            tag_details = TagSerializer(source='tags', many=True, read_only=True)

            # 只读字段
            id = serializers.IntegerField(read_only=True)
            view_count = serializers.IntegerField(read_only=True)
            like_count = serializers.IntegerField(read_only=True)
            comment_count = serializers.IntegerField(read_only=True)
            created_at = serializers.DateTimeField(read_only=True)
            updated_at = serializers.DateTimeField(read_only=True)
            published_at = serializers.DateTimeField(read_only=True)

            # 计算字段
            reading_time = serializers.SerializerMethodField(read_only=True)
            word_count = serializers.SerializerMethodField(read_only=True)
            seo_title = serializers.SerializerMethodField(read_only=True)
            seo_description = serializers.SerializerMethodField(read_only=True)

            # URL字段
            absolute_url = serializers.SerializerMethodField(read_only=True)
            api_url = serializers.HyperlinkedIdentityField(
                view_name='article-detail',
                lookup_field='slug'
            )

            class Meta:
                model = Article
                fields = [
                    # 基础字段
                    'id', 'title', 'slug', 'content', 'excerpt', 'status',

                    # 关系字段
                    'author', 'author_detail', 'category', 'category_detail',
                    'tags', 'tag_details',

                    # 媒体字段
                    'featured_image',

                    # 状态字段
                    'is_featured', 'is_top', 'view_count', 'like_count',
                    'comment_count',

                    # 时间字段
                    'created_at', 'updated_at', 'published_at',

                    # 计算字段
                    'reading_time', 'word_count', 'seo_title', 'seo_description',

                    # URL字段
                    'absolute_url', 'api_url',
                ]
                read_only_fields = [
                    'id', 'view_count', 'like_count', 'comment_count',
                    'created_at', 'updated_at', 'published_at'
                ]
                extra_kwargs = {
                    'slug': {'validators': []},  # 移除默认验证器,使用自定义验证
                }

            def validate_title(self, value):
                """标题验证"""
                # 检查标题是否包含敏感词
                sensitive_words = ['广告', '推广', '赚钱', '免费']
                for word in sensitive_words:
                    if word in value:
                        raise serializers.ValidationError(f'标题不能包含敏感词:{word}')

                # 检查标题唯一性(排除当前文章)
                queryset = Article.objects.filter(title__iexact=value)
                if self.instance:
                    queryset = queryset.exclude(pk=self.instance.pk)

                if queryset.exists():
                    raise serializers.ValidationError('标题已存在,请更换其他标题')

                return value

            def validate_slug(self, value):
                """URL别名验证"""
                if value:
                    # 检查slug格式
                    if not re.match(r'^[a-z0-9-]+$', value):
                        raise serializers.ValidationError('URL别名只能包含小写字母、数字和连字符')

                    # 检查slug唯一性
                    queryset = Article.objects.filter(slug__iexact=value)
                    if self.instance:
                        queryset = queryset.exclude(pk=self.instance.pk)

                    if queryset.exists():
                        raise serializers.ValidationError('URL别名已存在,请更换其他别名')

                return value

            def validate_content(self, value):
                """内容验证"""
                # 检查内容长度
                if len(value) < 100:
                    raise serializers.ValidationError('内容至少需要100个字符')

                # 检查是否包含恶意脚本
                if '<script' in value.lower():
                    raise serializers.ValidationError('内容不能包含脚本代码')

                return value

            def validate_category(self, value):
                """分类验证"""
                if value and not value.is_active:
                    raise serializers.ValidationError('选择的分类已被禁用')
                return value

            def validate_tags(self, value):
                """标签验证"""
                if value and len(value) > 10:
                    raise serializers.ValidationError('最多选择10个标签')
                return value

            def validate_featured_image(self, value):
                """特色图片验证"""
                if value:
                    # 检查文件大小(5MB限制)
                    if value.size > 5 * 1024 * 1024:
                        raise serializers.ValidationError('图片大小不能超过5MB')

                    # 检查文件格式
                    allowed_formats = ['JPEG', 'PNG', 'GIF', 'WebP']
                    if value.image.format not in allowed_formats:
                        raise serializers.ValidationError(f'只支持{", ".join(allowed_formats)}格式')

                return value

            def validate(self, attrs):
                """整体验证"""
                title = attrs.get('title', '')
                content = attrs.get('content', '')
                slug = attrs.get('slug', '')
                category = attrs.get('category')
                status = attrs.get('status')

                # 如果没有提供slug,根据标题自动生成
                if not slug and title:
                    attrs['slug'] = slugify(title)

                # 如果没有提供摘要,从内容中提取
                if not attrs.get('excerpt') and content:
                    attrs['excerpt'] = content[:200] + '...' if len(content) > 200 else content

                # 发布状态验证
                if status == 'published':
                    if not title or len(title) < 5:
                        raise serializers.ValidationError('发布文章必须填写标题(至少5个字符)')

                    if not content or len(content) < 100:
                        raise serializers.ValidationError('发布文章必须填写内容(至少100个字符)')

                # 分类验证
                if status == 'published' and not category:
                    raise serializers.ValidationError('发布文章必须选择分类')

                return attrs

            def get_reading_time(self, obj):
                """计算阅读时间"""
                if obj.content:
                    # 假设每分钟阅读200个中文字符
                    word_count = len(obj.content)
                    reading_time = max(1, word_count // 200)
                    return reading_time
                return 1

            def get_word_count(self, obj):
                """计算字数"""
                return len(obj.content) if obj.content else 0

            def get_seo_title(self, obj):
                """生成SEO标题"""
                if obj.excerpt:
                    return f"{obj.title} - {obj.excerpt[:50]}"
                return obj.title

            def get_seo_description(self, obj):
                """生成SEO描述"""
                if obj.excerpt:
                    return obj.excerpt
                elif obj.content:
                    return obj.content[:160] + '...' if len(obj.content) > 160 else obj.content
                return ''

            def get_absolute_url(self, obj):
                """获取绝对URL"""
                if obj.slug:
                    request = self.context.get('request')
                    if request:
                        return request.build_absolute_uri(obj.get_absolute_url())
                return ''

            def create(self, validated_data):
                """创建文章"""
                # 处理标签(多对多关系需要单独处理)
                tags_data = validated_data.pop('tags', [])

                # 创建文章
                article = Article.objects.create(**validated_data)

                # 添加标签
                if tags_data:
                    article.tags.set(tags_data)

                return article

            def update(self, instance, validated_data):
                """更新文章"""
                # 处理标签
                tags_data = validated_data.pop('tags', None)

                # 更新普通字段
                for attr, value in validated_data.items():
                    setattr(instance, attr, value)

                # 更新标签
                if tags_data is not None:
                    instance.tags.set(tags_data)

                instance.save()
                return instance

            def to_representation(self, instance):
                """自定义序列化输出"""
                data = super().to_representation(instance)

                # 添加额外的计算字段
                request = self.context.get('request')
                user = getattr(request, 'user', None) if request else None

                # 添加用户权限信息
                if user and user.is_authenticated:
                    data['user_permissions'] = {
                        'can_edit': instance.author == user or user.is_staff,
                        'can_delete': instance.author == user or user.is_superuser,
                        'can_like': True,
                        'can_comment': True,
                    }
                else:
                    data['user_permissions'] = {
                        'can_edit': False,
                        'can_delete': False,
                        'can_like': False,
                        'can_comment': False,
                    }

                return data

        # 在视图中使用自定义字段的序列化器
        from rest_framework import generics, permissions
        from rest_framework.response import Response
        from rest_framework import status

        class ArticleCreateView(generics.CreateAPIView):
            """创建文章视图"""
            queryset = Article.objects.all()
            serializer_class = ArticleDetailSerializer
            permission_classes = [permissions.IsAuthenticated]

            def perform_create(self, serializer):
                """设置作者为当前用户"""
                serializer.save(author=self.request.user)

        class ArticleUpdateView(generics.UpdateAPIView):
            """更新文章视图"""
            queryset = Article.objects.all()
            serializer_class = ArticleDetailSerializer
            lookup_field = 'slug'
            permission_classes = [permissions.IsAuthenticated]

            def get_queryset(self):
                """只允许作者或管理员编辑"""
                user = self.request.user
                if user.is_staff:
                    return Article.objects.all()
                return Article.objects.filter(author=user)

            def update(self, request, *args, **kwargs):
                """重写更新方法"""
                partial = kwargs.pop('partial', False)
                instance = self.get_object()
                serializer = self.get_serializer(instance, data=request.data, partial=partial)

                if serializer.is_valid():
                    self.perform_update(serializer)

                    return Response({
                        'success': True,
                        'message': '文章更新成功',
                        'data': serializer.data
                    }, status=status.HTTP_200_OK)
                else:
                    return Response({
                        'success': False,
                        'message': '文章更新失败',
                        'errors': serializer.errors
                    }, status=status.HTTP_400_BAD_REQUEST)

03.嵌套序列化
    a.说明
        嵌套关系处理
        深度控制
        性能优化考虑
    b.示例
        # apps/blog/serializers.py
        from rest_framework import serializers
        from django.contrib.auth import get_user_model
        from .models import Article, Category, Tag, Comment

        User = get_user_model()

        class CommentSerializer(serializers.ModelSerializer):
            """评论序列化器"""

            author = serializers.StringRelatedField(read_only=True)
            author_avatar = serializers.SerializerMethodField(read_only=True)
            created_at = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')

            # 嵌套回复
            replies = serializers.SerializerMethodField(read_only=True)
            reply_count = serializers.SerializerMethodField(read_only=True)

            class Meta:
                model = Comment
                fields = [
                    'id', 'author', 'author_avatar', 'content', 'created_at',
                    'is_approved', 'replies', 'reply_count'
                ]
                read_only_fields = ['id', 'author', 'created_at', 'is_approved']

            def get_author_avatar(self, obj):
                """获取作者头像"""
                if obj.author.profile.avatar:
                    request = self.context.get('request')
                    if request:
                        return request.build_absolute_uri(obj.author.profile.avatar.url)
                return ''

            def get_replies(self, obj):
                """获取回复评论"""
                replies = obj.replies.filter(is_approved=True).order_by('created_at')
                return CommentSerializer(replies, many=True, context=self.context).data

            def get_reply_count(self, obj):
                """获取回复数量"""
                return obj.replies.filter(is_approved=True).count()

        class ArticleWithCommentsSerializer(serializers.ModelSerializer):
            """包含评论的文章序列化器"""

            # 基础嵌套
            author = AuthorSerializer(read_only=True)
            category = CategorySerializer(read_only=True)
            tags = TagSerializer(many=True, read_only=True)

            # 深度嵌套评论
            comments = CommentSerializer(
                source='comments_set',
                many=True,
                read_only=True
            )

            # 评论统计
            comment_count = serializers.SerializerMethodField(read_only=True)
            approved_comment_count = serializers.SerializerMethodField(read_only=True)

            class Meta:
                model = Article
                fields = [
                    'id', 'title', 'slug', 'author', 'category', 'tags',
                    'content', 'excerpt', 'status', 'featured_image',
                    'view_count', 'like_count', 'comment_count', 'approved_comment_count',
                    'is_featured', 'is_top', 'created_at', 'updated_at', 'published_at',
                    'comments'
                ]
                depth = 2  # 控制嵌套深度

            def get_comment_count(self, obj):
                """获取总评论数"""
                return obj.comments_set.count()

            def get_approved_comment_count(self, obj):
                """获取已审核评论数"""
                return obj.comments_set.filter(is_approved=True).count()

        class CategoryWithArticlesSerializer(serializers.ModelSerializer):
            """包含文章的分类序列化器"""

            # 嵌套文章列表(限制数量)
            recent_articles = serializers.SerializerMethodField(read_only=True)
            article_count = serializers.SerializerMethodField(read_only=True)

            # 嵌套子分类
            children = serializers.SerializerMethodField(read_only=True)
            children_count = serializers.SerializerMethodField(read_only=True)

            class Meta:
                model = Category
                fields = [
                    'id', 'name', 'slug', 'description', 'parent',
                    'is_active', 'article_count', 'children_count',
                    'recent_articles', 'children', 'created_at', 'updated_at'
                ]

            def get_recent_articles(self, obj):
                """获取最新文章(限制5篇)"""
                articles = obj.articles.filter(
                    status='published'
                ).select_related('author').order_by('-published_at')[:5]

                return ArticleListSerializer(
                    articles,
                    many=True,
                    context=self.context
                ).data

            def get_article_count(self, obj):
                """获取文章数量"""
                return obj.articles.filter(status='published').count()

            def get_children(self, obj):
                """获取子分类"""
                children = obj.children.filter(is_active=True)
                return CategorySerializer(children, many=True, context=self.context).data

            def get_children_count(self, obj):
                """获取子分类数量"""
                return obj.children.filter(is_active=True).count()

        # 性能优化的嵌套序列化
        class OptimizedArticleSerializer(serializers.ModelSerializer):
            """性能优化的文章序列化器"""

            # 使用PrimaryKeyRelatedField减少嵌套查询
            author_id = serializers.IntegerField(source='author.id', read_only=True)
            author_username = serializers.CharField(source='author.username', read_only=True)
            category_id = serializers.IntegerField(source='category.id', read_only=True, allow_null=True)
            category_name = serializers.CharField(source='category.name', read_only=True, allow_null=True)

            # 标签只返回基本信息
            tag_ids = serializers.ListField(
                child=serializers.IntegerField(),
                source='tags.values_list',
                read_only=True
            )

            tag_names = serializers.ListField(
                child=serializers.CharField(),
                source='tags.values_list',
                read_only=True
            )

            class Meta:
                model = Article
                fields = [
                    'id', 'title', 'slug', 'excerpt', 'status',
                    'author_id', 'author_username', 'category_id', 'category_name',
                    'tag_ids', 'tag_names', 'featured_image',
                    'view_count', 'like_count', 'comment_count',
                    'is_featured', 'is_top', 'created_at', 'updated_at', 'published_at'
                ]

        # 动态嵌套序列化器
        class DynamicArticleSerializer(serializers.ModelSerializer):
            """动态嵌套序列化器"""

            def __init__(self, *args, **kwargs):
                """根据请求参数动态包含嵌套字段"""
                # 从context中获取请求参数
                request = kwargs.get('context', {}).get('request')
                include = request.GET.get('include', '') if request else ''

                # 动态添加字段
                if 'author' in include:
                    self.fields['author'] = AuthorSerializer(read_only=True)
                else:
                    self.fields['author_id'] = serializers.IntegerField(source='author.id', read_only=True)
                    self.fields['author_username'] = serializers.CharField(source='author.username', read_only=True)

                if 'category' in include:
                    self.fields['category'] = CategorySerializer(read_only=True)
                else:
                    self.fields['category_id'] = serializers.IntegerField(source='category.id', read_only=True, allow_null=True)
                    self.fields['category_name'] = serializers.CharField(source='category.name', read_only=True, allow_null=True)

                if 'tags' in include:
                    self.fields['tags'] = TagSerializer(many=True, read_only=True)
                else:
                    self.fields['tag_ids'] serializers.ListField(
                        child=serializers.IntegerField(),
                        source='tags.values_list',
                        read_only=True
                    )

                if 'comments' in include:
                    self.fields['comments'] = CommentSerializer(many=True, read_only=True)

                super().__init__(*args, **kwargs)

            class Meta:
                model = Article
                fields = []  # 动态设置字段

        # 在视图中使用嵌套序列化器
        from rest_framework import generics
        from rest_framework.permissions import IsAuthenticatedOrReadOnly

        class ArticleDetailView(generics.RetrieveAPIView):
            """文章详情视图"""
            queryset = Article.objects.filter(status='published')
            serializer_class = ArticleWithCommentsSerializer
            lookup_field = 'slug'
            permission_classes = [IsAuthenticatedOrReadOnly]

            def get_queryset(self):
                """优化查询性能"""
                return super().get_queryset().select_related(
                    'author', 'category'
                ).prefetch_related(
                    'tags',
                    'comments_set__author',
                    'comments_set__replies__author'
                )

        class CategoryDetailView(generics.RetrieveAPIView):
            """分类详情视图"""
            queryset = Category.objects.filter(is_active=True)
            serializer_class = CategoryWithArticlesSerializer
            lookup_field = 'slug'

            def get_queryset(self):
                """优化查询性能"""
                return super().get_queryset().prefetch_related(
                    'children',
                    'articles__author',
                    'articles__tags'
                )

3.3 高级序列化技术

01.内置验证器
    a.说明
        UniqueValidator验证
        UniqueTogetherValidator验证
        基于日期的验证器
    b.内置验证器示例
        # apps/blog/validators.py
        from rest_framework import serializers
        from django.core.exceptions import ValidationError
        from django.core.validators import RegexValidator
        from django.utils.translation import gettext_lazy as _
        from datetime import datetime, date
        import re

        # 自定义唯一性验证器
        class UniqueTitleValidator:
            """标题唯一性验证器"""
            message = _('具有此标题的文章已存在。')

            def __init__(self, queryset=None, message=None):
                self.queryset = queryset
                if message:
                    self.message = message

            def __call__(self, value, serializer_field):
                """验证标题唯一性"""
                from .models import Article

                # 获取查询集
                if self.queryset is None:
                    self.queryset = Article.objects.all()

                # 排除当前实例
                if serializer_field.parent.instance:
                    self.queryset = self.queryset.exclude(pk=serializer_field.parent.instance.pk)

                # 检查唯一性
                if self.queryset.filter(title__iexact=value).exists():
                    raise serializers.ValidationError(self.message)

        # 自定义验证器函数
        def validate_seo_title(value):
            """验证SEO标题"""
            if len(value) < 10:
                raise serializers.ValidationError('SEO标题至少10个字符')

            if len(value) > 60:
                raise serializers.ValidationError('SEO标题不能超过60个字符')

            # 检查是否包含关键词
            if not any(keyword in value.lower() for keyword in ['如何', '什么', '为什么', '方法']):
                raise serializers.ValidationError('SEO标题应该包含引导性关键词')

            return value

        def validate_publish_date(value):
            """验证发布日期"""
            if value and value > date.today():
                raise serializers.ValidationError('发布日期不能是未来时间')

            if value and value < date(2020, 1, 1):
                raise serializers.ValidationError('发布日期不能早于2020年')

            return value

        def validate_article_content(value):
            """验证文章内容"""
            # 检查最小长度
            if len(value) < 500:
                raise serializers.ValidationError('文章内容至少需要500个字符')

            # 检查是否包含HTML标签(如果需要纯文本)
            if '<' in value and '>' in value:
                # 简单的HTML标签检测
                import re
                html_tags = re.findall(r'<[^>]+>', value)
                if html_tags:
                    raise serializers.ValidationError('内容不应包含HTML标签')

            # 检查关键词密度
            words = re.findall(r'\b\w+\b', value.lower())
            if len(words) < 100:
                raise serializers.ValidationError('内容词汇量过少,请丰富内容')

            # 检查重复内容
            unique_words = set(words)
            if len(unique_words) / len(words) < 0.3:
                raise serializers.ValidationError('内容重复率过高,请提高内容质量')

            return value

        # 在序列化器中使用验证器
        # apps/blog/serializers.py
        from rest_framework import serializers
        from .models import Article, Category, Tag
        from .validators import (
            UniqueTitleValidator, validate_seo_title,
            validate_publish_date, validate_article_content
        )

        class AdvancedArticleSerializer(serializers.ModelSerializer):
            """高级文章序列化器"""

            # 使用验证器的字段
            title = serializers.CharField(
                max_length=200,
                validators=[
                    UniqueTitleValidator(),
                    RegexValidator(
                        regex=r'^[a-zA-Z0-9\u4e00-\u9fa5\s\-_!?,.]+$',
                        message='标题只能包含字母、数字、中文、空格和基本标点符号'
                    )
                ],
                help_text="文章标题,必须唯一"
            )

            slug = serializers.SlugField(
                max_length=200,
                validators=[
                    RegexValidator(
                        regex=r'^[a-z0-9-]+$',
                        message='URL别名只能包含小写字母、数字和连字符'
                    )
                ]
            )

            seo_title = serializers.CharField(
                max_length=60,
                required=False,
                validators=[validate_seo_title],
                help_text="SEO优化标题,10-60个字符"
            )

            seo_description = serializers.CharField(
                max_length=160,
                required=False,
                help_text="SEO描述,建议150个字符以内"
            )

            content = serializers.CharField(
                validators=[validate_article_content],
                help_text="文章内容,至少500个字符"
            )

            publish_date = serializers.DateField(
                required=False,
                validators=[validate_publish_date],
                help_text="发布日期,不能是未来时间"
            )

            tags = serializers.ListField(
                child=serializers.SlugField(max_length=50),
                required=False,
                allow_empty=True,
                help_text="标签列表,每个标签最多50个字符"
            )

            # 自定义验证字段
            reading_difficulty = serializers.ChoiceField(
                choices=[
                    ('beginner', '初级'),
                    ('intermediate', '中级'),
                    ('advanced', '高级'),
                ],
                required=False,
                default='beginner',
                help_text="阅读难度"
            )

            estimated_reading_time = serializers.IntegerField(
                required=False,
                min_value=1,
                max_value=60,
                help_text="预估阅读时间(分钟)"
            )

            class Meta:
                model = Article
                fields = [
                    'title', 'slug', 'seo_title', 'seo_description',
                    'content', 'excerpt', 'status', 'category', 'author',
                    'tags', 'featured_image', 'publish_date',
                    'reading_difficulty', 'estimated_reading_time',
                    'is_featured', 'is_top'
                ]
                validators = [
                    # 模型级别验证器
                    serializers.UniqueTogetherValidator(
                        queryset=Article.objects.all(),
                        fields=['title', 'author'],
                        message='同一作者不能有相同标题的文章'
                    ),
                    serializers.UniqueTogetherValidator(
                        queryset=Article.objects.all(),
                        fields=['slug', 'status'],
                        message='已发布的文章URL别名必须唯一'
                    )
                ]

            def validate_excerpt(self, value):
                """验证摘要"""
                if value and len(value) > len(self.content):
                    raise serializers.ValidationError('摘要长度不能超过正文长度')

                if value and len(value) < 50:
                    raise serializers.ValidationError('摘要至少需要50个字符')

                return value

            def validate_featured_image(self, value):
                """验证特色图片"""
                if value:
                    # 检查图片尺寸
                    if value.image.width < 800 or value.image.height < 400:
                        raise serializers.ValidationError('图片尺寸至少800x400像素')

                    # 检查图片比例
                    aspect_ratio = value.image.width / value.image.height
                    if aspect_ratio < 1.5 or aspect_ratio > 3:
                        raise serializers.ValidationError('图片宽高比应在1.5:1到3:1之间')

                return value

            def validate_tags(self, value):
                """验证标签"""
                if value:
                    # 检查标签数量
                    if len(value) > 10:
                        raise serializers.ValidationError('最多添加10个标签')

                    # 检查标签长度
                    for tag in value:
                        if len(tag) < 2:
                            raise serializers.ValidationError('标签长度至少2个字符')
                        if len(tag) > 20:
                            raise serializers.ValidationError('标签长度不能超过20个字符')

                    # 检查重复标签
                    if len(set(value)) != len(value):
                        raise serializers.ValidationError('不能添加重复的标签')

                return value

            def validate(self, attrs):
                """整体验证"""
                title = attrs.get('title', '')
                content = attrs.get('content', '')
                excerpt = attrs.get('excerpt', '')
                seo_title = attrs.get('seo_title', '')
                seo_description = attrs.get('seo_description', '')
                status = attrs.get('status', 'draft')

                # 发布状态验证
                if status == 'published':
                    if not title.strip():
                        raise serializers.ValidationError('发布文章必须填写标题')

                    if not content.strip():
                        raise serializers.ValidationError('发布文章必须填写内容')

                    if len(content) < 500:
                        raise serializers.ValidationError('发布文章内容至少500个字符')

                # SEO验证
                if status == 'published' and not seo_title:
                    # 自动生成SEO标题
                    attrs['seo_title'] = title[:50] + ('...' if len(title) > 50 else '')

                if status == 'published' and not seo_description:
                    # 自动生成SEO描述
                    attrs['seo_description'] = excerpt[:150] + ('...' if len(excerpt) > 150 else content[:150] + '...')

                # 内容质量验证
                if content:
                    # 检查标题和内容的相关性
                    title_words = set(re.findall(r'\b\w+\b', title.lower()))
                    content_words = set(re.findall(r'\b\w+\b', content.lower()))

                    if title_words:
                        relevance = len(title_words & content_words) / len(title_words)
                        if relevance < 0.2:
                            raise serializers.ValidationError('文章内容与标题相关性较低,请调整内容或标题')

                return attrs

        # 视图中使用高级验证器
        from rest_framework import generics, permissions, status
        from rest_framework.response import Response

        class AdvancedArticleCreateView(generics.CreateAPIView):
            """高级文章创建视图"""
            queryset = Article.objects.all()
            serializer_class = AdvancedArticleSerializer
            permission_classes = [permissions.IsAuthenticated]

            def perform_create(self, serializer):
                """设置作者并额外验证"""
                user = self.request.user

                # 检查用户今日发布文章数量限制
                from django.utils import timezone
                today = timezone.now().date()
                today_count = Article.objects.filter(
                    author=user,
                    created_at__date=today,
                    status='published'
                ).count()

                if today_count >= 5:  # 每日最多发布5篇
                    raise serializers.ValidationError('今日发布文章数量已达上限(5篇)')

                # 检查用户积分
                if hasattr(user, 'profile') and user.profile.points < 100:
                    raise serializers.ValidationError('积分不足,无法发布文章')

                serializer.save(author=user)

            def create(self, request, *args, **kwargs):
                """重写创建方法"""
                try:
                    response = super().create(request, *args, **kwargs)
                    return Response({
                        'success': True,
                        'message': '文章创建成功',
                        'data': response.data
                    }, status=status.HTTP_201_CREATED)
                except serializers.ValidationError as e:
                    return Response({
                        'success': False,
                        'message': '文章创建失败',
                        'errors': e.detail
                    }, status=status.HTTP_400_BAD_REQUEST)

02.自定义验证逻辑
    a.说明
        复杂验证规则
        跨字段验证
        条件验证
    b.自定义验证示例
        # apps/blog/advanced_validators.py
        from rest_framework import serializers
        from django.core.exceptions import ValidationError
        from django.contrib.auth import get_user_model
        from django.utils import timezone
        from datetime import datetime, timedelta
        import re

        User = get_user_model()

        class ArticleQualityValidator:
            """文章质量验证器"""

            def __init__(self, min_quality_score=70):
                self.min_quality_score = min_quality_score

            def __call__(self, value, serializer_field):
                """验证文章质量"""
                content = value
                score = 0

                # 检查内容长度 (20分)
                if len(content) >= 1000:
                    score += 20
                elif len(content) >= 500:
                    score += 10

                # 检查段落结构 (20分)
                paragraphs = content.split('\n\n')
                if len(paragraphs) >= 5:
                    score += 15
                elif len(paragraphs) >= 3:
                    score += 10

                # 检查标点符号使用 (10分)
                if any(punct in content for punct in ['。', '!', '?']):
                    score += 10

                # 检查数字和链接 (10分)
                if re.search(r'\d+', content):
                    score += 5
                if 'http' in content:
                    score += 5

                # 检查关键词丰富度 (20分)
                words = re.findall(r'\b\w+\b', content.lower())
                unique_words = set(words)
                if len(unique_words) / len(words) > 0.5:
                    score += 20
                elif len(unique_words) / len(words) > 0.3:
                    score += 10

                # 检查格式化 (20分)
                if '##' in content or '###' in content:  # 标题
                    score += 10
                if '```' in content:  # 代码块
                    score += 5
                if '1.' in content or '2.' in content:  # 列表
                    score += 5

                if score < self.min_quality_score:
                    raise serializers.ValidationError(
                        f'文章质量分数不足({score}/{self.min_quality_score}),请提升内容质量'
                    )

        class ContentPlagiarismValidator:
            """内容抄袭检查验证器"""

            def __init__(self, similarity_threshold=0.8):
                self.similarity_threshold = similarity_threshold

            def __call__(self, value, serializer_field):
                """检查内容抄袭"""
                from .models import Article

                content = value.lower()
                words = set(re.findall(r'\b\w+\b', content))

                # 获取已发布的文章
                existing_articles = Article.objects.filter(status='published')

                if serializer_field.parent.instance:
                    existing_articles = existing_articles.exclude(pk=serializer_field.parent.instance.pk)

                for article in existing_articles:
                    existing_words = set(re.findall(r'\b\w+\b', article.content.lower()))

                    # 计算相似度
                    if words and existing_words:
                        similarity = len(words & existing_words) / len(words | existing_words)

                        if similarity >= self.similarity_threshold:
                            raise serializers.ValidationError(
                                f'内容与已发布的文章《{article.title}》相似度过高({similarity:.0%})'
                            )

        class PublishingScheduleValidator:
            """发布计划验证器"""

            def __init__(self, max_per_day=3, min_interval_hours=2):
                self.max_per_day = max_per_day
                self.min_interval = timedelta(hours=min_interval_hours)

            def __call__(self, value, serializer_field):
                """验证发布计划"""
                user = serializer_field.context.get('request').user
                publish_date = value

                if publish_date:
                    # 检查发布日期是否合理
                    now = timezone.now()

                    if publish_date < now - timedelta(days=1):
                        raise serializers.ValidationError('不能发布超过一天前的文章')

                    if publish_date > now + timedelta(days=30):
                        raise serializers.ValidationError('不能预约超过30天后的发布')

                    # 检查每日发布限制
                    start_of_day = publish_date.replace(hour=0, minute=0, second=0, microsecond=0)
                    end_of_day = start_of_day + timedelta(days=1)

                    from .models import Article
                    day_count = Article.objects.filter(
                        author=user,
                        published_at__gte=start_of_day,
                        published_at__lt=end_of_day,
                        status='published'
                    ).count()

                    if day_count >= self.max_per_day:
                        raise serializers.ValidationError(f'每日最多发布{self.max_per_day}篇文章')

                    # 检查发布间隔
                    recent_articles = Article.objects.filter(
                        author=user,
                        published_at__gte=publish_date - self.min_interval,
                        published_at__lte=publish_date + self.min_interval,
                        status='published'
                    )

                    if recent_articles.exists():
                        raise serializers.ValidationError(f'文章发布间隔至少需要{self.min_interval.seconds // 3600}小时')

        # 高级序列化器
        # apps/blog/serializers.py
        from rest_framework import serializers
        from .models import Article
        from .advanced_validators import (
            ArticleQualityValidator, ContentPlagiarismValidator,
            PublishingScheduleValidator
        )

        class ExpertArticleSerializer(serializers.ModelSerializer):
            """专家级文章序列化器"""

            # 使用高级验证器
            content = serializers.CharField(
                validators=[
                    ArticleQualityValidator(min_quality_score=75),
                    ContentPlagiarismValidator(similarity_threshold=0.75)
                ]
            )

            scheduled_publish_at = serializers.DateTimeField(
                required=False,
                validators=[
                    PublishingScheduleValidator(max_per_day=3, min_interval_hours=2)
                ],
                help_text="预约发布时间"
            )

            # SEO字段
            focus_keywords = serializers.ListField(
                child=serializers.CharField(max_length=20),
                required=False,
                max_length=5,
                help_text="SEO关键词,最多5个"
            )

            meta_robots = serializers.ChoiceField(
                choices=[
                    ('index,follow', '索引并跟踪链接'),
                    ('index,nofollow', '索引但不跟踪链接'),
                    ('noindex,follow', '不索引但跟踪链接'),
                    ('noindex,nofollow', '不索引也不跟踪链接'),
                ],
                default='index,follow',
                required=False,
                help_text="搜索引擎索引指令"
            )

            # 内容策略字段
            content_type = serializers.ChoiceField(
                choices=[
                    ('tutorial', '教程'),
                    ('news', '新闻'),
                    ('analysis', '分析'),
                    ('review', '评测'),
                    ('opinion', '观点'),
                ],
                required=False,
                help_text="内容类型"
            )

            target_audience = serializers.ChoiceField(
                choices=[
                    ('beginner', '初学者'),
                    ('intermediate', '中级用户'),
                    ('advanced', '高级用户'),
                    ('expert', '专家'),
                ],
                required=False,
                help_text="目标读者"
            )

            estimated_read_time = serializers.IntegerField(
                required=False,
                min_value=1,
                max_value=30,
                help_text="预估阅读时间(分钟)"
            )

            class Meta:
                model = Article
                fields = [
                    'title', 'slug', 'content', 'excerpt', 'status',
                    'category', 'tags', 'featured_image', 'scheduled_publish_at',
                    'focus_keywords', 'meta_robots', 'content_type',
                    'target_audience', 'estimated_read_time',
                    'is_featured', 'is_top'
                ]

            def validate_title(self, value):
                """高级标题验证"""
                # 长度检查
                if len(value) < 10:
                    raise serializers.ValidationError('标题至少需要10个字符')

                if len(value) > 100:
                    raise serializers.ValidationError('标题不能超过100个字符')

                # 标题格式检查
                if not re.search(r'[a-zA-Z\u4e00-\u9fa5]', value):
                    raise serializers.ValidationError('标题必须包含文字')

                # 检查是否包含数字(增加可信度)
                if not re.search(r'\d', value):
                    # 除非是特定类型的内容
                    content_type = self.initial_data.get('content_type', '')
                    if content_type not in ['opinion', 'analysis']:
                        pass  # 允许某些类型不包含数字

                # 检查情感词汇
                positive_words = ['如何', '方法', '技巧', '秘诀', '完整', '详细', '实用']
                if not any(word in value for word in positive_words):
                    # 建议添加积极词汇
                    pass

                # 检查标题吸引力
                attractive_patterns = [
                    r'\d+个?方法',  # 数字+方法
                    r'.*终极.*指南',  # 终极指南
                    r'.*完全.*手册',  # 完全手册
                    r'.*必知.*技巧',  # 必知技巧
                ]

                has_attractive = any(re.search(pattern, value) for pattern in attractive_patterns)

                return value

            def validate_content(self, value):
                """高级内容验证"""
                # 结构检查
                if not re.search(r'^#{1,6}\s', value, re.MULTILINE):
                    # 没有标题结构
                    pass  # 建议添加标题结构

                # 检查段落长度
                paragraphs = value.split('\n\n')
                long_paragraphs = [p for p in paragraphs if len(p) > 500]

                if len(long_paragraphs) > len(paragraphs) * 0.3:
                    raise serializers.ValidationError('过长的段落太多,建议拆分')

                # 检查媒体内容
                has_images = '!' in value  # Markdown图片语法
                has_code = '```' in value
                has_links = 'http' in value or '[' in value

                content_type = self.initial_data.get('content_type', '')

                if content_type == 'tutorial' and not has_code:
                    pass  # 教程建议包含代码示例

                if content_type == 'analysis' and not has_links:
                    pass  # 分析文章建议包含引用链接

                # 检查可读性
                sentences = re.split(r'[。!?]', value)
                avg_sentence_length = sum(len(s) for s in sentences) / len(sentences) if sentences else 0

                if avg_sentence_length > 50:
                    pass  # 句子过长,影响可读性

                return value

            def validate_focus_keywords(self, value):
                """验证SEO关键词"""
                if value:
                    # 检查关键词长度
                    for keyword in value:
                        if len(keyword) < 2:
                            raise serializers.ValidationError('关键词至少2个字符')
                        if len(keyword) > 20:
                            raise serializers.ValidationError('关键词不能超过20个字符')

                    # 检查关键词重复
                    if len(set(value)) != len(value):
                        raise serializers.ValidationError('不能有重复的关键词')

                    # 检查关键词在标题中的出现
                    title = self.initial_data.get('title', '').lower()
                    content = self.initial_data.get('content', '').lower()

                    for keyword in value:
                        keyword_lower = keyword.lower()
                        if keyword_lower not in title and keyword_lower not in content:
                            raise serializers.ValidationError(f'关键词"{keyword}"应在标题或内容中出现')

                return value

            def validate(self, attrs):
                """整体高级验证"""
                title = attrs.get('title', '')
                content = attrs.get('content', '')
                excerpt = attrs.get('excerpt', '')
                content_type = attrs.get('content_type', '')
                target_audience = attrs.get('target_audience', '')
                estimated_read_time = attrs.get('estimated_read_time')

                # 内容类型和目标读者一致性检查
                if content_type == 'beginner' and target_audience == 'expert':
                    raise serializers.ValidationError('初学者内容不适合专家读者')

                if content_type == 'tutorial' and target_audience == 'expert':
                    raise serializers.ValidationError('专家级教程需要更高级的内容')

                # 阅读时间估算验证
                if content and not estimated_read_time:
                    # 自动计算阅读时间
                    word_count = len(re.findall(r'\b\w+\b', content))
                    estimated_time = max(1, word_count // 200)  # 每分钟200字
                    attrs['estimated_read_time'] = estimated_time

                elif estimated_read_time and content:
                    # 验证估算时间是否合理
                    word_count = len(re.findall(r'\b\w+\b', content))
                    expected_time = max(1, word_count // 200)

                    if abs(estimated_read_time - expected_time) > 5:
                        raise serializers.ValidationError(
                            f'预估阅读时间{estimated_read_time}分钟与实际内容{expected_time}分钟相差过大'
                        )

                # 摘要质量检查
                if excerpt and len(excerpt) < len(title):
                    raise serializers.ValidationError('摘要长度应大于标题长度')

                if excerpt and not any(word in excerpt.lower() for word in title.lower().split()):
                    raise serializers.ValidationError('摘要应包含标题中的关键词')

                # 内容策略验证
                if content_type == 'tutorial':
                    if not re.search(r'步骤|方法|操作', title) and not re.search(r'如何|怎么', title):
                        pass  # 教程标题建议包含指导性词汇

                elif content_type == 'news':
                    if not re.search(r'\d{4}年|\d{1,2}月|最新|今日', title):
                        pass  # 新闻标题建议包含时间信息

                elif content_type == 'analysis':
                    if not re.search(r'分析|研究|报告|数据', title):
                        pass  # 分析文章标题建议包含分析性词汇

                return attrs

            def create(self, validated_data):
                """创建文章时的额外处理"""
                # 提取标签
                tags_data = validated_data.pop('tags', [])

                # 设置SEO默认值
                if not validated_data.get('meta_robots'):
                    if validated_data.get('status') == 'draft':
                        validated_data['meta_robots'] = 'noindex,nofollow'

                # 创建文章
                article = Article.objects.create(**validated_data)

                # 处理标签
                if tags_data:
                    article.tags.set(tags_data)

                # 记录内容分析
                self._analyze_content_quality(article)

                return article

            def _analyze_content_quality(self, article):
                """分析内容质量并记录"""
                content = article.content
                words = re.findall(r'\b\w+\b', content)
                sentences = re.split(r'[。!?]', content)
                paragraphs = content.split('\n\n')

                quality_metrics = {
                    'word_count': len(words),
                    'sentence_count': len(sentences),
                    'paragraph_count': len(paragraphs),
                    'avg_sentence_length': sum(len(s) for s in sentences) / len(sentences) if sentences else 0,
                    'avg_paragraph_length': sum(len(p) for p in paragraphs) / len(paragraphs) if paragraphs else 0,
                    'unique_words_ratio': len(set(words)) / len(words) if words else 0,
                }

                # 可以保存到日志或分析表
                import logging
                logger = logging.getLogger('content_quality')
                logger.info(f"Article {article.id} quality metrics: {quality_metrics}")

03.动态序列化
    a.说明
        条件字段显示
        运行时序列化配置
        多态序列化
    b.动态序列化示例
        # apps/blog/dynamic_serializers.py
        from rest_framework import serializers
        from django.contrib.auth import get_user_model
        from .models import Article, Category, Tag

        User = get_user_model()

        class DynamicFieldsSerializer(serializers.ModelSerializer):
            """动态字段序列化器基类"""

            def __init__(self, *args, **kwargs):
                """根据请求参数动态包含字段"""
                # 从kwargs中移除fields参数
                fields = kwargs.pop('fields', None)
                exclude = kwargs.pop('exclude', None)

                super().__init__(*args, **kwargs)

                if fields is not None:
                    # 只保留指定的字段
                    allowed = set(fields)
                    existing = set(self.fields.keys())
                    for field_name in existing - allowed:
                        self.fields.pop(field_name)

                if exclude is not None:
                    # 排除指定的字段
                    excluded = set(exclude)
                    for field_name in excluded:
                        self.fields.pop(field_name, None)

        class DynamicArticleSerializer(DynamicFieldsSerializer):
            """动态文章序列化器"""

            def __init__(self, *args, **kwargs):
                """根据请求动态配置序列化器"""
                # 获取请求对象
                request = kwargs.get('context', {}).get('request')
                user = getattr(request, 'user', None) if request else None

                super().__init__(*args, **kwargs)

                if not request:
                    return

                # 根据用户权限动态添加字段
                if user and user.is_authenticated:
                    # 为认证用户添加更多字段
                    self.fields['author_email'] = serializers.SerializerMethodField()

                    if user.is_staff:
                        # 为管理员添加管理字段
                        self.fields['admin_notes'] = serializers.CharField(read_only=True)
                        self.fields['moderation_status'] = serializers.CharField(read_only=True)

                # 根据查询参数添加嵌套字段
                include = request.GET.get('include', '').split(',')
                if 'author_detail' in include:
                    self.fields['author_detail'] = AuthorSerializer(read_only=True)

                if 'category_detail' in include:
                    self.fields['category_detail'] = CategorySerializer(read_only=True)

                if 'tags_detail' in include:
                    self.fields['tags_detail'] = TagSerializer(many=True, read_only=True)

                # 根据内容类型添加特定字段
                if self.instance and self.instance.content_type == 'tutorial':
                    self.fields['difficulty_level'] = serializers.ChoiceField(
                        choices=[('beginner', '初级'), ('intermediate', '中级'), ('advanced', '高级')],
                        read_only=True
                    )
                    self.fields['prerequisites'] = serializers.ListField(
                        child=serializers.CharField(),
                        read_only=True
                    )

                elif self.instance and self.instance.content_type == 'analysis':
                    self.fields['data_sources'] = serializers.ListField(
                        child=serializers.CharField(),
                        read_only=True
                    )
                    self.fields['methodology'] = serializers.CharField(read_only=True)

            class Meta:
                model = Article
                fields = ['id', 'title', 'slug', 'excerpt', 'status', 'created_at']

            def get_author_email(self, obj):
                """获取作者邮箱(仅认证用户可见)"""
                request = self.context.get('request')
                if request and request.user.is_authenticated:
                    return obj.author.email
                return None

        class ContextAwareSerializer(serializers.ModelSerializer):
            """上下文感知序列化器"""

            def __init__(self, *args, **kwargs):
                """根据上下文调整序列化行为"""
                super().__init__(*args, **kwargs)

                context = kwargs.get('context', {})
                request = context.get('request')
                view = context.get('view')

                if request:
                    # 根据HTTP方法调整字段
                    if request.method == 'POST':
                        # 创建时添加验证字段
                        self.fields['confirm_password'] = serializers.CharField(write_only=True)

                    elif request.method in ['PUT', 'PATCH']:
                        # 更新时添加版本控制字段
                        self.fields['last_modified'] = serializers.DateTimeField(read_only=True)

                if view:
                    # 根据视图类型调整序列化
                    if view.action == 'list':
                        # 列表视图使用精简字段
                        self.fields.pop('content', None)
                        self.fields.pop('admin_notes', None)

                    elif view.action == 'retrieve':
                        # 详情视图显示完整信息
                        self.fields['content'] = serializers.CharField()
                        self.fields['view_count'] = serializers.IntegerField(read_only=True)

        class PolymorphicSerializer(serializers.Serializer):
            """多态序列化器"""

            def __init__(self, *args, **kwargs):
                """根据对象类型选择合适的序列化器"""
                super().__init__(*args, **kwargs)

                # 获取要序列化的对象
                instance = kwargs.get('instance')
                data = kwargs.get('data')

                if instance:
                    # 根据实例类型选择序列化器
                    serializer_class = self.get_serializer_class(instance)
                    self.polymorphic_serializer = serializer_class(
                        instance=instance,
                        context=kwargs.get('context', {})
                    )
                elif data:
                    # 根据数据类型选择序列化器
                    serializer_class = self.get_serializer_class_from_data(data)
                    self.polymorphic_serializer = serializer_class(
                        data=data,
                        context=kwargs.get('context', {})
                    )
                else:
                    self.polymorphic_serializer = None

            def get_serializer_class(self, instance):
                """根据实例类型返回对应的序列化器类"""
                from .models import Article, VideoArticle, PodcastArticle

                if isinstance(instance, VideoArticle):
                    return VideoArticleSerializer
                elif isinstance(instance, PodcastArticle):
                    return PodcastArticleSerializer
                elif isinstance(instance, Article):
                    return ArticleSerializer
                else:
                    raise ValueError(f"Unknown instance type: {type(instance)}")

            def get_serializer_class_from_data(self, data):
                """根据数据类型返回对应的序列化器类"""
                content_type = data.get('content_type', 'article')

                if content_type == 'video':
                    return VideoArticleSerializer
                elif content_type == 'podcast':
                    return PodcastArticleSerializer
                else:
                    return ArticleSerializer

            def to_representation(self, instance):
                """委托给对应的序列化器"""
                return self.polymorphic_serializer.to_representation(instance)

            def to_internal_value(self, data):
                """委托给对应的序列化器"""
                return self.polymorphic_serializer.to_internal_value(data)

            def is_valid(self, raise_exception=False):
                """委托验证给对应的序列化器"""
                if self.polymorphic_serializer:
                    return self.polymorphic_serializer.is_valid(raise_exception)
                return False

            def save(self, **kwargs):
                """委托保存给对应的序列化器"""
                if self.polymorphic_serializer:
                    return self.polymorphic_serializer.save(**kwargs)
                return None

        class ConditionalFieldSerializer(serializers.ModelSerializer):
            """条件字段序列化器"""

            def __init__(self, *args, **kwargs):
                """根据条件动态添加字段"""
                super().__init__(*args, **kwargs)

                context = kwargs.get('context', {})
                request = context.get('request')
                user = getattr(request, 'user', None) if request else None
                instance = kwargs.get('instance')

                # 根据用户角色添加字段
                if user and user.is_authenticated:
                    if user.role == 'editor':
                        # 编辑者可以看到编辑状态
                        self.fields['edit_status'] = serializers.CharField(read_only=True)
                        self.fields['assigned_to'] = serializers.CharField(read_only=True)

                    elif user.role == 'reviewer':
                        # 审核者可以看到审核状态
                        self.fields['review_status'] = serializers.CharField(read_only=True)
                        self.fields['review_notes'] = serializers.CharField(read_only=True)

                # 根据文章状态添加字段
                if instance:
                    if instance.status == 'published':
                        self.fields['seo_score'] = serializers.IntegerField(read_only=True)
                        self.fields['page_views'] = serializers.IntegerField(read_only=True)

                    elif instance.status == 'draft':
                        self.fields['auto_save_enabled'] = serializers.BooleanField(read_only=True)
                        self.fields['last_auto_save'] = serializers.DateTimeField(read_only=True)

                # 根据时间条件添加字段
                if instance and instance.created_at:
                    from django.utils import timezone
                    now = timezone.now()

                    # 最近创建的文章显示创建信息
                    if (now - instance.created_at).days <= 7:
                        self.fields['is_new'] = serializers.BooleanField(read_only=True)
                        self.fields['new_badge'] = serializers.CharField(read_only=True)

                    # 即将发布的文章显示倒计时
                    if instance.scheduled_publish_at and instance.scheduled_publish_at > now:
                        self.fields['publish_countdown'] = serializers.CharField(read_only=True)

            class Meta:
                model = Article
                fields = ['id', 'title', 'status', 'created_at']

        class CachedSerializer(serializers.ModelSerializer):
            """缓存序列化器"""

            def __init__(self, *args, **kwargs):
                """初始化缓存配置"""
                self.cache_enabled = kwargs.pop('cache_enabled', True)
                self.cache_timeout = kwargs.pop('cache_timeout', 300)  # 5分钟

                super().__init__(*args, **kwargs)

            def to_representation(self, instance):
                """带缓存的序列化"""
                if not self.cache_enabled:
                    return super().to_representation(instance)

                # 生成缓存键
                cache_key = self.get_cache_key(instance)

                # 尝试从缓存获取
                from django.core.cache import cache
                cached_data = cache.get(cache_key)

                if cached_data is not None:
                    return cached_data

                # 序列化数据
                data = super().to_representation(instance)

                # 存入缓存
                cache.set(cache_key, data, self.cache_timeout)

                return data

            def get_cache_key(self, instance):
                """生成缓存键"""
                user = self.context.get('request').user if self.context.get('request') else None
                user_id = user.id if user and user.is_authenticated else 'anonymous'

                return f"serializer_{self.__class__.__name__}_{instance.pk}_{user_id}"

        # 视图中使用动态序列化器
        from rest_framework import generics, permissions
        from rest_framework.response import Response

        class DynamicArticleListView(generics.ListAPIView):
            """动态文章列表视图"""
            queryset = Article.objects.filter(status='published')

            def get_serializer_class(self):
                """根据请求参数选择序列化器"""
                request = self.request

                # 检查是否指定了序列化器类型
                serializer_type = request.GET.get('serializer', 'dynamic')

                if serializer_type == 'simple':
                    return SimpleArticleSerializer
                elif serializer_type == 'detailed':
                    return DetailedArticleSerializer
                elif serializer_type == 'admin':
                    if request.user.is_staff:
                        return AdminArticleSerializer
                    else:
                        return DynamicArticleSerializer
                else:
                    return DynamicArticleSerializer

            def get_serializer(self, *args, **kwargs):
                """创建序列化器实例"""
                serializer_class = self.get_serializer_class()

                # 传递fields参数
                fields = self.request.GET.get('fields', '').split(',') if self.request.GET.get('fields') else None
                exclude = self.request.GET.get('exclude', '').split(',') if self.request.GET.get('exclude') else None

                kwargs.setdefault('context', self.get_serializer_context())

                return serializer_class(*args, fields=fields, exclude=exclude, **kwargs)

            def list(self, request, *args, **kwargs):
                """重写列表方法以支持动态响应"""
                queryset = self.filter_queryset(self.get_queryset())

                page = self.paginate_queryset(queryset)
                if page is not None:
                    serializer = self.get_serializer(page, many=True)
                    return self.get_paginated_response(serializer.data)

                serializer = self.get_serializer(queryset, many=True)

                # 根据请求格式返回不同响应
                format_type = request.GET.get('format', 'json')

                if format_type == 'xml':
                    # 转换为XML格式
                    from xml.etree.ElementTree import Element, SubElement, tostring
                    root = Element('articles')

                    for article_data in serializer.data:
                        article_elem = SubElement(root, 'article')
                        for key, value in article_data.items():
                            elem = SubElement(article_elem, key)
                            elem.text = str(value)

                    xml_data = tostring(root, encoding='unicode')
                    return Response(xml_data, content_type='application/xml')

                return Response(serializer.data)

3.4 字段选择优化

01.说明
    fields参数控制
    exclude参数控制
    动态字段排除

02.示例
    # apps/blog/performance_serializers.py
    from rest_framework import serializers
    from django.contrib.auth import get_user_model
    from .models import Article, Category, Tag, Comment

            User = get_user_model()

            class FieldOptimizedSerializer(serializers.ModelSerializer):
                """字段优化的序列化器"""

                def __init__(self, *args, **kwargs):
                    """根据请求参数优化字段选择"""
                    # 获取请求参数
                    request = kwargs.get('context', {}).get('request')
                    if not request:
                        super().__init__(*args, **kwargs)
                        return

                    # 解析字段参数
                    fields_param = request.GET.get('fields', '')
                    exclude_param = request.GET.get('exclude', '')
                    view_type = request.GET.get('view', 'default')

                    super().__init__(*args, **kwargs)

                    # 根据视图类型预设字段
                    view_fields = self.get_view_fields(view_type)
                    if view_fields:
                        self._restrict_fields(view_fields)

                    # 应用include参数
                    if fields_param:
                        include_fields = [f.strip() for f in fields_param.split(',')]
                        self._include_fields(include_fields)

                    # 应用exclude参数
                    if exclude_param:
                        exclude_fields = [f.strip() for f in exclude_param.split(',')]
                        self._exclude_fields(exclude_fields)

                    # 根据用户权限调整字段
                    self._adjust_fields_by_permissions(request.user)

                def get_view_fields(self, view_type):
                    """根据视图类型返回字段列表"""
                    field_mappings = {
                        'list': ['id', 'title', 'slug', 'excerpt', 'author', 'category', 'created_at'],
                        'card': ['id', 'title', 'slug', 'excerpt', 'featured_image', 'view_count'],
                        'thumbnail': ['id', 'title', 'slug', 'featured_image'],
                        'seo': ['id', 'title', 'slug', 'seo_title', 'seo_description', 'meta_robots'],
                        'stats': ['id', 'title', 'view_count', 'like_count', 'comment_count', 'share_count'],
                        'admin': ['id', 'title', 'status', 'author', 'created_at', 'updated_at', 'admin_notes'],
                        'default': None  # 使用所有字段
                    }
                    return field_mappings.get(view_type)

                def _restrict_fields(self, allowed_fields):
                    """限制只使用指定字段"""
                    allowed = set(allowed_fields)
                    existing = set(self.fields.keys())

                    for field_name in existing - allowed:
                        self.fields.pop(field_name)

                def _include_fields(self, include_fields):
                    """只包含指定字段"""
                    allowed = set(include_fields)
                    existing = set(self.fields.keys())

                    for field_name in existing - allowed:
                        self.fields.pop(field_name)

                def _exclude_fields(self, exclude_fields):
                    """排除指定字段"""
                    for field_name in exclude_fields:
                        self.fields.pop(field_name, None)

                def _adjust_fields_by_permissions(self, user):
                    """根据用户权限调整字段"""
                    if not user or not user.is_authenticated:
                        # 匿名用户移除敏感字段
                        sensitive_fields = ['author_email', 'admin_notes', 'internal_status']
                        for field in sensitive_fields:
                            self.fields.pop(field, None)

                    elif not user.is_staff:
                        # 普通用户移除管理字段
                        admin_fields = ['admin_notes', 'internal_status', 'moderation_flags']
                        for field in admin_fields:
                            self.fields.pop(field, None)

                class Meta:
                    model = Article
                    fields = '__all__'

            # 分层序列化器
            class LayeredArticleSerializer(serializers.ModelSerializer):
                """分层序列化器"""

                # 第一层:基础信息
                basic_fields = ['id', 'title', 'slug', 'status', 'created_at']

                # 第二层:内容信息
                content_fields = ['excerpt', 'content', 'featured_image', 'reading_time']

                # 第三层:关系信息
                relation_fields = ['author', 'category', 'tags']

                # 第四层:统计信息
                stats_fields = ['view_count', 'like_count', 'comment_count', 'share_count']

                # 第五层:管理信息
                admin_fields = ['admin_notes', 'internal_status', 'moderation_flags']

                def __init__(self, *args, **kwargs):
                    """根据请求层级选择字段"""
                    layer = kwargs.pop('layer', 1)  # 默认第一层
                    super().__init__(*args, **kwargs)

                    # 根据层级包含字段
                    included_fields = []
                    if layer >= 1:
                        included_fields.extend(self.basic_fields)
                    if layer >= 2:
                        included_fields.extend(self.content_fields)
                    if layer >= 3:
                        included_fields.extend(self.relation_fields)
                    if layer >= 4:
                        included_fields.extend(self.stats_fields)
                    if layer >= 5:
                        included_fields.extend(self.admin_fields)

                    # 限制字段
                    self._restrict_fields(included_fields)

                class Meta:
                    model = Article
                    fields = '__all__'

            # 条件字段序列化器
            class ConditionalFieldSerializer(serializers.ModelSerializer):
                """条件字段序列化器"""

                def __init__(self, *args, **kwargs):
                    """根据条件动态包含字段"""
                    super().__init__(*args, **kwargs)

                    context = kwargs.get('context', {})
                    request = context.get('request')
                    user = getattr(request, 'user', None) if request else None
                    instance = kwargs.get('instance')

                    # 用户相关条件
                    if user:
                        if user.is_staff:
                            # 管理员字段
                            self.fields['admin_notes'] = serializers.CharField(read_only=True)
                            self.fields['internal_status'] = serializers.CharField(read_only=True)

                        if user == instance.author if instance else False:
                            # 作者字段
                            self.fields['draft_content'] = serializers.CharField(read_only=True)
                            self.fields['edit_history'] = serializers.JSONField(read_only=True)

                    # 内容相关条件
                    if instance:
                        if instance.status == 'published':
                            self.fields['seo_score'] = serializers.IntegerField(read_only=True)
                            self.fields['search_ranking'] = serializers.IntegerField(read_only=True)

                        if instance.is_featured:
                            self.fields['featured_reason'] = serializers.CharField(read_only=True)
                            self.fields['featured_at'] = serializers.DateTimeField(read_only=True)

                    # 时间相关条件
                    if instance and instance.created_at:
                        from django.utils import timezone
                        now = timezone.now()

                        # 最近发布的文章
                        if (now - instance.created_at).days <= 7:
                            self.fields['is_new'] = serializers.BooleanField(read_only=True)
                            self.fields['trending_score'] = serializers.FloatField(read_only=True)

                        # 即将过期的内容
                        if instance.expires_at and (instance.expires_at - now).days <= 30:
                            self.fields['expires_soon'] = serializers.BooleanField(read_only=True)
                            self.fields['expiry_warning'] = serializers.CharField(read_only=True)

                class Meta:
                    model = Article
                    fields = ['id', 'title', 'status', 'created_at']
            ```

        b.预加载关联数据
            select_related优化
            prefetch_related优化
            查询集优化
            预加载优化示例:
            ```python
            # apps/blog/optimized_serializers.py
            from rest_framework import serializers
            from django.contrib.auth import get_user_model
    from django.db.models import Prefetch, Count, Avg, Q, F
    from .models import Article, Category, Tag, Comment, ArticleView

            User = get_user_model()

            class QueryOptimizedArticleSerializer(serializers.ModelSerializer):
                """查询优化的文章序列化器"""

                # 基础字段,避免复杂嵌套
                author_username = serializers.CharField(source='author.username', read_only=True)
                author_first_name = serializers.CharField(source='author.first_name', read_only=True)
                author_last_name = serializers.CharField(source='author.last_name', read_only=True)

                category_name = serializers.CharField(source='category.name', read_only=True)
                category_slug = serializers.CharField(source='category.slug', read_only=True)

                tag_names = serializers.ListField(
                    child=serializers.CharField(),
                    source='tags.values_list',
                    read_only=True
                )

                # 统计字段
                comment_count = serializers.SerializerMethodField()
                like_count = serializers.SerializerMethodField()
                view_count = serializers.SerializerMethodField()

                class Meta:
                    model = Article
                    fields = [
                        'id', 'title', 'slug', 'excerpt', 'status',
                        'author_username', 'author_first_name', 'author_last_name',
                        'category_name', 'category_slug', 'tag_names',
                        'featured_image', 'is_featured', 'is_top',
                        'comment_count', 'like_count', 'view_count',
                        'created_at', 'updated_at', 'published_at'
                    ]

                def get_comment_count(self, obj):
                    """获取评论数量(使用预加载的数据)"""
                    return getattr(obj, 'comment_count_value', 0)

                def get_like_count(self, obj):
                    """获取点赞数量"""
                    return getattr(obj, 'like_count_value', 0)

                def get_view_count(self, obj):
                    """获取浏览数量"""
                    return getattr(obj, 'view_count_value', 0)

            class ArticleListOptimizedSerializer(serializers.ModelSerializer):
                """文章列表优化序列化器"""

                # 最小化嵌套,使用标量字段
                author_name = serializers.CharField(source='author.get_full_name', read_only=True)
                author_avatar = serializers.SerializerMethodField()

                category_name = serializers.CharField(source='category.name', read_only=True)

                # 使用ID列表代替嵌套对象
                tag_ids = serializers.ListField(
                    child=serializers.IntegerField(),
                    source='tags.values_list',
                    read_only=True
                )

                # 缓存计算字段
                display_time = serializers.SerializerMethodField()
                engagement_score = serializers.SerializerMethodField()

                class Meta:
                    model = Article
                    fields = [
                        'id', 'title', 'slug', 'excerpt',
                        'author_name', 'author_avatar',
                        'category_name', 'tag_ids',
                        'featured_image', 'view_count', 'like_count',
                        'is_featured', 'display_time', 'engagement_score',
                        'created_at'
                    ]

                def get_author_avatar(self, obj):
                    """获取作者头像(使用预加载)"""
                    profile = getattr(obj, 'author_profile', None)
                    if profile and profile.avatar:
                        request = self.context.get('request')
                        if request:
                            return request.build_absolute_uri(profile.avatar.url)
                    return ''

                def get_display_time(self, obj):
                    """获取显示时间"""
                    from django.utils import timezone
                    now = timezone.now()
                    diff = now - obj.created_at

                    if diff.days == 0:
                        if diff.seconds < 3600:
                            return f"{diff.seconds // 60}分钟前"
                        else:
                            return f"{diff.seconds // 3600}小时前"
                    elif diff.days < 7:
                        return f"{diff.days}天前"
                    else:
                        return obj.created_at.strftime('%Y-%m-%d')

                def get_engagement_score(self, obj):
                    """计算参与度分数"""
                    # 使用预加载的统计数据进行计算
                    view_count = getattr(obj, 'view_count_value', obj.view_count)
                    like_count = getattr(obj, 'like_count_value', obj.like_count)
                    comment_count = getattr(obj, 'comment_count_value', 0)

                    return view_count + (like_count * 10) + (comment_count * 20)

            # 视图中的查询优化
            from rest_framework import generics
            from rest_framework.permissions import IsAuthenticatedOrReadOnly
            from django.db.models import Prefetch, Count, Avg

            class OptimizedArticleListView(generics.ListAPIView):
                """优化的文章列表视图"""
                serializer_class = ArticleListOptimizedSerializer
                permission_classes = [IsAuthenticatedOrReadOnly]

                def get_queryset(self):
                    """获取优化的查询集"""
                    queryset = Article.objects.filter(status='published')

                    # select_related 优化外键关系
                    queryset = queryset.select_related(
                        'author',
                        'category',
                        'author__profile'  # 预加载作者资料
                    )

                    # prefetch_related 优化多对多关系
                    queryset = queryset.prefetch_related(
                        Prefetch('tags', queryset=Tag.objects.only('id', 'name'))
                    )

                    # 预加载统计数据
                    queryset = queryset.annotate(
                        comment_count_value=Count('comments', distinct=True),
                        view_count_value=Count('articleview', distinct=True),
                        like_count_value=Count('likes', distinct=True)
                    )

                    # 性能优化:只选择需要的字段
                    queryset = queryset.only(
                        'id', 'title', 'slug', 'excerpt', 'featured_image',
                        'view_count', 'like_count', 'is_featured', 'is_top',
                        'created_at', 'updated_at', 'published_at',
                        'author__id', 'author__first_name', 'author__last_name',
                        'category__id', 'category__name'
                    )

                    return queryset.order_by('-created_at')

            class OptimizedArticleDetailView(generics.RetrieveAPIView):
                """优化的文章详情视图"""
                serializer_class = QueryOptimizedArticleSerializer
                lookup_field = 'slug'

                def get_queryset(self):
                    """获取优化的查询集"""
                    queryset = Article.objects.filter(status='published')

                    # 预加载所有需要的关联数据
                    queryset = queryset.select_related(
                        'author__profile',
                        'category'
                    ).prefetch_related(
                        'tags',
                        Prefetch(
                            'comments_set',
                            queryset=Comment.objects.filter(
                                is_approved=True
                            ).select_related('author__profile').order_by('created_at'),
                            to_attr='approved_comments'
                        ),
                        Prefetch(
                            'articleview_set',
                            queryset=ArticleView.objects.all().order_by('-created_at')[:10],
                            to_attr='recent_views'
                        )
                    )

                    # 预加载统计信息
                    queryset = queryset.annotate(
                        comment_count_value=Count('comments', distinct=True),
                        like_count_value=Count('likes', distinct=True),
                        view_count_value=Count('articleview', distinct=True),
                        avg_rating=Avg('reviews__rating', distinct=True)
                    )

                    return queryset

                def retrieve(self, request, *args, **kwargs):
                    """重写retrieve方法,记录浏览"""
                    instance = self.get_object()

                    # 异步记录浏览,不阻塞响应
                    from .models import ArticleView
                    from django.utils import timezone

                    # 创建浏览记录(使用批量插入优化)
                    user_agent = request.META.get('HTTP_USER_AGENT', '')
                    ip_address = self.get_client_ip(request)

                    ArticleView.objects.get_or_create(
                        article=instance,
                        ip_address=ip_address,
                        defaults={
                            'user_agent': user_agent,
                            'viewed_at': timezone.now(),
                            'user': request.user if request.user.is_authenticated else None
                        }
                    )

                    serializer = self.get_serializer(instance)
                    return Response(serializer.data)

                def get_client_ip(self, request):
                    """获取客户端IP"""
                    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
                    if x_forwarded_for:
                        ip = x_forwarded_for.split(',')[0]
                    else:
                        ip = request.META.get('REMOTE_ADDR')
                    return ip

            class BulkOptimizedCategoryView(generics.ListAPIView):
                """批量优化的分类视图"""

                def get_queryset(self):
                    """获取分类及其统计信息"""
                    from django.db.models import Subquery, OuterRef

                    # 预加载每个分类的文章数量
                    latest_article_subquery = Article.objects.filter(
                        category=OuterRef('pk'),
                        status='published'
                    ).order_by('-published_at').values('published_at')[:1]

                    queryset = Category.objects.filter(is_active=True).annotate(
                        article_count=Count(
                            'articles',
                            filter=Q(articles__status='published')
                        ),
                        total_views=Count(
                            'articles__articleview',
                            filter=Q(articles__status='published')
                        ),
                        latest_article_date=Subquery(latest_article_subquery)
                    ).prefetch_related(
                        Prefetch(
                            'articles',
                            queryset=Article.objects.filter(
                                status='published',
                                is_featured=True
                            ).select_related('author').only(
                                'id', 'title', 'slug', 'featured_image',
                                'author__first_name', 'author__last_name'
                            )[:5],
                            to_attr='featured_articles'
                        )
                    )

                    return queryset.order_by('name')
            ```

        c.批量序列化
            批量处理优化
            缓存机制
            异步处理
            批量序列化示例:
            ```python
            # apps/blog/bulk_serializers.py
            from rest_framework import serializers
            from django.contrib.auth import get_user_model
    from django.core.cache import cache
    from django.db import transaction
    from .models import Article, Category, Tag, Comment

    User = get_user_model()

    class BulkArticleSerializer(serializers.ModelSerializer):
        """批量文章序列化器"""

        def __init__(self, *args, **kwargs):
            """批量处理优化"""
            self.bulk_mode = kwargs.pop('bulk', False)
            self.cache_enabled = kwargs.pop('cache', True)
            self.cache_timeout = kwargs.pop('cache_timeout', 300)

            super().__init__(*args, **kwargs)

        def to_representation(self, instance):
            """优化的批量序列化"""
            if self.bulk_mode:
                return self._bulk_to_representation(instance)
            else:
                return self._cached_to_representation(instance)

        def _bulk_to_representation(self, instance):
            """批量序列化(无缓存)"""
            # 直接使用最简单的字段提取
            return {
                'id': instance.id,
                'title': instance.title,
                'slug': instance.slug,
                'excerpt': instance.excerpt or '',
                'status': instance.status,
                'created_at': instance.created_at.isoformat() if instance.created_at else None,
                'view_count': getattr(instance, 'view_count_value', instance.view_count),
            }

        def _cached_to_representation(self, instance):
            """带缓存的序列化"""
            if not self.cache_enabled:
                return super().to_representation(instance)

            # 生成缓存键
            cache_key = f"article_{instance.pk}_{self._get_user_context_hash()}"

            # 尝试从缓存获取
            cached_data = cache.get(cache_key)
            if cached_data is not None:
                return cached_data

            # 序列化数据
            data = super().to_representation(instance)

            # 存入缓存
            cache.set(cache_key, data, self.cache_timeout)

            return data

        def _get_user_context_hash(self):
            """获取用户上下文哈希"""
            request = self.context.get('request')
            if not request:
                return 'anonymous'

            user = request.user
            if user.is_authenticated:
                return f"user_{user.id}_{'staff' if user.is_staff else 'normal'}"
            else:
                return 'anonymous'

        class Meta:
            model = Article
            fields = ['id', 'title', 'slug', 'excerpt', 'status', 'created_at']

    class BatchUpdateSerializer(serializers.Serializer):
        """批量更新序列化器"""

        articles = serializers.ListField(
            child=serializers.DictField(),
            write_only=True
        )

        # 批量操作选项
        update_fields = serializers.ListField(
            child=serializers.CharField(),
            required=False,
            help_text="指定要更新的字段"
        )

        validate_only = serializers.BooleanField(
            default=False,
            help_text="仅验证不实际更新"
        )

        def validate_articles(self, value):
            """验证批量文章数据"""
            if not value:
                raise serializers.ValidationError("文章列表不能为空")

            if len(value) > 100:  # 限制批量大小
                raise serializers.ValidationError("单次最多批量处理100篇文章")

            # 验证每篇文章的必需字段
            required_fields = ['id']
            optional_fields = ['title', 'content', 'excerpt', 'status', 'category', 'tags']

            for i, article_data in enumerate(value):
                # 检查必需字段
                for field in required_fields:
                    if field not in article_data:
                        raise serializers.ValidationError(
                            f"第{i+1}篇文章缺少必需字段:{field}"
                        )

                # 检查字段有效性
                for field in article_data.keys():
                    if field not in required_fields + optional_fields:
                        raise serializers.ValidationError(
                            f"第{i+1}篇文章包含无效字段:{field}"
                        )

            return value

        def validate(self, attrs):
            """整体验证"""
            articles = attrs.get('articles', [])
            update_fields = attrs.get('update_fields', [])

            # 检查是否所有文章都存在
            article_ids = [article['id'] for article in articles]
            existing_articles = Article.objects.filter(id__in=article_ids)
            existing_ids = set(existing_articles.values_list('id', flat=True))

            missing_ids = set(article_ids) - existing_ids
            if missing_ids:
                raise serializers.ValidationError(
                    f"以下文章ID不存在:{', '.join(map(str, missing_ids))}"
                )

            # 权限检查
            request = self.context.get('request')
            user = getattr(request, 'user', None) if request else None

            if user:
                user_articles = existing_articles.filter(author=user)
                if user_articles.count() != len(existing_articles):
                    if not user.is_staff:
                        raise serializers.ValidationError("您只能更新自己的文章")

            # 检查更新字段的有效性
            valid_fields = ['title', 'content', 'excerpt', 'status', 'category', 'tags']
            invalid_fields = set(update_fields) - set(valid_fields)
            if invalid_fields:
                raise serializers.ValidationError(
                    f"无效的更新字段:{', '.join(invalid_fields)}"
                )

            return attrs

        def create(self, validated_data):
            """执行批量更新"""
            articles_data = validated_data.get('articles', [])
            update_fields = validated_data.get('update_fields', [])
            validate_only = validated_data.get('validate_only', False)

            if validate_only:
                # 仅验证,不实际更新
                return {'success': True, 'message': '验证通过,未执行更新'}

            # 获取文章映射
            article_ids = [article['id'] for article in articles_data]
            articles_map = {
                article.id: article
                for article in Article.objects.filter(id__in=article_ids)
            }

            results = []
            updated_count = 0

            with transaction.atomic():
                for article_data in articles_data:
                    article_id = article_data['id']
                    article = articles_map.get(article_id)

                    if not article:
                        results.append({
                            'id': article_id,
                            'success': False,
                            'error': '文章不存在'
                        })
                        continue

                    try:
                        # 更新字段
                        for field, value in article_data.items():
                            if field == 'id':
                                continue

                            if not update_fields or field in update_fields:
                                setattr(article, field, value)

                        article.save()

                        results.append({
                            'id': article_id,
                            'success': True,
                            'message': '更新成功'
                        })
                        updated_count += 1

                    except Exception as e:
                        results.append({
                            'id': article_id,
                            'success': False,
                            'error': str(e)
                        })

            return {
                'success': True,
                'updated_count': updated_count,
                'total_count': len(articles_data),
                'results': results
            }

    class AsyncBulkSerializer(serializers.ModelSerializer):
        """异步批量处理序列化器"""

        def __init__(self, *args, **kwargs):
            """初始化异步处理"""
            self.async_mode = kwargs.pop('async', False)
            self.batch_size = kwargs.pop('batch_size', 50)

            super().__init__(*args, **kwargs)

        def save(self, **kwargs):
            """保存实例(支持异步)"""
            if self.async_mode and len(self.instances) > self.batch_size:
                return self._async_save(**kwargs)
            else:
                return super().save(**kwargs)

        def _async_save(self, **kwargs):
            """异步保存"""
            from .tasks import bulk_create_articles_task

            # 将任务加入队列
            task = bulk_create_articles_task.delay(
                self.validated_data,
                **kwargs
            )

            return {
                'task_id': task.id,
                'message': '批量创建任务已提交',
                'status': 'processing'
            }

        class Meta:
            model = Article
            fields = '__all__'

    # 批量视图
    from rest_framework import generics, permissions, status
    from rest_framework.response import Response
    from rest_framework.decorators import action
    from django.utils.decorators import method_decorator
    from django.views.decorators.cache import cache_page

    class BulkArticleView(generics.GenericAPIView):
        """批量文章操作视图"""
        permission_classes = [permissions.IsAuthenticated]
        serializer_class = BatchUpdateSerializer

        @method_decorator(cache_page(60))  # 缓存1分钟
        def get(self, request):
            """获取批量操作状态"""
            # 这里可以实现获取批量操作历史状态
            return Response({
                'success': True,
                'message': '批量操作API正常',
                'endpoints': {
                    'bulk_update': 'PUT /api/articles/bulk/',
                    'bulk_create': 'POST /api/articles/bulk/',
                    'bulk_delete': 'DELETE /api/articles/bulk/'
                }
            })

        def put(self, request):
            """批量更新文章"""
            serializer = self.get_serializer(data=request.data)

            if serializer.is_valid():
                result = serializer.save()
                return Response(result, status=status.HTTP_200_OK)
            else:
                return Response({
                    'success': False,
                    'errors': serializer.errors
                }, status=status.HTTP_400_BAD_REQUEST)

        def post(self, request):
            """批量创建文章"""
            articles_data = request.data.get('articles', [])

            if not articles_data:
                return Response({
                    'success': False,
                    'message': '文章数据不能为空'
                }, status=status.HTTP_400_BAD_REQUEST)

            # 检查批量大小
            if len(articles_data) > 100:
                return Response({
                    'success': False,
                    'message': '单次最多批量创建100篇文章'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                created_articles = []

                with transaction.atomic():
                    for article_data in articles_data:
                        # 设置作者
                        article_data['author'] = request.user.id

                        serializer = ArticleSerializer(data=article_data)
                        if serializer.is_valid():
                            article = serializer.save()
                            created_articles.append({
                                'id': article.id,
                                'title': article.title,
                                'slug': article.slug
                            })
                        else:
                            return Response({
                                'success': False,
                                'message': '文章数据验证失败',
                                'errors': serializer.errors,
                                'article_data': article_data
                            }, status=status.HTTP_400_BAD_REQUEST)

                return Response({
                    'success': True,
                    'message': f'成功创建{len(created_articles)}篇文章',
                    'created_count': len(created_articles),
                    'articles': created_articles
                }, status=status.HTTP_201_CREATED)

            except Exception as e:
                return Response({
                    'success': False,
                    'message': f'批量创建失败:{str(e)}'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        def delete(self, request):
            """批量删除文章"""
            article_ids = request.data.get('article_ids', [])

            if not article_ids:
                return Response({
                    'success': False,
                    'message': '文章ID列表不能为空'
                }, status=status.HTTP_400_BAD_REQUEST)

            # 权限检查
    queryset = Article.objects.filter(id__in=article_ids)

    if not request.user.is_staff:
        queryset = queryset.filter(author=request.user)

    if queryset.count() != len(set(article_ids)):
        missing_ids = set(article_ids) - set(queryset.values_list('id', flat=True))
        return Response({
            'success': False,
            'message': f'以下文章不存在或无权限删除:{", ".join(map(str, missing_ids))}'
        }, status=status.HTTP_400_BAD_REQUEST)

    # 执行删除
    deleted_count, _ = queryset.delete()

    return Response({
        'success': True,
        'message': f'成功删除{deleted_count}篇文章',
        'deleted_count': deleted_count
    }, status=status.HTTP_200_OK)

    # 性能监控装饰器
    import time
    import logging
    from functools import wraps

    def monitor_performance(func):
        """性能监控装饰器"""
        @wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()

            try:
                result = func(*args, **kwargs)

                execution_time = time.time() - start_time

                # 记录性能日志
                logger = logging.getLogger('serializer_performance')
                logger.info(
                    f"{func.__name__} 执行时间: {execution_time:.3f}秒"
                )

                # 如果执行时间过长,记录警告
                if execution_time > 1.0:
                    logger.warning(
                        f"{func.__name__} 执行时间过长: {execution_time:.3f}秒"
                    )

                return result

            except Exception as e:
                execution_time = time.time() - start_time

                logger = logging.getLogger('serializer_performance')
                logger.error(
                    f"{func.__name__} 执行失败,耗时: {execution_time:.3f}秒,错误: {str(e)}"
                )

                raise

        return wrapper

    # 应用性能监控
    @monitor_performance
    class MonitoredBulkSerializer(BulkArticleSerializer):
            """带性能监控的批量序列化器"""
            pass

4 视图与路由

4.1 APIView基础

01.请求处理流程
    a.说明
        Request对象解析
        响应格式化
        内容协商机制
    b.示例
        # apps/core/views.py
        from rest_framework.views import APIView
        from rest_framework.response import Response
        from rest_framework import status
        from django.http import Http404
        from django.contrib.auth import get_user_model
        from django.utils import timezone
        from django.core.paginator import Paginator
        import logging

        User = get_user_model()
        logger = logging.getLogger('api')

        class BaseAPIView(APIView):
            """基础API视图类"""

            def initial(self, request, *args, **kwargs):
                """请求初始化处理"""
                super().initial(request, *args, **kwargs)

                # 记录请求信息
                logger.info(
                    f"API Request: {request.method} {request.path} "
                    f"from {self.get_client_ip(request)} "
                    f"by {getattr(request.user, 'username', 'Anonymous')}"
                )

                # 设置响应头
                self.response_headers = {
                    'X-API-Version': '1.0',
                    'X-Timestamp': timezone.now().isoformat(),
                    'Cache-Control': 'no-cache, no-store, must-revalidate',
                }

                # 检查API版本
                self.check_api_version(request)

                # 限流检查
                self.check_rate_limit(request)

            def finalize_response(self, request, response, *args, **kwargs):
                """响应最终处理"""
                # 添加自定义响应头
                for key, value in getattr(self, 'response_headers', {}).items():
                    response[key] = value

                # 记录响应信息
                logger.info(
                    f"API Response: {response.status_code} "
                    f"for {request.method} {request.path} "
                    f"by {getattr(request.user, 'username', 'Anonymous')}"
                )

                return super().finalize_response(request, response, *args, **kwargs)

            def get_client_ip(self, request):
                """获取客户端IP地址"""
                x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
                if x_forwarded_for:
                    ip = x_forwarded_for.split(',')[0]
                else:
                    ip = request.META.get('REMOTE_ADDR')
                return ip

            def check_api_version(self, request):
                """检查API版本"""
                version = request.META.get('HTTP_API_VERSION', '1.0')
                supported_versions = ['1.0', '1.1', '2.0']

                if version not in supported_versions:
                    response = Response({
                        'success': False,
                        'message': f'不支持的API版本: {version}',
                        'supported_versions': supported_versions
                    }, status=status.HTTP_400_BAD_REQUEST)
                    raise response

            def check_rate_limit(self, request):
                """检查API调用频率限制"""
                from django.core.cache import cache
                from django.utils import timezone
                import time

                client_ip = self.get_client_ip(request)
                cache_key = f"rate_limit_{client_ip}"

                # 获取当前计数
                count = cache.get(cache_key, 0)
                current_time = time.time()

                # 限制每分钟最多100次请求
                if count >= 100:
                    logger.warning(f"Rate limit exceeded for IP: {client_ip}")
                    response = Response({
                        'success': False,
                        'message': 'API调用频率超限,请稍后再试',
                        'retry_after': 60
                    }, status=status.HTTP_429_TOO_MANY_REQUESTS)
                    raise response

                # 增加计数
                cache.set(cache_key, count + 1, 60)

            def handle_exception(self, exc):
                """全局异常处理"""
                response = super().handle_exception(exc)

                # 记录异常信息
                logger.error(
                    f"API Exception: {exc.__class__.__name__}: {str(exc)}",
                    exc_info=True
                )

                # 自定义错误响应格式
                if hasattr(response, 'data') and isinstance(response.data, dict):
                    if 'detail' in response.data:
                        response.data = {
                            'success': False,
                            'message': response.data['detail'],
                            'error_code': getattr(exc, 'default_code', 'UNKNOWN_ERROR'),
                            'timestamp': timezone.now().isoformat(),
                        }

                return response

            def success_response(self, data=None, message='操作成功', status_code=status.HTTP_200_OK):
                """成功响应"""
                response_data = {
                    'success': True,
                    'message': message,
                    'timestamp': timezone.now().isoformat(),
                }

                if data is not None:
                    response_data['data'] = data

                return Response(response_data, status=status_code)

            def error_response(self, message='操作失败', error_code=None, status_code=status.HTTP_400_BAD_REQUEST):
                """错误响应"""
                response_data = {
                    'success': False,
                    'message': message,
                    'timestamp': timezone.now().isoformat(),
                }

                if error_code:
                    response_data['error_code'] = error_code

                return Response(response_data, status=status_code)

        class HealthCheckView(BaseAPIView):
            """健康检查视图"""

            def get(self, request):
                """系统健康检查"""
                from django.db import connection
                from django.core.cache import cache
                import redis

                health_status = {
                    'status': 'healthy',
                    'timestamp': timezone.now().isoformat(),
                    'services': {}
                }

                try:
                    # 数据库连接检查
                    with connection.cursor() as cursor:
                        cursor.execute("SELECT 1")
                        health_status['services']['database'] = 'healthy'
                except Exception as e:
                    health_status['services']['database'] = f'unhealthy: {str(e)}'
                    health_status['status'] = 'unhealthy'

                try:
                    # 缓存连接检查
                    cache.set('health_check', 'ok', 1)
                    cache_result = cache.get('health_check')
                    if cache_result == 'ok':
                        health_status['services']['cache'] = 'healthy'
                    else:
                        health_status['services']['cache'] = 'unhealthy: cache read/write failed'
                except Exception as e:
                    health_status['services']['cache'] = f'unhealthy: {str(e)}'

                try:
                    # Redis连接检查(如果配置了)
                    from django.conf import settings
                    if hasattr(settings, 'REDIS_URL'):
                        redis_client = redis.from_url(settings.REDIS_URL)
                        redis_client.ping()
                        health_status['services']['redis'] = 'healthy'
                except Exception as e:
                    health_status['services']['redis'] = f'unhealthy: {str(e)}'

                # 系统资源检查
                import psutil
                health_status['system'] = {
                    'cpu_percent': psutil.cpu_percent(interval=1),
                    'memory_percent': psutil.virtual_memory().percent,
                    'disk_usage': psutil.disk_usage('/').percent
                }

                # 根据服务状态确定HTTP状态码
                if health_status['status'] == 'healthy':
                    return self.success_response(health_status, '系统正常运行')
                else:
                    return self.error_response(
                        '系统存在异常',
                        'SYSTEM_UNHEALTHY',
                        status.HTTP_503_SERVICE_UNAVAILABLE
                    )

        class UserInfoView(BaseAPIView):
            """用户信息视图"""

            def get(self, request):
                """获取当前用户信息"""
                if not request.user.is_authenticated:
                    return self.error_response(
                        '用户未认证',
                        'AUTHENTICATION_REQUIRED',
                        status.HTTP_401_UNAUTHORIZED
                    )

                user = request.user
                user_data = {
                    'id': user.id,
                    'username': user.username,
                    'email': user.email,
                    'first_name': user.first_name,
                    'last_name': user.last_name,
                    'full_name': user.get_full_name(),
                    'is_staff': user.is_staff,
                    'is_active': user.is_active,
                    'date_joined': user.date_joined.isoformat() if user.date_joined else None,
                    'last_login': user.last_login.isoformat() if user.last_login else None,
                }

                # 添加用户资料信息
                if hasattr(user, 'profile'):
                    profile = user.profile
                    user_data['profile'] = {
                        'avatar': profile.avatar.url if profile.avatar else None,
                        'bio': profile.bio,
                        'phone': profile.phone,
                        'birth_date': profile.birth_date.isoformat() if profile.birth_date else None,
                    }

                return self.success_response(user_data, '获取用户信息成功')

            def put(self, request):
                """更新用户信息"""
                if not request.user.is_authenticated:
                    return self.error_response(
                        '用户未认证',
                        'AUTHENTICATION_REQUIRED',
                        status.HTTP_401_UNAUTHORIZED
                    )

                user = request.user
                data = request.data

                # 更新用户基本信息
                if 'first_name' in data:
                    user.first_name = data['first_name']
                if 'last_name' in data:
                    user.last_name = data['last_name']

                # 更新邮箱(需要验证)
                if 'email' in data and data['email'] != user.email:
                    if User.objects.filter(email=data['email']).exists():
                        return self.error_response(
                            '邮箱已被使用',
                            'EMAIL_ALREADY_EXISTS',
                            status.HTTP_400_BAD_REQUEST
                        )
                    user.email = data['email']

                user.save()

                # 更新用户资料
                if hasattr(user, 'profile'):
                    profile = user.profile
                    if 'bio' in data:
                        profile.bio = data['bio']
                    if 'phone' in data:
                        profile.phone = data['phone']
                    if 'birth_date' in data:
                        from datetime import datetime
                        try:
                            profile.birth_date = datetime.strptime(data['birth_date'], '%Y-%m-%d').date()
                        except ValueError:
                            return self.error_response(
                                '生日格式错误,应为YYYY-MM-DD',
                                'INVALID_DATE_FORMAT',
                                status.HTTP_400_BAD_REQUEST
                            )

                    profile.save()

                return self.success_response({'message': '用户信息更新成功'})

        class StatisticsView(BaseAPIView):
        """统计信息视图"""

        def get(self, request):
            """获取系统统计信息"""
            from django.db.models import Count, Avg, Sum
            from apps.blog.models import Article, Comment

            try:
                # 基础统计
                stats = {
                    'users': {
                        'total': User.objects.count(),
                        'active': User.objects.filter(is_active=True).count(),
                        'staff': User.objects.filter(is_staff=True).count(),
                    },
                    'articles': {
                        'total': Article.objects.count(),
                        'published': Article.objects.filter(status='published').count(),
                        'draft': Article.objects.filter(status='draft').count(),
                        'featured': Article.objects.filter(is_featured=True).count(),
                    },
                    'comments': {
                        'total': Comment.objects.count(),
                        'approved': Comment.objects.filter(is_approved=True).count(),
                        'pending': Comment.objects.filter(is_approved=False).count(),
                    },
                    'engagement': {
                        'avg_views_per_article': Article.objects.aggregate(
                            avg_views=Avg('view_count')
                        )['avg_views'] or 0,
                        'total_views': Article.objects.aggregate(
                            total_views=Sum('view_count')
                        )['total_views'] or 0,
                        'total_likes': Article.objects.aggregate(
                            total_likes=Sum('like_count')
                        )['total_likes'] or 0,
                    }
                }

                # 时间序列统计(最近7天)
                from django.utils import timezone
                from datetime import timedelta

                end_date = timezone.now()
                start_date = end_date - timedelta(days=7)

                daily_stats = []
                for i in range(7):
                    date = start_date + timedelta(days=i)
                    date_str = date.strftime('%Y-%m-%d')

                    daily_articles = Article.objects.filter(
                        created_at__date=date.date()
                    ).count()

                    daily_comments = Comment.objects.filter(
                        created_at__date=date.date()
                    ).count()

                    daily_stats.append({
                        'date': date_str,
                        'articles': daily_articles,
                        'comments': daily_comments,
                    })

                stats['daily_stats'] = daily_stats

                return self.success_response(stats, '获取统计信息成功')

            except Exception as e:
                logger.error(f"获取统计信息失败: {str(e)}", exc_info=True)
                return self.error_response(
                    '获取统计信息失败',
                    'STATISTICS_ERROR',
                    status.HTTP_500_INTERNAL_SERVER_ERROR
                )

02.响应格式化
    a.说明
        JSON响应处理
        错误响应结构
        自定义响应头
    b.示例
        # apps/core/response_formatters.py
        from rest_framework.response import Response
        from rest_framework import status
        from django.utils import timezone
        import json

        class APIResponse:
            """统一的API响应格式"""

            @staticmethod
            def success(data=None, message='操作成功', code=200, metadata=None):
                """成功响应"""
                response_data = {
                    'success': True,
                    'code': code,
                    'message': message,
                    'timestamp': timezone.now().isoformat(),
                }

                if data is not None:
                    response_data['data'] = data

                if metadata:
                    response_data['metadata'] = metadata

                return Response(response_data, status=code)

            @staticmethod
            def error(message='操作失败', code=400, error_details=None, error_code=None):
                """错误响应"""
                response_data = {
                    'success': False,
                    'code': code,
                    'message': message,
                    'timestamp': timezone.now().isoformat(),
                }

                if error_code:
                    response_data['error_code'] = error_code

                if error_details:
                    response_data['error_details'] = error_details

                return Response(response_data, status=code)

            @staticmethod
            def paginated(data, paginator, message='获取数据成功'):
                """分页响应"""
                response_data = {
                    'success': True,
                    'code': 200,
                    'message': message,
                    'timestamp': timezone.now().isoformat(),
                    'data': data,
                    'pagination': {
                        'current_page': paginator.page.number,
                        'total_pages': paginator.page.paginator.num_pages,
                        'per_page': paginator.page.paginator.per_page,
                        'total_items': paginator.page.paginator.count,
                        'has_next': paginator.page.has_next(),
                        'has_previous': paginator.page.has_previous(),
                    }
                }

                return Response(response_data, status=status.HTTP_200_OK)

            @staticmethod
            def created(data, message='创建成功', location=None):
                """创建成功响应"""
                response = APIResponse.success(data, message, status.HTTP_201_CREATED)

                if location:
                    response['Location'] = location

                return response

            @staticmethod
            def no_content(message='删除成功'):
                """无内容响应"""
                return APIResponse.success(message=message, code=status.HTTP_204_NO_CONTENT)

        class XMLResponse(Response):
            """XML响应格式"""

            def __init__(self, data, status=None, template_name=None, headers=None, content_type='application/xml'):
                """初始化XML响应"""
                from xml.etree.ElementTree import Element, SubElement, tostring

                # 转换数据为XML
                root = Element('response')
                root.set('success', str(data.get('success', 'true')))
                root.set('timestamp', data.get('timestamp', timezone.now().isoformat()))

                # 添加消息
                if 'message' in data:
                    msg_elem = SubElement(root, 'message')
                    msg_elem.text = data['message']

                # 添加数据
                if 'data' in data:
                    data_elem = SubElement(root, 'data')
                    self._dict_to_xml(data['data'], data_elem)

                xml_data = tostring(root, encoding='unicode')

                super().__init__(xml_data, status, template_name, headers, content_type)

            def _dict_to_xml(self, data, parent):
                """字典转XML"""
                if isinstance(data, dict):
                    for key, value in data.items():
                        child = SubElement(parent, key)
                        if isinstance(value, (dict, list)):
                            self._dict_to_xml(value, child)
                        else:
                            child.text = str(value)
                elif isinstance(data, list):
                    for item in data:
                        child = SubElement(parent, 'item')
                        if isinstance(item, (dict, list)):
                            self._dict_to_xml(item, child)
                        else:
                            child.text = str(item)

        class CSVResponse(Response):
            """CSV响应格式"""

            def __init__(self, data, filename='data.csv', headers=None):
                """初始化CSV响应"""
                import csv
                from io import StringIO

                output = StringIO()
                writer = csv.writer(output)

                # 如果数据是列表且不为空,写入表头
                if isinstance(data, list) and data:
                    if isinstance(data[0], dict):
                        writer.writerow(data[0].keys())
                        for row in data:
                            writer.writerow(row.values())
                    else:
                        writer.writerow(['data'])
                        for row in data:
                            writer.writerow([row])

                csv_content = output.getvalue()
                output.close()

                response_headers = {
                    'Content-Type': 'text/csv',
                    'Content-Disposition': f'attachment; filename="{filename}"',
                }

                if headers:
                    response_headers.update(headers)

                super().__init__(csv_content, headers=response_headers)

        # 视图中使用响应格式化
        # apps/blog/views.py
        from rest_framework.views import APIView
        from .response_formatters import APIResponse, XMLResponse, CSVResponse

        class ArticleListView(APIView):
            """文章列表视图"""

            def get(self, request):
                """获取文章列表"""
                from .models import Article
                from .serializers import ArticleListSerializer

                # 获取查询参数
                page = int(request.GET.get('page', 1))
                per_page = int(request.GET.get('per_page', 10))
                format_type = request.GET.get('format', 'json')

                # 查询文章
                articles = Article.objects.filter(status='published').select_related('author', 'category')
                paginator = Paginator(articles, per_page)
                page_obj = paginator.get_page(page)

                # 序列化数据
                serializer = ArticleListSerializer(page_obj.object_list, many=True, context={'request': request})
                data = serializer.data

                # 根据格式返回不同响应
                if format_type == 'xml':
                    return XMLResponse({
                        'success': True,
                        'message': '获取文章列表成功',
                        'data': data,
                        'pagination': {
                            'current_page': page_obj.number,
                            'total_pages': paginator.num_pages,
                            'total_items': paginator.count,
                        }
                    })
                elif format_type == 'csv':
                    csv_data = []
                    for article in data:
                        csv_data.append({
                            'ID': article['id'],
                            '标题': article['title'],
                            '作者': article['author_name'],
                            '创建时间': article['created_at'],
                        })
                    return CSVResponse(csv_data, 'articles.csv')
                else:
                    return APIResponse.paginated(data, page_obj)

        class StatisticsView(APIView):
            """统计信息视图"""

            def get(self, request):
                """获取统计信息"""
                from .models import Article, Comment
                from django.db.models import Count, Avg

                stats = {
                    'articles_count': Article.objects.count(),
                    'comments_count': Comment.objects.count(),
                    'avg_comments_per_article': Article.objects.aggregate(
                        avg_comments=Avg('comment_count')
                    )['avg_comments'] or 0,
                }

                metadata = {
                    'generated_at': timezone.now().isoformat(),
                    'cache_duration': '300 seconds',
                }

                return APIResponse.success(stats, '获取统计信息成功', metadata=metadata)

03.异常处理
    a.说明
        自定义异常类
        全局异常处理
        错误码管理
    b.示例
        # apps/core/exceptions.py
        from rest_framework.exceptions import APIException
        from rest_framework import status
        from django.utils.translation import gettext_lazy as _
        import logging

        logger = logging.getLogger('api')

        class BaseAPIException(APIException):
            """基础API异常类"""

            def __init__(self, detail=None, code=None, status_code=status.HTTP_400_BAD_REQUEST):
                self.status_code = status_code
                super().__init__(detail, code)

            def __str__(self):
                return str(self.detail)

        # 业务异常类
        class BusinessException(BaseAPIException):
            """业务逻辑异常"""

            def __init__(self, message, code='BUSINESS_ERROR', status_code=status.HTTP_400_BAD_REQUEST):
                super().__init__(
                    detail=message,
                    code=code,
                    status_code=status_code
                )
                logger.warning(f"BusinessException: {message}")

        class ValidationException(BaseAPIException):
            """数据验证异常"""

            def __init__(self, message, field=None, code='VALIDATION_ERROR'):
                super().__init__(
                    detail=message,
                    code=code,
                    status_code=status.HTTP_400_BAD_REQUEST
                )
                self.field = field
                logger.warning(f"ValidationException: {message} (field: {field})")

        class AuthenticationException(BaseAPIException):
            """认证异常"""

            def __init__(self, message='认证失败', code='AUTHENTICATION_FAILED'):
                super().__init__(
                    detail=message,
                    code=code,
                    status_code=status.HTTP_401_UNAUTHORIZED
                )
                logger.warning(f"AuthenticationException: {message}")

        class PermissionException(BaseAPIException):
            """权限异常"""

            def __init__(self, message='权限不足', code='PERMISSION_DENIED'):
                super().__init__(
                    detail=message,
                    code=code,
                    status_code=status.HTTP_403_FORBIDDEN
                )
                logger.warning(f"PermissionException: {message}")

        class ResourceNotFoundException(BaseAPIException):
            """资源不存在异常"""

            def __init__(self, resource_name='资源', code='RESOURCE_NOT_FOUND'):
                super().__init__(
                    detail=f'{resource_name}不存在',
                    code=code,
                    status_code=status.HTTP_404_NOT_FOUND
                )
                self.resource_name = resource_name
                logger.warning(f"ResourceNotFoundException: {resource_name}")

        class RateLimitException(BaseAPIException):
            """频率限制异常"""

            def __init__(self, message='请求频率超限', retry_after=60, code='RATE_LIMIT_EXCEEDED'):
                super().__init__(
                    detail=message,
                    code=code,
                    status_code=status.HTTP_429_TOO_MANY_REQUESTS
                )
                self.retry_after = retry_after
                logger.warning(f"RateLimitException: {message} (retry_after: {retry_after}s)")

        class ServerException(BaseAPIException):
            """服务器异常"""

            def __init__(self, message='服务器内部错误', code='SERVER_ERROR'):
                super().__init__(
                    detail=message,
                    code=code,
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
                )
                logger.error(f"ServerException: {message}")

        # 特定业务异常
        class ArticleNotFoundException(ResourceNotFoundException):
            """文章不存在异常"""

            def __init__(self):
                super().__init__(resource_name='文章', code='ARTICLE_NOT_FOUND')

        class CommentNotFoundException(ResourceNotFoundException):
            """评论不存在异常"""

            def __init__(self):
                super().__init__(resource_name='评论', code='COMMENT_NOT_FOUND')

        class UserNotFoundException(ResourceNotFoundException):
            """用户不存在异常"""

            def __init__(self):
                super().__init__(resource_name='用户', code='USER_NOT_FOUND')

        class InvalidStatusException(ValidationException):
            """无效状态异常"""

            def __init__(self, current_status, valid_statuses):
                message = f'当前状态{current_status}无效,有效状态为: {", ".join(valid_statuses)}'
                super().__init__(message, field='status', code='INVALID_STATUS')

        class DuplicateOperationException(BusinessException):
            """重复操作异常"""

            def __init__(self, operation):
                super().__init__(
                    message=f'重复执行操作: {operation}',
                    code='DUPLICATE_OPERATION'
                )

        # 自定义异常处理器
        from rest_framework.views import exception_handler
        from django.http import Http404
        from django.core.exceptions import PermissionDenied, ValidationError

        def custom_exception_handler(exc, context):
            """自定义异常处理器"""

            # 调用DRF默认异常处理器
            response = exception_handler(exc, context)

            if response is not None:
                # 记录异常信息
                logger.error(
                    f"API Exception: {exc.__class__.__name__}: {str(exc)}",
                    extra={
                        'status_code': response.status_code,
                        'path': context['request'].path,
                        'method': context['request'].method,
                        'user': getattr(context['request'].user, 'username', 'Anonymous'),
                    },
                    exc_info=True
                )

                # 自定义响应格式
                custom_response_data = {
                    'success': False,
                    'code': response.status_code,
                    'message': '',
                    'timestamp': timezone.now().isoformat(),
                }

                # 根据异常类型设置错误信息
                if isinstance(exc, BaseAPIException):
                    custom_response_data['message'] = exc.detail
                    custom_response_data['error_code'] = exc.default_code

                    # 添加额外信息
                    if hasattr(exc, 'field') and exc.field:
                        custom_response_data['field'] = exc.field

                    if hasattr(exc, 'retry_after') and exc.retry_after:
                        response['Retry-After'] = str(exc.retry_after)
                        custom_response_data['retry_after'] = exc.retry_after

                elif isinstance(exc, Http404):
                    custom_response_data['message'] = '请求的资源不存在'
                    custom_response_data['error_code'] = 'NOT_FOUND'

                elif isinstance(exc, PermissionDenied):
                    custom_response_data['message'] = '权限不足'
                    custom_response_data['error_code'] = 'PERMISSION_DENIED'

                elif isinstance(exc, ValidationError):
                    if hasattr(exc, 'message_dict'):
                        # 表单验证错误
                        custom_response_data['message'] = '数据验证失败'
                        custom_response_data['error_code'] = 'VALIDATION_FAILED'
                        custom_response_data['errors'] = exc.message_dict
                    elif hasattr(exc, 'messages'):
                        # 列表验证错误
                        custom_response_data['message'] = '数据验证失败'
                        custom_response_data['error_code'] = 'VALIDATION_FAILED'
                        custom_response_data['errors'] = exc.messages
                    else:
                        custom_response_data['message'] = str(exc)
                        custom_response_data['error_code'] = 'VALIDATION_FAILED'

                elif response.status_code == 400:
                    custom_response_data['message'] = '请求参数错误'
                    custom_response_data['error_code'] = 'BAD_REQUEST'

                elif response.status_code == 401:
                    custom_response_data['message'] = '认证失败'
                    custom_response_data['error_code'] = 'AUTHENTICATION_FAILED'

                elif response.status_code == 403:
                    custom_response_data['message'] = '权限不足'
                    custom_response_data['error_code'] = 'PERMISSION_DENIED'

                elif response.status_code == 422:
                    custom_response_data['message'] = '请求无法处理'
                    custom_response_data['error_code'] = 'UNPROCESSABLE_ENTITY'

                elif response.status_code >= 500:
                    custom_response_data['message'] = '服务器内部错误'
                    custom_response_data['error_code'] = 'INTERNAL_SERVER_ERROR'

                # 保留原始错误详情(如果有)
                if 'detail' in response.data:
                    custom_response_data['original_error'] = response.data['detail']

                response.data = custom_response_data

            return response

        # 错误码管理
        class ErrorCodes:
            """错误码常量"""

            # 通用错误码
            SUCCESS = 'SUCCESS'
            BAD_REQUEST = 'BAD_REQUEST'
            UNAUTHORIZED = 'UNAUTHORIZED'
            FORBIDDEN = 'FORBIDDEN'
            NOT_FOUND = 'NOT_FOUND'
            INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR'

            # 业务错误码
            VALIDATION_FAILED = 'VALIDATION_FAILED'
            INVALID_PARAMETERS = 'INVALID_PARAMETERS'
            DUPLICATE_OPERATION = 'DUPLICATE_OPERATION'
            BUSINESS_ERROR = 'BUSINESS_ERROR'

            # 用户相关错误码
            USER_NOT_FOUND = 'USER_NOT_FOUND'
            USER_ALREADY_EXISTS = 'USER_ALREADY_EXISTS'
            INVALID_CREDENTIALS = 'INVALID_CREDENTIALS'
            ACCOUNT_LOCKED = 'ACCOUNT_LOCKED'

            # 文章相关错误码
            ARTICLE_NOT_FOUND = 'ARTICLE_NOT_FOUND'
            ARTICLE_ALREADY_PUBLISHED = 'ARTICLE_ALREADY_PUBLISHED'
            INVALID_ARTICLE_STATUS = 'INVALID_ARTICLE_STATUS'

            # 权限相关错误码
            PERMISSION_DENIED = 'PERMISSION_DENIED'
            INSUFFICIENT_PERMISSIONS = 'INSUFFICIENT_PERMISSIONS'

            # 频率限制错误码
            RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED'
            QUOTA_EXCEEDED = 'QUOTA_EXCEEDED'

        # 在视图中使用异常处理
        # apps/blog/views.py
        from rest_framework.views import APIView
        from rest_framework.permissions import IsAuthenticated
        from .exceptions import (
            ArticleNotFoundException, InvalidStatusException,
            PermissionException, DuplicateOperationException
        )

        class ArticleDetailView(APIView):
        """文章详情视图"""

        def get_object(self, slug):
            """获取文章对象"""
            try:
                return Article.objects.get(slug=slug, status='published')
            except Article.DoesNotExist:
                raise ArticleNotFoundException()

        def get(self, request, slug):
            """获取文章详情"""
            try:
                article = self.get_object(slug)
                serializer = ArticleDetailSerializer(article, context={'request': request})
                return APIResponse.success(serializer.data, '获取文章详情成功')

            except BaseAPIException as e:
                raise e
            except Exception as e:
                logger.error(f"获取文章详情失败: {str(e)}", exc_info=True)
                raise ServerException('获取文章详情失败')

        def put(self, request, slug):
            """更新文章"""
            if not request.user.is_authenticated:
                raise AuthenticationException('需要登录才能更新文章')

            try:
                article = self.get_object(slug)

                # 权限检查
                if article.author != request.user and not request.user.is_staff:
                    raise PermissionException('只能更新自己的文章')

                # 检查文章状态
                if article.status == 'published':
                    raise InvalidStatusException(article.status, ['draft', 'under_review'])

                serializer = ArticleUpdateSerializer(article, data=request.data, partial=True)
                if serializer.is_valid():
                    serializer.save()
                    return APIResponse.success(serializer.data, '文章更新成功')
                else:
                    raise ValidationException('数据验证失败', error_details=serializer.errors)

            except BaseAPIException as e:
                raise e
            except Exception as e:
                logger.error(f"更新文章失败: {str(e)}", exc_info=True)
                raise ServerException('更新文章失败')

        def post(self, request, slug):
            """点赞文章"""
            if not request.user.is_authenticated:
                raise AuthenticationException('需要登录才能点赞')

            try:
                article = self.get_object(slug)

                # 检查是否已经点赞
                if ArticleLike.objects.filter(user=request.user, article=article).exists():
                    raise DuplicateOperationException('点赞')

                # 创建点赞记录
                ArticleLike.objects.create(user=request.user, article=article)

                # 更新文章点赞数
                article.like_count += 1
                article.save(update_fields=['like_count'])

                return APIResponse.success({'like_count': article.like_count}, '点赞成功')

            except BaseAPIException as e:
                raise e
            except Exception as e:
                logger.error(f"点赞失败: {str(e)}", exc_info=True)
                raise ServerException('点赞失败')

4.2 Generic Views

01.基础混入类
    a.说明
        ListModelMixin
        CreateModelMixin
        RetrieveModelMixin
        UpdateModelMixin
        DestroyModelMixin
    b.示例
        # apps/blog/mixins.py
        from rest_framework import mixins
        from rest_framework.response import Response
        from rest_framework import status
        from django.utils import timezone
        from django.core.cache import cache
        import logging

        logger = logging.getLogger('api')

        class CustomListModelMixin(mixins.ListModelMixin):
            """自定义列表混入"""

            def list(self, request, *args, **kwargs):
                """重写列表方法"""
                queryset = self.filter_queryset(self.get_queryset())

                # 缓存检查
                cache_key = self.get_cache_key(request)
                cached_data = cache.get(cache_key)
                if cached_data and not request.GET.get('no_cache'):
                    logger.info(f"Cache hit for {cache_key}")
                    return Response(cached_data)

                page = self.paginate_queryset(queryset)
                if page is not None:
                    serializer = self.get_serializer(page, many=True)
                    response_data = self.get_paginated_response(serializer.data).data

                    # 缓存响应数据
                    cache.set(cache_key, response_data, timeout=300)  # 5分钟缓存

                    return Response(response_data)

                serializer = self.get_serializer(queryset, many=True)
                return Response(serializer.data)

            def get_cache_key(self, request):
                """生成缓存键"""
                from django.contrib.auth.models import AnonymousUser
                import hashlib

                user = getattr(request, 'user', AnonymousUser())
                user_id = user.id if user.is_authenticated else 'anonymous'
                path = request.get_full_path()

                # 创建包含用户和路径的缓存键
                cache_string = f"{user_id}:{path}"
                return hashlib.md5(cache_string.encode()).hexdigest()

        class CustomCreateModelMixin(mixins.CreateModelMixin):
            """自定义创建混入"""

            def create(self, request, *args, **kwargs):
                """重写创建方法"""
                # 创建前的验证和预处理
                self.perform_pre_creation_validation(request.data)

                serializer = self.get_serializer(data=request.data)
                serializer.is_valid(raise_exception=True)

                # 执行创建前操作
                self.perform_create_before(serializer)

                try:
                    instance = self.perform_create(serializer)
                except Exception as e:
                    logger.error(f"创建对象失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '创建失败',
                        'error': str(e)
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 执行创建后操作
                self.perform_create_after(instance)

                # 清除相关缓存
                self.clear_related_cache()

                headers = self.get_success_headers(serializer.data)
                return Response({
                    'success': True,
                    'message': '创建成功',
                    'data': serializer.data
                }, status=status.HTTP_201_CREATED, headers=headers)

            def perform_pre_creation_validation(self, data):
                """创建前验证"""
                # 检查用户权限
                if not hasattr(self, 'request') or not self.request.user.is_authenticated:
                    from .exceptions import AuthenticationException
                    raise AuthenticationException('需要登录才能创建内容')

                # 检查创建频率限制
                self.check_creation_rate_limit()

                # 业务逻辑验证
                self.validate_business_rules(data)

            def check_creation_rate_limit(self):
                """检查创建频率限制"""
                from django.core.cache import cache
                import time

                user = self.request.user
                cache_key = f"create_limit_{user.id}"
                current_time = int(time.time())
                window_start = current_time - 60  # 1分钟窗口

                # 获取最近创建记录
                creations = cache.get(cache_key, [])
                recent_creations = [c for c in creations if c > window_start]

                # 限制每分钟最多创建5个对象
                if len(recent_creations) >= 5:
                    from .exceptions import RateLimitException
                    raise RateLimitException('创建频率过高,请稍后再试', retry_after=60)

                # 记录本次创建
                recent_creations.append(current_time)
                cache.set(cache_key, recent_creations, 60)

            def validate_business_rules(self, data):
                """验证业务规则"""
                # 子类可以重写此方法实现自定义业务验证
                pass

            def perform_create_before(self, serializer):
                """创建前操作"""
                # 设置默认值
                if hasattr(serializer.Meta.model, 'created_at'):
                    serializer.validated_data['created_at'] = timezone.now()

                if hasattr(serializer.Meta.model, 'author'):
                    serializer.validated_data['author'] = self.request.user

            def perform_create(self, serializer):
                """执行创建"""
                return serializer.save()

            def perform_create_after(self, instance):
                """创建后操作"""
                # 记录操作日志
                logger.info(
                    f"对象创建成功: {instance.__class__.__name__} {instance.id} "
                    f"by {self.request.user.username}"
                )

                # 发送通知
                self.send_creation_notification(instance)

            def send_creation_notification(self, instance):
                """发送创建通知"""
                # 子类可以重写此方法发送通知
                pass

            def clear_related_cache(self):
                """清除相关缓存"""
                from django.core.cache import cache
                import re

                # 清除用户相关的缓存
                if hasattr(self.request, 'user') and self.request.user.is_authenticated:
                    user_cache_pattern = f"*{self.request.user.id}:*"
                    # 这里应该使用更高效的缓存清除方法
                    cache.delete_pattern(user_cache_pattern)

        class CustomRetrieveModelMixin(mixins.RetrieveModelMixin):
            """自定义检索混入"""

            def retrieve(self, request, *args, **kwargs):
                """重写检索方法"""
                try:
                    instance = self.get_object()

                    # 记录访问
                    self.log_access(instance)

                    # 增加浏览次数
                    self.increment_view_count(instance)

                    serializer = self.get_serializer(instance)
                    return Response({
                        'success': True,
                        'message': '获取数据成功',
                        'data': serializer.data
                    })

                except Exception as e:
                    logger.error(f"检索对象失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '获取数据失败',
                        'error': str(e)
                    }, status=status.HTTP_404_NOT_FOUND)

            def log_access(self, instance):
                """记录访问"""
                logger.info(
                    f"对象访问: {instance.__class__.__name__} {instance.id} "
                    f"by {getattr(self.request.user, 'username', 'Anonymous')} "
                    f"from {self.get_client_ip()}"
                )

            def increment_view_count(self, instance):
                """增加浏览次数"""
                if hasattr(instance, 'view_count'):
                    instance.view_count += 1
                    instance.save(update_fields=['view_count'])

            def get_client_ip(self):
                """获取客户端IP"""
                x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR')
                if x_forwarded_for:
                    ip = x_forwarded_for.split(',')[0]
                else:
                    ip = self.request.META.get('REMOTE_ADDR')
                return ip

        class CustomUpdateModelMixin(mixins.UpdateModelMixin):
            """自定义更新混入"""

            def update(self, request, *args, **kwargs):
                """重写更新方法"""
                partial = kwargs.pop('partial', False)
                instance = self.get_object()

                # 权限检查
                self.check_update_permissions(instance)

                # 版本检查(乐观锁)
                if 'version' in request.data:
                    self.check_version(instance, request.data['version'])

                serializer = self.get_serializer(instance, data=request.data, partial=partial)
                serializer.is_valid(raise_exception=True)

                # 执行更新前操作
                self.perform_update_before(instance, serializer)

                try:
                    instance = self.perform_update(serializer)
                except Exception as e:
                    logger.error(f"更新对象失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '更新失败',
                        'error': str(e)
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 执行更新后操作
                self.perform_update_after(instance)

                # 清除相关缓存
                self.clear_related_cache(instance)

                return Response({
                    'success': True,
                    'message': '更新成功',
                    'data': serializer.data
                })

            def check_update_permissions(self, instance):
                """检查更新权限"""
                if not self.request.user.is_authenticated:
                    from .exceptions import AuthenticationException
                    raise AuthenticationException('需要登录才能更新内容')

                # 检查是否为作者或管理员
                if hasattr(instance, 'author'):
                    if instance.author != self.request.user and not self.request.user.is_staff:
                        from .exceptions import PermissionException
                        raise PermissionException('只能更新自己的内容')

            def check_version(self, instance, provided_version):
                """检查版本(乐观锁)"""
                if hasattr(instance, 'version'):
                    if instance.version != provided_version:
                        from .exceptions import ValidationException
                        raise ValidationException(
                            '对象已被其他用户修改,请刷新后重试',
                            field='version',
                            error_code='VERSION_CONFLICT'
                        )

            def perform_update_before(self, instance, serializer):
                """更新前操作"""
                # 设置更新时间
                if hasattr(instance, 'updated_at'):
                    instance.updated_at = timezone.now()

                # 版本号递增
                if hasattr(instance, 'version'):
                    serializer.validated_data['version'] = instance.version + 1

            def perform_update(self, serializer):
                """执行更新"""
                return serializer.save()

            def perform_update_after(self, instance):
                """更新后操作"""
                # 记录操作日志
                logger.info(
                    f"对象更新成功: {instance.__class__.__name__} {instance.id} "
                    f"by {self.request.user.username}"
                )

                # 发送更新通知
                self.send_update_notification(instance)

            def send_update_notification(self, instance):
                """发送更新通知"""
                # 子类可以重写此方法发送通知
                pass

            def clear_related_cache(self, instance):
                """清除相关缓存"""
                from django.core.cache import cache

                # 清除实例相关的缓存
                instance_cache_keys = [
                    f"instance_{instance.__class__.__name__.lower()}_{instance.id}",
                    f"list_{instance.__class__.__name__.lower()}_*",
                ]

                for key_pattern in instance_cache_keys:
                    # 这里应该使用更高效的缓存清除方法
                    cache.delete_pattern(key_pattern)

        class CustomDestroyModelMixin(mixins.DestroyModelMixin):
            """自定义删除混入"""

            def destroy(self, request, *args, **kwargs):
                """重写删除方法"""
                try:
                    instance = self.get_object()

                    # 权限检查
                    self.check_destroy_permissions(instance)

                    # 执行删除前操作
                    self.perform_destroy_before(instance)

                    # 执行删除
                    self.perform_destroy(instance)

                    # 执行删除后操作
                    self.perform_destroy_after(instance)

                    # 清除相关缓存
                    self.clear_related_cache(instance)

                    return Response({
                        'success': True,
                        'message': '删除成功'
                    }, status=status.HTTP_204_NO_CONTENT)

                except Exception as e:
                    logger.error(f"删除对象失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '删除失败',
                        'error': str(e)
                    }, status=status.HTTP_400_BAD_REQUEST)

            def check_destroy_permissions(self, instance):
                """检查删除权限"""
                if not self.request.user.is_authenticated:
                    from .exceptions import AuthenticationException
                    raise AuthenticationException('需要登录才能删除内容')

                # 检查是否为作者或管理员
                if hasattr(instance, 'author'):
                    if instance.author != self.request.user and not self.request.user.is_superuser:
                        from .exceptions import PermissionException
                        raise PermissionException('只能删除自己的内容')

            def perform_destroy_before(self, instance):
                """删除前操作"""
                # 备份数据(软删除选项)
                if hasattr(instance, 'is_deleted'):
                    instance.is_deleted = True
                    instance.deleted_at = timezone.now()
                    instance.deleted_by = self.request.user
                    instance.save(update_fields=['is_deleted', 'deleted_at', 'deleted_by'])
                    # 不执行物理删除
                    return False

            def perform_destroy(self, instance):
                """执行删除"""
                instance.delete()

            def perform_destroy_after(self, instance):
                """删除后操作"""
                # 记录操作日志
                logger.info(
                    f"对象删除成功: {instance.__class__.__name__} {instance.id} "
                    f"by {self.request.user.username}"
                )

                # 发送删除通知
                self.send_deletion_notification(instance)

            def send_deletion_notification(self, instance):
                """发送删除通知"""
                # 子类可以重写此方法发送通知
                pass

            def clear_related_cache(self, instance):
                """清除相关缓存"""
                from django.core.cache import cache

                # 清除实例相关的缓存
                instance_cache_keys = [
                    f"instance_{instance.__class__.__name__.lower()}_{instance.id}",
                    f"list_{instance.__class__.__name__.lower()}_*",
                ]

                for key_pattern in instance_cache_keys:
                    cache.delete_pattern(key_pattern)

        # 组合混入类
        class CustomModelMixin(
            CustomListModelMixin,
            CustomCreateModelMixin,
            CustomRetrieveModelMixin,
            CustomUpdateModelMixin,
            CustomDestroyModelMixin
        ):
            """自定义模型混入组合"""
            pass

02.通用视图类
    a.说明
        GenericAPIView
        ListCreateAPIView
        RetrieveUpdateDestroyAPIView
        自定义通用视图
    b.示例
        # apps/blog/generic_views.py
        from rest_framework import generics, permissions, status
        from rest_framework.response import Response
        from django_filters.rest_framework import DjangoFilterBackend
        from rest_framework.filters import SearchFilter, OrderingFilter
        from django.core.cache import cache
        from django.db.models import Q, F, Count
        from django.utils import timezone
        from datetime import timedelta

        from .models import Article, Category, Tag, Comment
        from .serializers import (
            ArticleListSerializer, ArticleDetailSerializer,
            CategorySerializer, TagSerializer, CommentSerializer
        )
        from .mixins import CustomModelMixin
        from .exceptions import ArticleNotFoundException
        from .pagination import StandardResultsSetPagination
        import logging

        logger = logging.getLogger('api')

        class BaseGenericAPIView(generics.GenericAPIView):
            """基础通用视图"""

            permission_classes = [permissions.IsAuthenticatedOrReadOnly]
            pagination_class = StandardResultsSetPagination

            def get_queryset(self):
                """获取查询集"""
                queryset = super().get_queryset()

                # 应用时间过滤
                queryset = self.apply_time_filters(queryset)

                # 应用状态过滤
                queryset = self.apply_status_filters(queryset)

                # 应用排序
                queryset = self.apply_ordering(queryset)

                return queryset

            def apply_time_filters(self, queryset):
                """应用时间过滤"""
                # 获取时间范围参数
                days = self.request.GET.get('days')
                start_date = self.request.GET.get('start_date')
                end_date = self.request.GET.get('end_date')

                if days:
                    # 过滤最近N天的数据
                    start_date = timezone.now() - timedelta(days=int(days))
                    queryset = queryset.filter(created_at__gte=start_date)

                if start_date:
                    queryset = queryset.filter(created_at__date__gte=start_date)

                if end_date:
                    queryset = queryset.filter(created_at__date__lte=end_date)

                return queryset

            def apply_status_filters(self, queryset):
                """应用状态过滤"""
                # 对于非管理员,只显示已发布的内容
                if not self.request.user.is_staff:
                    queryset = queryset.filter(status='published')

                return queryset

            def apply_ordering(self, queryset):
                """应用排序"""
                ordering = self.request.GET.get('ordering', '-created_at')
                if ordering:
                    # 允许多字段排序
                    order_fields = ordering.split(',')
                    queryset = queryset.order_by(*order_fields)

                return queryset

            def get_serializer_context(self):
                """获取序列化器上下文"""
                context = super().get_serializer_context()
                context['request'] = self.request
                context['view'] = self
                return context

            def get_paginated_response(self, data):
                """自定义分页响应"""
                return self.paginator.get_paginated_response({
                    'success': True,
                    'message': '获取数据成功',
                    'data': data,
                    'pagination': {
                        'count': self.page.paginator.count,
                        'next': self.paginator.get_next_link(),
                        'previous': self.paginator.get_previous_link(),
                        'total_pages': self.paginator.num_pages,
                        'current_page': self.paginator.page.number,
                        'page_size': self.paginator.page_size,
                    }
                })

            def handle_exception(self, exc):
                """异常处理"""
                logger.error(f"视图异常: {exc.__class__.__name__}: {str(exc)}", exc_info=True)
                return super().handle_exception(exc)

        class ArticleListCreateView(CustomModelMixin, BaseGenericAPIView):
            """文章列表和创建视图"""

            serializer_class = ArticleListSerializer
            filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
            filterset_fields = ['category', 'tags', 'author', 'status']
            search_fields = ['title', 'content', 'excerpt']
            ordering_fields = ['created_at', 'updated_at', 'view_count', 'like_count']
            ordering = ['-created_at']

            def get_serializer_class(self):
                """根据请求方法选择序列化器"""
                if self.request.method == 'POST':
                    from .serializers import ArticleCreateSerializer
                    return ArticleCreateSerializer
                return self.serializer_class

            def perform_create_before(self, serializer):
                """创建文章前的预处理"""
                super().perform_create_before(serializer)

                # 设置作者
                serializer.validated_data['author'] = self.request.user

                # 自动生成slug
                title = serializer.validated_data.get('title')
                if title and not serializer.validated_data.get('slug'):
                    from django.utils.text import slugify
                    slug = slugify(title)

                    # 确保slug唯一
                    original_slug = slug
                    counter = 1
                    while Article.objects.filter(slug=slug).exists():
                        slug = f"{original_slug}-{counter}"
                        counter += 1

                    serializer.validated_data['slug'] = slug

                # 自动生成摘要
                content = serializer.validated_data.get('content')
                if content and not serializer.validated_data.get('excerpt'):
                    excerpt = content[:200] + '...' if len(content) > 200 else content
                    serializer.validated_data['excerpt'] = excerpt

            def send_creation_notification(self, instance):
                """发送文章创建通知"""
                # 通知关注者
                followers = instance.author.followers.all()
                for follower in followers:
                    from .notifications import send_notification
                    send_notification(
                        user=follower,
                        title=f"新文章:{instance.title}",
                        message=f"{instance.author.username} 发布了新文章",
                        url=instance.get_absolute_url()
                    )

            def validate_business_rules(self, data):
                """验证文章创建的业务规则"""
                # 检查标题重复
                title = data.get('title')
                if title and Article.objects.filter(
                    title__iexact=title,
                    author=self.request.user
                ).exists():
                    from .exceptions import ValidationException
                    raise ValidationException(
                        '您已发布过相同标题的文章',
                        field='title',
                        error_code='DUPLICATE_TITLE'
                    )

                # 检查发布频率
                today = timezone.now().date()
                today_published = Article.objects.filter(
                    author=self.request.user,
                    status='published',
                    published_at__date=today
                ).count()

                if today_published >= 5:  # 每日最多发布5篇
                    from .exceptions import BusinessException
                    raise BusinessException(
                        '今日发布文章数量已达上限',
                        code='DAILY_PUBLISH_LIMIT_EXCEEDED'
                    )

        class ArticleDetailView(CustomModelMixin, BaseGenericAPIView):
            """文章详情视图"""

            serializer_class = ArticleDetailSerializer
            lookup_field = 'slug'

            def get_serializer_class(self):
                """根据请求方法选择序列化器"""
                if self.request.method in ['PUT', 'PATCH']:
                    from .serializers import ArticleUpdateSerializer
                    return ArticleUpdateSerializer
                return self.serializer_class

            def get_object(self):
                """获取文章对象"""
                slug = self.kwargs.get(self.lookup_field)

                try:
                    queryset = self.get_queryset()
                    instance = queryset.get(slug=slug)
                    return instance
                except Article.DoesNotExist:
                    raise ArticleNotFoundException()

            def check_update_permissions(self, instance):
                """检查更新权限"""
                super().check_update_permissions(instance)

                # 草稿状态的检查
                if instance.status == 'published' and not self.request.user.is_staff:
                    from .exceptions import PermissionException
                    raise PermissionException(
                        '已发布的文章需要管理员权限才能修改',
                        code='PUBLISHED_ARTICLE_MODIFICATION_RESTRICTED'
                    )

            def check_destroy_permissions(self, instance):
                """检查删除权限"""
                super().check_destroy_permissions(instance)

                # 已发布文章的删除限制
                if instance.status == 'published':
                    days_since_published = (timezone.now() - instance.published_at).days

                    if days_since_published < 7 and not self.request.user.is_superuser:
                        from .exceptions import PermissionException
                        raise PermissionException(
                            '已发布7天内的文章需要超级管理员权限才能删除',
                            code='RECENT_PUBLISHED_ARTICLE_DELETION_RESTRICTED'
                        )

            def increment_view_count(self, instance):
                """增加浏览次数"""
                from .models import ArticleView
                from django.utils import timezone

                # 异步记录浏览
                client_ip = self.get_client_ip()
                user_agent = self.request.META.get('HTTP_USER_AGENT', '')

                # 检查是否为有效浏览(防止刷量)
                cache_key = f"view_{instance.id}_{client_ip}"
                last_view_time = cache.get(cache_key)

                if not last_view_time or (timezone.now() - last_view_time).seconds > 300:  # 5分钟内只计一次
                    # 创建浏览记录
                    ArticleView.objects.get_or_create(
                        article=instance,
                        ip_address=client_ip,
                        user_agent=user_agent,
                        user=self.request.user if self.request.user.is_authenticated else None,
                        defaults={'viewed_at': timezone.now()}
                    )

                    # 更新浏览次数
                    instance.view_count = F('view_count') + 1
                    instance.save(update_fields=['view_count'])

                    # 设置缓存防止重复计数
                    cache.set(cache_key, timezone.now(), 300)

            def get_client_ip(self):
                """获取客户端IP"""
                x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR')
                if x_forwarded_for:
                    ip = x_forwarded_for.split(',')[0]
                else:
                    ip = self.request.META.get('REMOTE_ADDR')
                return ip

            def send_update_notification(self, instance):
                """发送文章更新通知"""
                if instance.status == 'published':
                    # 通知文章的评论者
                    commenters = Comment.objects.filter(
                        article=instance,
                        is_approved=True
                    ).values_list('author', flat=True).distinct()

                    for commenter_id in commenters:
                        from .notifications import send_notification
                        send_notification(
                            user_id=commenter_id,
                            title=f"文章更新:{instance.title}",
                            message=f"您评论的文章《{instance.title}》已更新",
                            url=instance.get_absolute_url()
                        )

        class CategoryListCreateView(BaseGenericAPIView):
            """分类列表和创建视图"""

            serializer_class = CategorySerializer
            queryset = Category.objects.all()

            def get_queryset(self):
                """获取查询集"""
                queryset = super().get_queryset()

                # 只显示激活的分类
                if not self.request.user.is_staff:
                    queryset = queryset.filter(is_active=True)

                return queryset

            def perform_create(self, serializer):
                """创建分类"""
                # 自动生成slug
                name = serializer.validated_data.get('name')
                if name and not serializer.validated_data.get('slug'):
                    from django.utils.text import slugify
                    slug = slugify(name)

                    # 确保slug唯一
                    original_slug = slug
                    counter = 1
                    while Category.objects.filter(slug=slug).exists():
                        slug = f"{original_slug}-{counter}"
                        counter += 1

                    serializer.validated_data['slug'] = slug

                return super().perform_create(serializer)

        class TagListCreateView(BaseGenericAPIView):
            """标签列表和创建视图"""

            serializer_class = TagSerializer
            queryset = Tag.objects.all()

            def get_queryset(self):
                """获取查询集"""
                queryset = super().get_queryset()

                # 按使用频率排序
                sort_by_usage = self.request.GET.get('sort_by_usage', 'false').lower() == 'true'
                if sort_by_usage:
                    queryset = queryset.annotate(
                        usage_count=Count('articles')
                    ).order_by('-usage_count', 'name')
                else:
                    queryset = queryset.order_by('name')

                return queryset

        class CommentListCreateView(BaseGenericAPIView):
            """评论列表和创建视图"""

            serializer_class = CommentSerializer
            filter_backends = [DjangoFilterBackend, OrderingFilter]
            filterset_fields = ['article', 'author', 'is_approved']
            ordering_fields = ['created_at', 'updated_at']
            ordering = ['-created_at']

            def get_queryset(self):
                """获取查询集"""
                queryset = super().get_queryset()

                # 对于普通用户,只显示已审核的评论
                if not self.request.user.is_staff:
                    queryset = queryset.filter(is_approved=True)

                return queryset

            def perform_create(self, serializer):
                """创建评论"""
                # 设置作者
                if self.request.user.is_authenticated:
                    serializer.validated_data['author'] = self.request.user

                # 根据用户权限设置审核状态
                if self.request.user.is_authenticated and self.request.user.is_staff:
                    serializer.validated_data['is_approved'] = True
                else:
                    serializer.validated_data['is_approved'] = False

                # 检查评论频率限制
                self.check_comment_rate_limit()

                return super().perform_create(serializer)

            def check_comment_rate_limit(self):
                """检查评论频率限制"""
                from django.core.cache import cache
                import time

                if not self.request.user.is_authenticated:
                    return

                user_id = self.request.user.id
                cache_key = f"comment_limit_{user_id}"
                current_time = int(time.time())
                window_start = current_time - 300  # 5分钟窗口

                # 获取最近评论记录
                comments = cache.get(cache_key, [])
                recent_comments = [c for c in comments if c > window_start]

                # 限制5分钟内最多评论10次
                if len(recent_comments) >= 10:
                    from .exceptions import RateLimitException
                    raise RateLimitException('评论频率过高,请稍后再试', retry_after=300)

                # 记录本次评论
                recent_comments.append(current_time)
                cache.set(cache_key, recent_comments, 300)

        class BulkOperationView(generics.GenericAPIView):
            """批量操作视图"""

            def post(self, request):
                """批量操作"""
                operation = request.data.get('operation')
                ids = request.data.get('ids', [])

                if not operation or not ids:
                    return Response({
                        'success': False,
                        'message': '操作类型和ID列表不能为空'
                    }, status=status.HTTP_400_BAD_REQUEST)

                if len(ids) > 100:  # 限制批量操作数量
                    return Response({
                        'success': False,
                        'message': '批量操作数量不能超过100个'
                    }, status=status.HTTP_400_BAD_REQUEST)

                try:
                    if operation == 'bulk_publish':
                        return self.bulk_publish(ids)
                    elif operation == 'bulk_unpublish':
                        return self.bulk_unpublish(ids)
                    elif operation == 'bulk_delete':
                        return self.bulk_delete(ids)
                    elif operation == 'bulk_feature':
                        return self.bulk_feature(ids)
                    else:
                        return Response({
                            'success': False,
                            'message': f'不支持的操作类型: {operation}'
                        }, status=status.HTTP_400_BAD_REQUEST)

                except Exception as e:
                    logger.error(f"批量操作失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': f'批量操作失败: {str(e)}'
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            def bulk_publish(self, ids):
                """批量发布"""
                from .models import Article

                queryset = Article.objects.filter(id__in=ids)

                # 权限检查
                if not self.request.user.is_staff:
                    queryset = queryset.filter(author=self.request.user)

                updated_count = queryset.update(
                    status='published',
                    published_at=timezone.now()
                )

                return Response({
                    'success': True,
                    'message': f'成功发布{updated_count}篇文章'
                })

            def bulk_unpublish(self, ids):
                """批量取消发布"""
                from .models import Article

                queryset = Article.objects.filter(id__in=ids)

                # 权限检查
                if not self.request.user.is_staff:
                    queryset = queryset.filter(author=self.request.user)

                updated_count = queryset.update(status='draft')

                return Response({
                    'success': True,
                    'message': f'成功取消发布{updated_count}篇文章'
                })

            def bulk_delete(self, ids):
                """批量删除"""
                from .models import Article

                queryset = Article.objects.filter(id__in=ids)

                # 权限检查
                if not self.request.user.is_superuser:
                    queryset = queryset.filter(author=self.request.user)

                deleted_count, _ = queryset.delete()

                return Response({
                    'success': True,
                    'message': f'成功删除{deleted_count}篇文章'
                })

            def bulk_feature(self, ids):
                """批量设为推荐"""
                from .models import Article

                queryset = Article.objects.filter(id__in=ids)

                # 只有管理员可以推荐文章
                if not self.request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只有管理员可以推荐文章'
                    }, status=status.HTTP_403_FORBIDDEN)

                updated_count = queryset.update(is_featured=True)

                return Response({
                    'success': True,
                    'message': f'成功推荐{updated_count}篇文章'
                })

4.3 ViewSet

01ViewSet核心概念
    a.说明
        动作方法映射
        URL配置简化
        代码复用性提升
    b.示例
        # apps/blog/viewsets.py
        from rest_framework import viewsets, permissions, status, filters
        from rest_framework.decorators import action
        from rest_framework.response import Response
        from django_filters.rest_framework import DjangoFilterBackend
        from django.db.models import Q, F, Count, Avg
        from django.utils import timezone
        from datetime import timedelta
        import logging

        from .models import Article, Category, Tag, Comment
        from .serializers import (
            ArticleSerializer, ArticleListSerializer, ArticleDetailSerializer,
            CategorySerializer, TagSerializer, CommentSerializer
        )
        from .permissions import IsOwnerOrReadOnly, IsStaffOrReadOnly
        from .exceptions import ArticleNotFoundException, ValidationException

        logger = logging.getLogger('api')

        class BaseViewSet(viewsets.ModelViewSet):
            """基础ViewSet类"""

            permission_classes = [permissions.IsAuthenticatedOrReadOnly]
            filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]

            def get_queryset(self):
                """获取查询集"""
                queryset = super().get_queryset()

                # 应用状态过滤
                if not self.request.user.is_staff:
                    queryset = self.filter_by_user_permissions(queryset)

                # 应用时间过滤
                queryset = self.apply_time_filters(queryset)

                return queryset

            def filter_by_user_permissions(self, queryset):
                """根据用户权限过滤查询集"""
                # 子类可以重写此方法实现自定义权限过滤
                return queryset

            def apply_time_filters(self, queryset):
                """应用时间过滤"""
                days = self.request.query_params.get('days')
                if days:
                    start_date = timezone.now() - timedelta(days=int(days))
                    queryset = queryset.filter(created_at__gte=start_date)

                return queryset

            def perform_create(self, serializer):
                """创建对象时的预处理"""
                # 设置作者
                if hasattr(serializer.Meta.model, 'author'):
                    serializer.validated_data['author'] = self.request.user

                super().perform_create(serializer)

            def get_serializer_class(self):
                """根据动作选择序列化器"""
        if self.action == 'list':
            return getattr(self, 'list_serializer_class', self.serializer_class)
        elif self.action in ['retrieve', 'update', 'partial_update']:
            return getattr(self, 'detail_serializer_class', self.serializer_class)
        elif self.action == 'create':
            return getattr(self, 'create_serializer_class', self.serializer_class)
        return self.serializer_class

            def list(self, request, *args, **kwargs):
                """重写列表方法"""
                try:
                    queryset = self.filter_queryset(self.get_queryset())
                    page = self.paginate_queryset(queryset)

                    if page is not None:
                        serializer = self.get_serializer(page, many=True)
                        return self.get_paginated_response(serializer.data)

                    serializer = self.get_serializer(queryset, many=True)
                    return Response({
                        'success': True,
                        'message': '获取列表成功',
                        'data': serializer.data
                    })

                except Exception as e:
                    logger.error(f"获取列表失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '获取列表失败',
                        'error': str(e)
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            def create(self, request, *args, **kwargs):
                """重写创建方法"""
                try:
                    serializer = self.get_serializer(data=request.data)
                    serializer.is_valid(raise_exception=True)
                    self.perform_create(serializer)

                    headers = self.get_success_headers(serializer.data)
                    return Response({
                        'success': True,
                        'message': '创建成功',
                        'data': serializer.data
                    }, status=status.HTTP_201_CREATED, headers=headers)

                except Exception as e:
                    logger.error(f"创建失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '创建失败',
                        'error': str(e)
                    }, status=status.HTTP_400_BAD_REQUEST)

            def retrieve(self, request, *args, **kwargs):
                """重写检索方法"""
                try:
                    instance = self.get_object()
                    serializer = self.get_serializer(instance)
                    return Response({
                        'success': True,
                        'message': '获取详情成功',
                        'data': serializer.data
                    })

                except Exception as e:
                    logger.error(f"获取详情失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '获取详情失败',
                        'error': str(e)
                    }, status=status.HTTP_404_NOT_FOUND)

            def update(self, request, *args, **kwargs):
                """重写更新方法"""
                try:
                    partial = kwargs.pop('partial', False)
                    instance = self.get_object()
                    serializer = self.get_serializer(instance, data=request.data, partial=partial)
                    serializer.is_valid(raise_exception=True)
                    self.perform_update(serializer)

                    return Response({
                        'success': True,
                        'message': '更新成功',
                        'data': serializer.data
                    })

                except Exception as e:
                    logger.error(f"更新失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '更新失败',
                        'error': str(e)
                    }, status=status.HTTP_400_BAD_REQUEST)

            def destroy(self, request, *args, **kwargs):
                """重写删除方法"""
                try:
                    instance = self.get_object()
                    self.perform_destroy(instance)
                    return Response({
                        'success': True,
                        'message': '删除成功'
                    }, status=status.HTTP_204_NO_CONTENT)

                except Exception as e:
                    logger.error(f"删除失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '删除失败',
                        'error': str(e)
                    }, status=status.HTTP_400_BAD_REQUEST)

        class ArticleViewSet(BaseViewSet):
            """文章ViewSet"""

            serializer_class = ArticleSerializer
            list_serializer_class = ArticleListSerializer
            detail_serializer_class = ArticleDetailSerializer
            create_serializer_class = ArticleSerializer

            filter_fields = ['category', 'tags', 'author', 'status']
            search_fields = ['title', 'content', 'excerpt']
            ordering_fields = ['created_at', 'updated_at', 'view_count', 'like_count', 'published_at']
            ordering = ['-created_at']

            def get_queryset(self):
                """获取文章查询集"""
                queryset = Article.objects.all()

                # 应用状态过滤
                if not self.request.user.is_staff:
                    queryset = queryset.filter(status='published')

                # 应用作者过滤
                author_username = self.request.query_params.get('author_username')
                if author_username:
                    queryset = queryset.filter(author__username=author_username)

                # 应用分类过滤
                category_slug = self.request.query_params.get('category_slug')
                if category_slug:
                    queryset = queryset.filter(category__slug=category_slug)

                # 应用标签过滤
                tags = self.request.query_params.get('tags')
                if tags:
                    tag_list = tags.split(',')
                    queryset = queryset.filter(tags__slug__in=tag_list).distinct()

                # 应用时间过滤
                days = self.request.query_params.get('days')
                if days:
                    start_date = timezone.now() - timedelta(days=int(days))
                    queryset = queryset.filter(created_at__gte=start_date)

                # 预加载关联数据
                queryset = queryset.select_related('author', 'category').prefetch_related('tags')

                return queryset

            def get_permissions(self):
                """根据动作获取权限"""
                if self.action in ['update', 'partial_update', 'destroy']:
                    permission_classes = [IsOwnerOrReadOnly]
                elif self.action in ['create']:
                    permission_classes = [permissions.IsAuthenticated]
                else:
                    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

                return [permission() for permission in permission_classes]

            def perform_create(self, serializer):
                """创建文章时的预处理"""
                # 自动生成slug
                title = serializer.validated_data.get('title')
                if title and not serializer.validated_data.get('slug'):
                    from django.utils.text import slugify
                    slug = slugify(title)

                    # 确保slug唯一
                    original_slug = slug
                    counter = 1
                    while Article.objects.filter(slug=slug).exists():
                        slug = f"{original_slug}-{counter}"
                        counter += 1

                    serializer.validated_data['slug'] = slug

                # 设置作者
                serializer.validated_data['author'] = self.request.user

                # 自动生成摘要
                content = serializer.validated_data.get('content')
                if content and not serializer.validated_data.get('excerpt'):
                    excerpt = content[:200] + '...' if len(content) > 200 else content
                    serializer.validated_data['excerpt'] = excerpt

                super().perform_create(serializer)

            @action(detail=True, methods=['post'])
            def like(self, request, pk=None):
                """点赞文章"""
                article = self.get_object()

                if not request.user.is_authenticated:
                    return Response({
                        'success': False,
                        'message': '需要登录才能点赞'
                    }, status=status.HTTP_401_UNAUTHORIZED)

                # 检查是否已经点赞
                if article.likes.filter(user=request.user).exists():
                    return Response({
                        'success': False,
                        'message': '您已经点赞过这篇文章'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 创建点赞记录
                from .models import ArticleLike
                ArticleLike.objects.create(user=request.user, article=article)

                # 更新点赞数
                article.like_count += 1
                article.save(update_fields=['like_count'])

                return Response({
                    'success': True,
                    'message': '点赞成功',
                    'data': {
                        'like_count': article.like_count,
                        'is_liked': True
                    }
                })

            @action(detail=True, methods=['post'])
            def unlike(self, request, pk=None):
                """取消点赞"""
                article = self.get_object()

                if not request.user.is_authenticated:
                    return Response({
                        'success': False,
                        'message': '需要登录才能取消点赞'
                    }, status=status.HTTP_401_UNAUTHORIZED)

                # 删除点赞记录
                from .models import ArticleLike
                deleted_count, _ = ArticleLike.objects.filter(
                    user=request.user,
                    article=article
                ).delete()

                if deleted_count == 0:
                    return Response({
                        'success': False,
                        'message': '您还没有点赞过这篇文章'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 更新点赞数
                article.like_count = max(0, article.like_count - 1)
                article.save(update_fields=['like_count'])

                return Response({
                    'success': True,
                    'message': '取消点赞成功',
                    'data': {
                        'like_count': article.like_count,
                        'is_liked': False
                    }
                })

            @action(detail=True, methods=['post'])
            def publish(self, request, pk=None):
                """发布文章"""
                article = self.get_object()

                # 权限检查
                if article.author != request.user and not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只能发布自己的文章'
                    }, status=status.HTTP_403_FORBIDDEN)

                # 状态检查
                if article.status == 'published':
                    return Response({
                        'success': False,
                        'message': '文章已经发布'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 发布文章
                article.status = 'published'
                article.published_at = timezone.now()
                article.save(update_fields=['status', 'published_at'])

                return Response({
                    'success': True,
                    'message': '文章发布成功',
                    'data': {
                        'status': article.status,
                        'published_at': article.published_at.isoformat()
                    }
                })

            @action(detail=True, methods=['post'])
            def unpublish(self, request, pk=None):
                """取消发布文章"""
                article = self.get_object()

                # 权限检查
                if article.author != request.user and not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只能取消发布自己的文章'
                    }, status=status.HTTP_403_FORBIDDEN)

                # 状态检查
                if article.status != 'published':
                    return Response({
                        'success': False,
                        'message': '文章尚未发布'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 取消发布
                article.status = 'draft'
                article.save(update_fields=['status'])

                return Response({
                    'success': True,
                    'message': '文章已取消发布',
                    'data': {
                        'status': article.status
                    }
                })

            @action(detail=True, methods=['get'])
            def comments(self, request, pk=None):
                """获取文章评论"""
                article = self.get_object()

                # 获取评论查询集
                comments = Comment.objects.filter(
                    article=article,
                    is_approved=True
                ).select_related('author').order_by('created_at')

                # 分页
                from .pagination import CommentPagination
                paginator = CommentPagination()
                page = paginator.paginate_queryset(comments, request)

                if page is not None:
                    serializer = CommentSerializer(page, many=True, context={'request': request})
                    return paginator.get_paginated_response({
                        'success': True,
                        'message': '获取评论成功',
                        'data': serializer.data
                    })

                serializer = CommentSerializer(comments, many=True, context={'request': request})
                return Response({
                    'success': True,
                    'message': '获取评论成功',
                    'data': serializer.data
                })

            @action(detail=True, methods=['post'])
            def add_comment(self, request, pk=None):
                """添加评论"""
                article = self.get_object()

                if not request.user.is_authenticated:
                    return Response({
                        'success': False,
                        'message': '需要登录才能评论'
                    }, status=status.HTTP_401_UNAUTHORIZED)

                data = request.data.copy()
                data['article'] = article.id
                data['author'] = request.user.id

                # 检查评论频率限制
                self.check_comment_rate_limit()

                serializer = CommentSerializer(data=data, context={'request': request})
                if serializer.is_valid():
                    # 根据用户权限设置审核状态
                    is_approved = request.user.is_staff
                    comment = serializer.save(is_approved=is_approved)

                    return Response({
                        'success': True,
                        'message': '评论提交成功' + (',等待审核' if not is_approved else ''),
                        'data': serializer.data
                    }, status=status.HTTP_201_CREATED)
                else:
                    return Response({
                        'success': False,
                        'message': '评论提交失败',
                        'errors': serializer.errors
                    }, status=status.HTTP_400_BAD_REQUEST)

            def check_comment_rate_limit(self):
                """检查评论频率限制"""
                from django.core.cache import cache
                import time

                user_id = self.request.user.id
                cache_key = f"comment_limit_{user_id}"
                current_time = int(time.time())
                window_start = current_time - 300  # 5分钟窗口

                # 获取最近评论记录
                comments = cache.get(cache_key, [])
                recent_comments = [c for c in comments if c > window_start]

                # 限制5分钟内最多评论10次
                if len(recent_comments) >= 10:
                    return Response({
                        'success': False,
                        'message': '评论频率过高,请稍后再试'
                    }, status=status.HTTP_429_TOO_MANY_REQUESTS)

                # 记录本次评论
                recent_comments.append(current_time)
                cache.set(cache_key, recent_comments, 300)

            @action(detail=False, methods=['get'])
            def statistics(self, request):
                """获取文章统计信息"""
                from django.db.models import Count, Avg, Sum

                # 权限检查
                if not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只有管理员可以查看统计信息'
                    }, status=status.HTTP_403_FORBIDDEN)

                # 基础统计
                stats = {
                    'total_articles': Article.objects.count(),
                    'published_articles': Article.objects.filter(status='published').count(),
                    'draft_articles': Article.objects.filter(status='draft').count(),
                    'total_views': Article.objects.aggregate(
                        total_views=Sum('view_count')
                    )['total_views'] or 0,
                    'total_likes': Article.objects.aggregate(
                        total_likes=Sum('like_count')
                    )['total_likes'] or 0,
                    'avg_likes_per_article': Article.objects.aggregate(
                        avg_likes=Avg('like_count')
                    )['avg_likes'] or 0,
                }

                # 作者统计
                author_stats = Article.objects.values('author__username').annotate(
                    article_count=Count('id'),
                    total_views=Sum('view_count'),
                    total_likes=Sum('like_count')
                ).order_by('-article_count')[:10]

                stats['top_authors'] = list(author_stats)

                # 分类统计
                category_stats = Category.objects.annotate(
                    article_count=Count('articles')
                ).order_by('-article_count')[:10]

                stats['top_categories'] = [
                    {
                        'name': cat.name,
                        'article_count': cat.article_count
                    }
                    for cat in category_stats
                ]

                return Response({
                    'success': True,
                    'message': '获取统计信息成功',
                    'data': stats
                })

            @action(detail=False, methods=['get'])
            def popular(self, request):
                """获取热门文章"""
                days = int(request.query_params.get('days', 7))
                limit = int(request.query_params.get('limit', 10))

                start_date = timezone.now() - timedelta(days=days)

                popular_articles = Article.objects.filter(
                    status='published',
                    published_at__gte=start_date
                ).order_by('-view_count', '-like_count', '-created_at')[:limit]

                serializer = self.get_serializer(popular_articles, many=True)
                return Response({
                    'success': True,
                    'message': f'获取最近{days}天热门文章成功',
                    'data': serializer.data
                })

        class CategoryViewSet(BaseViewSet):
            """分类ViewSet"""

            queryset = Category.objects.all()
            serializer_class = CategorySerializer
            lookup_field = 'slug'
            filter_fields = ['parent', 'is_active']
            search_fields = ['name', 'description']
            ordering_fields = ['name', 'created_at', 'article_count']
            ordering = ['name']

            def get_queryset(self):
                """获取分类查询集"""
                queryset = super().get_queryset()

                # 对于非管理员,只显示激活的分类
                if not self.request.user.is_staff:
                    queryset = queryset.filter(is_active=True)

                # 添加文章数量统计
                queryset = queryset.annotate(
                    article_count=Count('articles', filter=Q(articles__status='published'))
                )

                return queryset

            def get_permissions(self):
                """根据动作获取权限"""
                if self.action in ['create', 'update', 'partial_update', 'destroy']:
                    permission_classes = [permissions.IsStaffOrReadOnly]
                else:
                    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

                return [permission() for permission in permission_classes]

            def perform_create(self, serializer):
                """创建分类时的预处理"""
                # 自动生成slug
                name = serializer.validated_data.get('name')
                if name and not serializer.validated_data.get('slug'):
                    from django.utils.text import slugify
                    slug = slugify(name)

                    # 确保slug唯一
                    original_slug = slug
                    counter = 1
                    while Category.objects.filter(slug=slug).exists():
                        slug = f"{original_slug}-{counter}"
                        counter += 1

                    serializer.validated_data['slug'] = slug

                super().perform_create(serializer)

            @action(detail=True, methods=['get'])
            def articles(self, request, slug=None):
                """获取分类下的文章"""
                category = self.get_object()

                # 获取该分类下的文章
                articles = Article.objects.filter(
                    category=category,
                    status='published'
                ).select_related('author').order_by('-published_at')

                # 分页
                page = self.paginate_queryset(articles)
                if page is not None:
                    serializer = ArticleListSerializer(page, many=True, context={'request': request})
                    return self.get_paginated_response({
                        'success': True,
                        'message': f'获取分类"{category.name}"下的文章成功',
                        'data': serializer.data
                    })

                serializer = ArticleListSerializer(articles, many=True, context={'request': request})
                return Response({
                    'success': True,
                    'message': f'获取分类"{category.name}"下的文章成功',
                    'data': serializer.data
                })

            @action(detail=True, methods=['get'])
            def subcategories(self, request, slug=None):
                """获取子分类"""
                category = self.get_object()

                subcategories = Category.objects.filter(
                    parent=category,
                    is_active=True
                ).annotate(
                    article_count=Count('articles', filter=Q(articles__status='published'))
                ).order_by('name')

                serializer = self.get_serializer(subcategories, many=True)
                return Response({
                    'success': True,
                    'message': f'获取分类"{category.name}"的子分类成功',
                    'data': serializer.data
                })

        class TagViewSet(BaseViewSet):
            """标签ViewSet"""

            queryset = Tag.objects.all()
            serializer_class = TagSerializer
            lookup_field = 'slug'
            search_fields = ['name', 'description']
            ordering_fields = ['name', 'created_at', 'article_count']
            ordering = ['name']

            def get_queryset(self):
                """获取标签查询集"""
                queryset = super().get_queryset()

                # 按使用频率排序
                sort_by_usage = request.query_params.get('sort_by_usage', 'false').lower() == 'true'
                if sort_by_usage:
                    queryset = queryset.annotate(
                        article_count=Count('articles', filter=Q(articles__status='published'))
                    ).order_by('-article_count', 'name')
                else:
                    queryset = queryset.annotate(
                        article_count=Count('articles', filter=Q(articles__status='published'))
                    )

                return queryset

            @action(detail=True, methods=['get'])
            def articles(self, request, slug=None):
                """获取标签下的文章"""
                tag = self.get_object()

                articles = Article.objects.filter(
                    tags=tag,
                    status='published'
                ).select_related('author').order_by('-published_at')

                # 分页
                page = self.paginate_queryset(articles)
                if page is not None:
                    serializer = ArticleListSerializer(page, many=True, context={'request': request})
                    return self.get_paginated_response({
                        'success': True,
                        'message': f'获取标签"{tag.name}"下的文章成功',
                        'data': serializer.data
                    })

                serializer = ArticleListSerializer(articles, many=True, context={'request': request})
                return Response({
                    'success': True,
                    'message': f'获取标签"{tag.name}"下的文章成功',
                    'data': serializer.data
                })

            @action(detail=False, methods=['get'])
            def popular(self, request):
                """获取热门标签"""
                limit = int(request.query_params.get('limit', 20))

                popular_tags = Tag.objects.annotate(
                    article_count=Count('articles', filter=Q(articles__status='published'))
                ).filter(article_count__gt=0).order_by('-article_count')[:limit]

                serializer = self.get_serializer(popular_tags, many=True)
                return Response({
                    'success': True,
                    'message': '获取热门标签成功',
                    'data': serializer.data
                })

            @action(detail=False, methods=['get'])
            def cloud(self, request):
                """获取标签云数据"""
                tags = Tag.objects.annotate(
                    article_count=Count('articles', filter=Q(articles__status='published'))
                ).filter(article_count__gt=0)

                # 计算权重(用于标签云显示)
                max_count = max(tag.article_count for tag in tags) if tags else 1
                min_count = min(tag.article_count for tag in tags) if tags else 0
                count_range = max_count - min_count if max_count > min_count else 1

        tag_cloud_data = []
        for tag in tags:
            # 权重范围 1-10
            weight = 1 + int((tag.article_count - min_count) * 9 / count_range)
            tag_cloud_data.append({
                'name': tag.name,
                'slug': tag.slug,
                'count': tag.article_count,
                'weight': weight,
                'color': tag.color
            })

        return Response({
            'success': True,
            'message': '获取标签云数据成功',
            'data': tag_cloud_data
        })

        class CommentViewSet(BaseViewSet):
            """评论ViewSet"""

            queryset = Comment.objects.all()
            serializer_class = CommentSerializer
            filter_fields = ['article', 'author', 'is_approved']
            search_fields = ['content']
            ordering_fields = ['created_at', 'updated_at']
            ordering = ['-created_at']

            def get_queryset(self):
                """获取评论查询集"""
                queryset = super().get_queryset()

                # 对于非管理员,只显示已审核的评论
                if not self.request.user.is_staff:
                    queryset = queryset.filter(is_approved=True)

                return queryset.select_related('author', 'article')

            def get_permissions(self):
                """根据动作获取权限"""
                if self.action in ['update', 'partial_update', 'destroy']:
                    permission_classes = [IsOwnerOrReadOnly]
                elif self.action == 'create':
                    permission_classes = [permissions.IsAuthenticated]
                else:
                    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

                return [permission() for permission in permission_classes]

            def perform_create(self, serializer):
                """创建评论时的预处理"""
                # 设置作者
                if self.request.user.is_authenticated:
                    serializer.validated_data['author'] = self.request.user

                # 根据用户权限设置审核状态
                is_approved = self.request.user.is_staff
                serializer.validated_data['is_approved'] = is_approved

                super().perform_create(serializer)

            @action(detail=True, methods=['post'])
            def approve(self, request, pk=None):
                """审核评论"""
                comment = self.get_object()

                # 权限检查
                if not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只有管理员可以审核评论'
                    }, status=status.HTTP_403_FORBIDDEN)

                if comment.is_approved:
                    return Response({
                        'success': False,
                        'message': '评论已经审核通过'
                    }, status=status.HTTP_400_BAD_REQUEST)

                comment.is_approved = True
                comment.save(update_fields=['is_approved'])

                return Response({
                    'success': True,
                    'message': '评论审核通过',
                    'data': {
                        'is_approved': True
                    }
                })

            @action(detail=True, methods=['post'])
            def reject(self, request, pk=None):
                """拒绝评论"""
                comment = self.get_object()

                # 权限检查
                if not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只有管理员可以审核评论'
                    }, status=status.HTTP_403_FORBIDDEN)

                if not comment.is_approved:
                    return Response({
                        'success': False,
                        'message': '评论尚未审核'
                    }, status=status.HTTP_400_BAD_REQUEST)

                comment.is_approved = False
                comment.save(update_fields=['is_approved'])

                return Response({
                    'success': True,
                    'message': '评论审核拒绝',
                    'data': {
                        'is_approved': False
                    }
                })

            @action(detail=True, methods=['post'])
            def reply(self, request, pk=None):
                """回复评论"""
                parent_comment = self.get_object()

                data = request.data.copy()
                data['article'] = parent_comment.article.id
                data['parent'] = parent_comment.id

                if request.user.is_authenticated:
                    data['author'] = request.user.id

                serializer = CommentSerializer(data=data, context={'request': request})
                if serializer.is_valid():
                    # 根据用户权限设置审核状态
                    is_approved = request.user.is_staff
                    comment = serializer.save(is_approved=is_approved)

                    return Response({
                        'success': True,
                        'message': '回复评论成功' + (',等待审核' if not is_approved else ''),
                        'data': serializer.data
                    }, status=status.HTTP_201_CREATED)
                else:
                    return Response({
                        'success': False,
                        'message': '回复评论失败',
                        'errors': serializer.errors
                    }, status=status.HTTP_400_BAD_REQUEST)

            @action(detail=False, methods=['get'])
            def pending(self, request):
                """获取待审核评论"""
                # 权限检查
                if not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只有管理员可以查看待审核评论'
                    }, status=status.HTTP_403_FORBIDDEN)

                pending_comments = Comment.objects.filter(is_approved=False).order_by('-created_at')

                # 分页
                page = self.paginate_queryset(pending_comments)
                if page is not None:
                    serializer = self.get_serializer(page, many=True)
                    return self.get_paginated_response({
                        'success': True,
                        'message': '获取待审核评论成功',
                        'data': serializer.data
                    })

                serializer = self.get_serializer(pending_comments, many=True)
                return Response({
                    'success': True,
                    'message': '获取待审核评论成功',
                    'data': serializer.data
                })

02.ModelViewSet
    a.说明
        CRUD操作自动生成
        权限控制集成
        自定义动作方法
    b.示例
        # apps/blog/model_viewsets.py
        from rest_framework import viewsets, permissions, status, filters
        from rest_framework.decorators import action
        from rest_framework.response import Response
        from django_filters.rest_framework import DjangoFilterBackend
        from django.db.models import Q, F, Count, Avg, Sum
        from django.utils import timezone
        from datetime import timedelta
        import logging

        from .models import Article, Category, Tag, Comment, User, UserProfile
        from .serializers import *
        from .permissions import *
        from .exceptions import *
        from .pagination import *
        from .tasks import async_article_processing

        logger = logging.getLogger('api')

        class UserViewSet(viewsets.ModelViewSet):
            """用户ViewSet - 管理用户资料"""

        queryset = User.objects.all()
        serializer_class = UserSerializer
        lookup_field = 'username'
        filter_backends = [filters.SearchFilter, filters.OrderingFilter]
        search_fields = ['username', 'email', 'first_name', 'last_name']
        ordering_fields = ['username', 'date_joined', 'last_login']
        ordering = ['-date_joined']

        def get_queryset(self):
            """获取用户查询集"""
            queryset = super().get_queryset()

            # 应用过滤条件
            is_active = self.request.query_params.get('is_active')
            if is_active is not None:
                queryset = queryset.filter(is_active=is_active.lower() == 'true')

            is_staff = self.request.query_params.get('is_staff')
            if is_staff is not None:
                queryset = queryset.filter(is_staff=is_staff.lower() == 'true')

            return queryset

        def get_permissions(self):
            """根据动作获取权限"""
            if self.action in ['create']:
                permission_classes = [permissions.AllowAny]  # 注册
            elif self.action in ['list', 'retrieve']:
                permission_classes = [permissions.IsAuthenticatedOrReadOnly]
            elif self.action in ['update', 'partial_update']:
                permission_classes = [IsOwnerOrStaff]
            elif self.action == 'destroy':
                permission_classes = [permissions.IsAdminUser]
            else:
                permission_classes = [permissions.IsAuthenticated]

            return [permission() for permission in permission_classes]

        def get_serializer_class(self):
            """根据动作选择序列化器"""
            if self.action == 'create':
                return UserRegistrationSerializer
            elif self.action == 'update' or self.action == 'partial_update':
                return UserUpdateSerializer
            elif self.action == 'me':
                return UserProfileSerializer
            return self.serializer_class

        @action(detail=False, methods=['get', 'put', 'patch'])
        def me(self, request):
            """获取或更新当前用户信息"""
            if not request.user.is_authenticated:
                return Response({
                    'success': False,
                    'message': '需要登录'
                }, status=status.HTTP_401_UNAUTHORIZED)

            user = request.user

            if request.method == 'GET':
                serializer = UserProfileSerializer(user, context={'request': request})
                return Response({
                    'success': True,
                    'message': '获取用户信息成功',
                    'data': serializer.data
                })

            elif request.method in ['PUT', 'PATCH']:
                partial = request.method == 'PATCH'
                serializer = UserProfileSerializer(
                    user, data=request.data, partial=partial,
                    context={'request': request}
                )

                if serializer.is_valid():
                    serializer.save()
                    return Response({
                        'success': True,
                        'message': '用户信息更新成功',
                        'data': serializer.data
                    })
                else:
                    return Response({
                        'success': False,
                        'message': '用户信息更新失败',
                        'errors': serializer.errors
                    }, status=status.HTTP_400_BAD_REQUEST)

        @action(detail=True, methods=['post'])
        def follow(self, request, username=None):
            """关注用户"""
            if not request.user.is_authenticated:
                return Response({
                    'success': False,
                    'message': '需要登录才能关注用户'
                }, status=status.HTTP_401_UNAUTHORIZED)

            target_user = self.get_object()

            # 不能关注自己
            if target_user == request.user:
                return Response({
                    'success': False,
                    'message': '不能关注自己'
                }, status=status.HTTP_400_BAD_REQUEST)

            # 检查是否已经关注
            if target_user.followers.filter(id=request.user.id).exists():
                return Response({
                    'success': False,
                    'message': '您已经关注了该用户'
                }, status=status.HTTP_400_BAD_REQUEST)

            # 创建关注关系
            target_user.followers.add(request.user)

            return Response({
                'success': True,
                'message': '关注成功',
                'data': {
                    'is_following': True,
                    'followers_count': target_user.followers.count()
                }
            })

        @action(detail=True, methods=['post'])
        def unfollow(self, request, username=None):
            """取消关注用户"""
            if not request.user.is_authenticated:
                return Response({
                    'success': False,
                    'message': '需要登录才能取消关注'
                }, status=status.HTTP_401_UNAUTHORIZED)

            target_user = self.get_object()

            # 检查是否已关注
            if not target_user.followers.filter(id=request.user.id).exists():
                return Response({
                    'success': False,
                    'message': '您尚未关注该用户'
                }, status=status.HTTP_400_BAD_REQUEST)

            # 删除关注关系
            target_user.followers.remove(request.user)

            return Response({
                'success': True,
                'message': '取消关注成功',
                'data': {
                    'is_following': False,
                    'followers_count': target_user.followers.count()
                }
            })

        @action(detail=True, methods=['get'])
        def articles(self, request, username=None):
            """获取用户的文章"""
            user = self.get_object()

            # 获取查询参数
            status_filter = request.query_params.get('status', 'published')
            page = int(request.query_params.get('page', 1))
            per_page = int(request.query_params.get('per_page', 10))

            # 构建查询
            queryset = Article.objects.filter(author=user)

            # 状态过滤
            if not request.user.is_staff and user != request.user:
                queryset = queryset.filter(status='published')
            elif status_filter:
                queryset = queryset.filter(status=status_filter)

            # 排序
            queryset = queryset.order_by('-created_at')

            # 分页
            paginator = ArticlePagination()
            page_obj = paginator.paginate_queryset(queryset, request)

            if page_obj is not None:
                serializer = ArticleListSerializer(page_obj, many=True, context={'request': request})
                return paginator.get_paginated_response({
                    'success': True,
                    'message': f'获取用户"{user.username}"的文章成功',
                    'data': serializer.data
                })

            serializer = ArticleListSerializer(queryset, many=True, context={'request': request})
            return Response({
                'success': True,
                'message': f'获取用户"{user.username}"的文章成功',
                'data': serializer.data
            })

        @action(detail=True, methods=['get'])
        def followers(self, request, username=None):
            """获取用户的关注者"""
            user = self.get_object()

            # 获取关注者列表
            followers = user.followers.all().order_by('-date_joined')

            # 分页
            page = self.paginate_queryset(followers)
            if page is not None:
                serializer = UserSerializer(page, many=True, context={'request': request})
                return self.get_paginated_response({
                    'success': True,
                    'message': f'获取用户"{user.username}"的关注者成功',
                    'data': serializer.data
                })

            serializer = UserSerializer(followers, many=True, context={'request': request})
            return Response({
                'success': True,
                'message': f'获取用户"{user.username}"的关注者成功',
                'data': serializer.data
            })

        @action(detail=True, methods=['get'])
        def following(self, request, username=None):
            """获取用户关注的用户"""
            user = self.get_object()

            # 获取关注的用户列表
            following = user.following.all().order_by('-date_joined')

            # 分页
            page = self.paginate_queryset(following)
            if page is not None:
                serializer = UserSerializer(page, many=True, context={'request': request})
                return self.get_paginated_response({
                    'success': True,
                    'message': f'获取用户"{user.username}"关注的用户成功',
                    'data': serializer.data
                })

            serializer = UserSerializer(following, many=True, context={'request': request})
            return Response({
                'success': True,
                'message': f'获取用户"{user.username}"关注的用户成功',
                'data': serializer.data
            })

        @action(detail=False, methods=['get'])
        def statistics(self, request):
            """获取用户统计信息"""
            if not request.user.is_staff:
                return Response({
                    'success': False,
                    'message': '只有管理员可以查看用户统计'
                }, status=status.HTTP_403_FORBIDDEN)

            from django.db.models import Count, Sum, Avg

            # 基础统计
            stats = {
                'total_users': User.objects.count(),
                'active_users': User.objects.filter(is_active=True).count(),
                'staff_users': User.objects.filter(is_staff=True).count(),
                'new_users_today': User.objects.filter(
                    date_joined__date=timezone.now().date()
                ).count(),
                'new_users_this_week': User.objects.filter(
                    date_joined__gte=timezone.now() - timedelta(days=7)
                ).count(),
                'new_users_this_month': User.objects.filter(
                    date_joined__gte=timezone.now() - timedelta(days=30)
                ).count(),
            }

            # 用户活跃度统计
            active_stats = User.objects.aggregate(
                avg_articles_per_user=Avg('articles__count'),
                total_articles=Sum('articles__count'),
                avg_comments_per_user=Avg('comments__count'),
                total_comments=Sum('comments__count')
            )

            stats.update(active_stats)

            # 热门用户(按文章数)
            top_authors = User.objects.annotate(
                article_count=Count('articles', filter=Q(articles__status='published'))
            ).filter(article_count__gt=0).order_by('-article_count')[:10]

            stats['top_authors'] = [
                {
                    'username': user.username,
                    'article_count': user.article_count
                }
                for user in top_authors
            ]

            return Response({
                'success': True,
                'message': '获取用户统计信息成功',
                'data': stats
            })

        class ExtendedArticleViewSet(viewsets.ModelViewSet):
            """扩展文章ViewSet - 包含更多高级功能"""

            queryset = Article.objects.all()
            serializer_class = ArticleSerializer
            list_serializer_class = ArticleListSerializer
            detail_serializer_class = ArticleDetailSerializer
            lookup_field = 'slug'

            filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
            filter_fields = ['category', 'tags', 'author', 'status', 'is_featured']
            search_fields = ['title', 'content', 'excerpt']
            ordering_fields = ['created_at', 'updated_at', 'view_count', 'like_count', 'published_at']
            ordering = ['-created_at']

            def get_queryset(self):
                """获取增强的文章查询集"""
                queryset = Article.objects.select_related('author', 'category').prefetch_related('tags')

                # 状态过滤
                if not self.request.user.is_staff:
                    queryset = queryset.filter(status='published')

                # 时间过滤
                date_from = self.request.query_params.get('date_from')
                date_to = self.request.query_params.get('date_to')

                if date_from:
                    queryset = queryset.filter(created_at__date__gte=date_from)
                if date_to:
                    queryset = queryset.filter(created_at__date__lte=date_to)

                # 作者过滤
                author_id = self.request.query_params.get('author_id')
                if author_id:
                    queryset = queryset.filter(author_id=author_id)

                # 标签过滤(多选)
                tag_ids = self.request.query_params.getlist('tag_ids')
                if tag_ids:
                    queryset = queryset.filter(tags__id__in=tag_ids).distinct()

                # 内容长度过滤
                min_length = self.request.query_params.get('min_length')
                max_length = self.request.query_params.get('max_length')

                if min_length:
                    queryset = queryset.filter(content__length__gte=int(min_length))
                if max_length:
                    queryset = queryset.filter(content__length__lte=int(max_length))

                return queryset

            def get_permissions(self):
                """根据动作获取权限"""
                if self.action in ['list', 'retrieve', 'popular', 'featured']:
                    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
                elif self.action == 'create':
                    permission_classes = [permissions.IsAuthenticated]
                elif self.action in ['update', 'partial_update', 'publish', 'unpublish']:
                    permission_classes = [IsOwnerOrReadOnly]
                elif self.action == 'destroy':
                    permission_classes = [IsOwnerOrAdmin]
                else:
                    permission_classes = [permissions.IsAuthenticated]

                return [permission() for permission in permission_classes]

            def get_serializer_class(self):
                """根据动作选择序列化器"""
                if self.action == 'list':
                    return self.list_serializer_class
                elif self.action in ['retrieve', 'update', 'partial_update']:
                    return self.detail_serializer_class
                elif self.action == 'create':
                    return ArticleCreateSerializer
                return self.serializer_class

            def perform_create(self, serializer):
                """创建文章的增强处理"""
                # 预处理数据
                data = serializer.validated_data

                # 自动生成slug
                if data.get('title') and not data.get('slug'):
                    from django.utils.text import slugify
                    slug = slugify(data['title'])

                    # 确保slug唯一
                    original_slug = slug
                    counter = 1
                    while Article.objects.filter(slug=slug).exists():
                        slug = f"{original_slug}-{counter}"
                        counter += 1

                    data['slug'] = slug

                # 自动生成摘要
                if data.get('content') and not data.get('excerpt'):
                    content = data['content']
                    excerpt = content[:200] + '...' if len(content) > 200 else content
                    data['excerpt'] = excerpt

                # 设置作者和状态
                data['author'] = self.request.user
                if not self.request.user.is_staff:
                    data['status'] = 'draft'  # 普通用户创建的文章默认为草稿

                # 保存文章
                article = serializer.save()

                # 异步处理(如生成摘要、图片处理等)
                async_article_processing.delay(article.id)

                # 记录操作日志
                logger.info(f"文章创建成功: {article.title} by {self.request.user.username}")

            @action(detail=True, methods=['post'])
            def duplicate(self, request, slug=None):
                """复制文章"""
                original_article = self.get_object()

                # 权限检查
                if original_article.author != request.user and not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只能复制自己的文章'
                    }, status=status.HTTP_403_FORBIDDEN)

                # 创建副本
                new_title = f"{original_article.title} (副本)"
                new_slug = f"{original_article.slug}-copy"

                # 确保slug唯一
                counter = 1
                while Article.objects.filter(slug=new_slug).exists():
                    new_slug = f"{original_article.slug}-copy-{counter}"
                    counter += 1

                duplicated_article = Article.objects.create(
                    title=new_title,
                    slug=new_slug,
                    content=original_article.content,
                    excerpt=original_article.excerpt,
                    author=request.user,
                    category=original_article.category,
                    status='draft',  # 副本默认为草稿
                    featured_image=original_article.featured_image,
                )

                # 复制标签
                duplicated_article.tags.set(original_article.tags.all())

                serializer = self.get_serializer(duplicated_article)
                return Response({
                    'success': True,
                    'message': '文章复制成功',
                    'data': serializer.data
                }, status=status.HTTP_201_CREATED)

            @action(detail=True, methods=['post'])
            def archive(self, request, slug=None):
                """归档文章"""
                article = self.get_object()

                # 权限检查
                if article.author != request.user and not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只能归档自己的文章'
                    }, status=status.HTTP_403_FORBIDDEN)

                if article.status == 'archived':
                    return Response({
                        'success': False,
                        'message': '文章已经归档'
                    }, status=status.HTTP_400_BAD_REQUEST)

                article.status = 'archived'
                article.save(update_fields=['status'])

                return Response({
                    'success': True,
                    'message': '文章归档成功',
                    'data': {
                        'status': article.status
                    }
                })

            @action(detail=True, methods=['post'])
            def unarchive(self, request, slug=None):
                """取消归档文章"""
                article = self.get_object()

                # 权限检查
                if article.author != request.user and not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只能取消归档自己的文章'
                    }, status=status.HTTP_403_FORBIDDEN)

                if article.status != 'archived':
                    return Response({
                        'success': False,
                        'message': '文章尚未归档'
                    }, status=status.HTTP_400_BAD_REQUEST)

                article.status = 'draft'
                article.save(update_fields=['status'])

                return Response({
                    'success': True,
                    'message': '文章取消归档成功',
                    'data': {
                        'status': article.status
                    }
                })

            @action(detail=False, methods=['get'])
            def featured(self, request):
                """获取推荐文章"""
                queryset = self.get_queryset().filter(is_featured=True)

                # 分页
                page = self.paginate_queryset(queryset)
                if page is not None:
                    serializer = self.get_serializer(page, many=True)
                    return self.get_paginated_response({
                        'success': True,
                        'message': '获取推荐文章成功',
                        'data': serializer.data
                    })

                serializer = self.get_serializer(queryset, many=True)
                return Response({
                    'success': True,
                    'message': '获取推荐文章成功',
                    'data': serializer.data
                })

            @action(detail=False, methods=['post'])
            def bulk_update_status(self, request):
                """批量更新文章状态"""
                article_ids = request.data.get('article_ids', [])
                new_status = request.data.get('status')

                if not article_ids or not new_status:
                    return Response({
                        'success': False,
                        'message': '文章ID列表和状态不能为空'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 权限检查
                if not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只有管理员可以批量更新状态'
                    }, status=status.HTTP_403_FORBIDDEN)

                # 验证状态
                valid_statuses = ['draft', 'published', 'archived']
                if new_status not in valid_statuses:
                    return Response({
                        'success': False,
                        'message': f'无效的状态,有效状态为: {", ".join(valid_statuses)}'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 更新状态
                updated_count = Article.objects.filter(id__in=article_ids).update(status=new_status)

                return Response({
                    'success': True,
                    'message': f'成功更新{updated_count}篇文章的状态',
                    'data': {
                        'updated_count': updated_count,
                        'status': new_status
                    }
                })

            @action(detail=True, methods=['get'])
            def analytics(self, request, slug=None):
                """获取文章分析数据"""
                article = self.get_object()

                # 权限检查
                if article.author != request.user and not request.user.is_staff:
                    return Response({
                        'success': False,
                        'message': '只能查看自己文章的分析数据'
                    }, status=status.HTTP_403_FORBIDDEN)

                from django.db.models import Count
        from django.utils import timezone
        from datetime import timedelta

        # 基础统计
        analytics = {
            'view_count': article.view_count,
            'like_count': article.like_count,
            'comment_count': Comment.objects.filter(article=article, is_approved=True).count(),
            'share_count': getattr(article, 'share_count', 0),
        }

        # 时间序列数据
        days = int(request.query_params.get('days', 30))
        start_date = timezone.now() - timedelta(days=days)

        # 每日浏览量
        daily_views = []
        for i in range(days):
            date = start_date + timedelta(days=i)
            date_str = date.strftime('%Y-%m-%d')

            # 这里应该从浏览记录表中统计
            daily_view_count = ArticleView.objects.filter(
                article=article,
                viewed_at__date=date.date()
            ).count()

            daily_views.append({
                'date': date_str,
                'views': daily_view_count
            })

        analytics['daily_views'] = daily_views

        # 读者分析
        reader_demographics = {
            'total_unique_readers': ArticleView.objects.filter(article=article).values('user').distinct().count(),
            'returning_readers': 0,  # 需要实现计算逻辑
            'new_readers': 0,        # 需要实现计算逻辑
        }

        analytics['reader_demographics'] = reader_demographics

        # 参与度分析
        engagement_metrics = {
            'like_rate': analytics['like_count'] / max(analytics['view_count'], 1) * 100,
            'comment_rate': analytics['comment_count'] / max(analytics['view_count'], 1) * 100,
            'avg_reading_time': getattr(article, 'avg_reading_time', 0),
        }

        analytics['engagement_metrics'] = engagement_metrics

        return Response({
            'success': True,
            'message': '获取文章分析数据成功',
            'data': analytics
        })

        class CustomViewSet(viewsets.ViewSet):
            """自定义ViewSet - 不绑定Model"""

            permission_classes = [permissions.IsAuthenticated]

            @action(detail=False, methods=['get'])
            def dashboard(self, request):
                """获取仪表板数据"""
                user = request.user

                # 用户统计
                user_stats = {
                    'articles_count': Article.objects.filter(author=user).count(),
                    'published_articles_count': Article.objects.filter(
                        author=user, status='published'
                    ).count(),
                    'draft_articles_count': Article.objects.filter(
                        author=user, status='draft'
                    ).count(),
                    'total_views': Article.objects.filter(author=user).aggregate(
                        total=Sum('view_count')
                    )['total'] or 0,
                    'total_likes': Article.objects.filter(author=user).aggregate(
                        total=Sum('like_count')
                    )['total'] or 0,
                }

                # 最近活动
                recent_articles = Article.objects.filter(
                    author=user
                ).order_by('-updated_at')[:5]

                recent_comments = Comment.objects.filter(
                    article__author=user
                ).order_by('-created_at')[:5]

                # 快速统计
                from django.utils import timezone
                from datetime import timedelta

                last_30_days = timezone.now() - timedelta(days=30)

                recent_stats = {
                    'articles_this_month': Article.objects.filter(
                        author=user,
                        created_at__gte=last_30_days
                    ).count(),
                    'views_this_month': Article.objects.filter(
                        author=user,
                        created_at__gte=last_30_days
                    ).aggregate(total=Sum('view_count'))['total'] or 0,
                    'comments_this_month': Comment.objects.filter(
                        article__author=user,
                        created_at__gte=last_30_days
                    ).count(),
                }

                dashboard_data = {
                    'user_stats': user_stats,
                    'recent_stats': recent_stats,
                    'recent_articles': ArticleListSerializer(
                        recent_articles, many=True, context={'request': request}
                    ).data,
                    'recent_comments': CommentSerializer(
                        recent_comments, many=True, context={'request': request}
                    ).data,
                }

                return Response({
                    'success': True,
                    'message': '获取仪表板数据成功',
                    'data': dashboard_data
                })

            @action(detail=False, methods=['get'])
            def notifications(self, request):
                """获取通知列表"""
                user = request.user

                # 获取通知类型
                notification_type = request.query_params.get('type', 'all')
                unread_only = request.query_params.get('unread_only', 'false').lower() == 'true'

                # 构建查询
                notifications = Notification.objects.filter(user=user)

                if notification_type != 'all':
                    notifications = notifications.filter(type=notification_type)

                if unread_only:
                    notifications = notifications.filter(is_read=False)

                notifications = notifications.order_by('-created_at')

                # 分页
                page = self.paginate_queryset(notifications)
                if page is not None:
                    serializer = NotificationSerializer(page, many=True)
                    return self.get_paginated_response({
                        'success': True,
                        'message': '获取通知列表成功',
                        'data': serializer.data
                    })

                serializer = NotificationSerializer(notifications, many=True)
                return Response({
                    'success': True,
                    'message': '获取通知列表成功',
                    'data': serializer.data
                })

            @action(detail=False, methods=['post'])
            def mark_notifications_read(self, request):
                """标记通知为已读"""
                user = request.user
                notification_ids = request.data.get('notification_ids', [])
                mark_all = request.data.get('mark_all', False)

                if mark_all:
                    # 标记所有通知为已读
                    updated_count = Notification.objects.filter(
                        user=user, is_read=False
                    ).update(is_read=True)
                else:
                    # 标记指定通知为已读
                    updated_count = Notification.objects.filter(
                        id__in=notification_ids, user=user
                    ).update(is_read=True)

                return Response({
                    'success': True,
                    'message': f'成功标记{updated_count}条通知为已读',
                    'data': {
                        'updated_count': updated_count
                    }
                })

            @action(detail=False, methods=['get'])
            def search(self, request):
                """全局搜索"""
                query = request.query_params.get('q', '').strip()
                search_type = request.query_params.get('type', 'all')

                if not query:
                    return Response({
                        'success': False,
                        'message': '搜索关键词不能为空'
                    }, status=status.HTTP_400_BAD_REQUEST)

                results = {
                    'articles': [],
                    'users': [],
                    'tags': [],
                    'categories': [],
                }

                # 搜索文章
                if search_type in ['all', 'articles']:
                    articles = Article.objects.filter(
                        Q(title__icontains=query) |
                        Q(content__icontains=query) |
                        Q(excerpt__icontains=query),
                        status='published'
                    ).select_related('author', 'category')[:10]

                    results['articles'] = ArticleListSerializer(
                        articles, many=True, context={'request': request}
                    ).data

                # 搜索用户
                if search_type in ['all', 'users']:
                    users = User.objects.filter(
                        Q(username__icontains=query) |
                        Q(first_name__icontains=query) |
                        Q(last_name__icontains=query)
                    )[:10]

                    results['users'] = UserSerializer(
                        users, many=True, context={'request': request}
                    ).data

                # 搜索标签
                if search_type in ['all', 'tags']:
                    tags = Tag.objects.filter(
                        Q(name__icontains=query) |
                        Q(description__icontains=query)
                    )[:10]

                    results['tags'] = TagSerializer(
                        tags, many=True
                    ).data

                # 搜索分类
                if search_type in ['all', 'categories']:
                    categories = Category.objects.filter(
                        Q(name__icontains=query) |
                        Q(description__icontains=query)
                    )[:10]

                    results['categories'] = CategorySerializer(
                        categories, many=True
                    ).data

                return Response({
                    'success': True,
                    'message': f'搜索"{query}"成功',
                    'data': {
                        'query': query,
                        'results': results,
                        'total_count': sum(len(items) for items in results.values())
                    }
                })

4.4 路由系统

01.SimpleRouter
    a.说明
        基础URL配置
        动作方法映射
        URL参数处理
    b.示例
        # apps/blog/urls.py
        from django.urls import path, include
        from rest_framework.routers import SimpleRouter, DefaultRouter
        from . import viewsets

        # 创建SimpleRouter实例
        router = SimpleRouter()

        # 注册ViewSet路由
        router.register(r'articles', viewsets.ArticleViewSet, basename='article')
        router.register(r'categories', viewsets.CategoryViewSet, basename='category')
        router.register(r'tags', viewsets.TagViewSet, basename='tag')
        router.register(r'comments', viewsets.CommentViewSet, basename='comment')
        router.register(r'users', viewsets.UserViewSet, basename='user')

        # 生成的URL模式:
        # articles/                    - list
        # articles/<slug>/             - retrieve
        # articles/<slug>/like/        - like (custom action)
        # articles/<slug>/publish/     - publish (custom action)
        # categories/                  - list
        # categories/<slug>/           - retrieve
        # categories/<slug>/articles/  - articles (custom action)
        # tags/                        - list
        # tags/<slug>/                 - retrieve
        # tags/<slug>/articles/        - articles (custom action)
        # comments/                    - list
        # comments/<id>/               - retrieve
        # comments/<id>/approve/       - approve (custom action)
        # users/                       - list
        # users/<username>/            - retrieve
        # users/<username>/articles/   - articles (custom action)
        # users/me/                    - me (custom action)

        urlpatterns = [
            path('api/v1/', include(router.urls)),
        ]

        # 路由详细信息
        print("Generated URLs:")
        for url_pattern in router.urls:
            print(f"{url_pattern.name}: {url_pattern.pattern}")

        # 自定义URL映射
        class CustomRouter(SimpleRouter):
            """自定义路由器"""

            def get_urls(self):
                """自定义URL生成"""
                urls = super().get_urls()

                # 添加额外的URL模式
        extra_urls = [
            path('api/v1/statistics/', viewsets.CustomViewSet.as_view({'get': 'statistics'}), name='statistics'),
            path('api/v1/search/', viewsets.CustomViewSet.as_view({'get': 'search'}), name='search'),
            path('api/v1/notifications/', viewsets.CustomViewSet.as_view({'get': 'notifications'}), name='notifications'),
        ]

                return extra_urls + urls

        # 使用自定义路由器
        custom_router = CustomRouter()
        custom_router.register(r'articles', viewsets.ArticleViewSet, basename='article')

        urlpatterns = [
            path('api/v1/', include(custom_router.urls)),
        ]

02.DefaultRouter
    a.说明
        自动生成根视图
        API浏览器支持
        响应格式选项
    b.示例
        # apps/blog/routers.py
        from rest_framework.routers import DefaultRouter
        from . import viewsets

        # 创建DefaultRouter实例
        router = DefaultRouter()

        # 注册ViewSet
        router.register(r'articles', viewsets.ArticleViewSet, basename='article')
        router.register(r'categories', viewsets.CategoryViewSet, basename='category')
        router.register(r'tags', viewsets.TagViewSet, basename='tag')
        router.register(r'comments', viewsets.CommentViewSet, basename='comment')
        router.register(r'users', viewsets.UserViewSet, basename='user')

        # 添加自定义根视图
        router.root_view_name = 'API Root'

        # 配置API文档
        router.APIRootView.title = 'Blog API'
        router.APIRootView.description = '博客系统API接口文档'

        # 版本化路由器
        class VersionedDefaultRouter(DefaultRouter):
            """版本化的DefaultRouter"""

            def __init__(self, version='v1', *args, **kwargs):
                self.version = version
                super().__init__(*args, **kwargs)

            def get_urls(self):
                """生成版本化的URL"""
                urls = super().get_urls()

                # 为每个URL添加版本前缀
                versioned_urls = []
                for url in urls:
                    # 将URL模式包装在版本路径中
                    versioned_url = path(f'api/{self.version}/', include([url]))
                    versioned_urls.append(versioned_url)

                return versioned_urls

        # 使用版本化路由器
        v1_router = VersionedDefaultRouter(version='v1')
        v1_router.register(r'articles', viewsets.ArticleViewSet, basename='article')
        v1_router.register(r'categories', viewsets.CategoryViewSet, basename='category')

        v2_router = VersionedDefaultRouter(version='v2')
        v2_router.register(r'articles', viewsets.ExtendedArticleViewSet, basename='article-v2')
        v2_router.register(r'users', viewsets.UserViewSet, basename='user-v2')

        # 多路由器组合
        class MultiVersionRouter:
            """多版本路由器管理"""

            def __init__(self):
                self.routers = {}

            def register_router(self, version, router):
                """注册版本路由器"""
                self.routers[version] = router

            def get_urls(self):
                """获取所有版本的URL"""
                urls = []
                for version, router in self.routers.items():
                    version_urls = router.get_urls()
                    urls.extend(version_urls)

                return urls

        # 使用多版本路由器
        multi_router = MultiVersionRouter()
        multi_router.register_router('v1', v1_router)
        multi_router.register_router('v2', v2_router)

        # 条件路由器
        class ConditionalRouter(DefaultRouter):
            """条件路由器"""

            def __init__(self, condition_func=None, *args, **kwargs):
                self.condition_func = condition_func
                super().__init__(*args, **kwargs)

            def get_urls(self):
                """根据条件生成URL"""
                if self.condition_func and not self.condition_func():
                    return []

                return super().get_urls()

        # 使用条件路由器(例如基于用户权限)
        def is_admin_user(request):
            """检查是否为管理员用户"""
            return request.user.is_authenticated and request.user.is_staff

        admin_router = ConditionalRouter(condition_func=is_admin_user)
        admin_router.register(r'admin/articles', viewsets.AdminArticleViewSet, basename='admin-article')

03.自定义路由
    a.说明
        动态路由生成
        参数化URL
        路由中间件
    b.示例
        # apps/blog/custom_routers.py
        from rest_framework.routers import DefaultRouter
        from rest_framework.urlpatterns import format_suffix_patterns
        django.conf.urls import url, include
        from django.urls import path
        from django.http import HttpResponse
        import re

        class DynamicRouter(DefaultRouter):
            """动态路由器"""

            def __init__(self, *args, **kwargs):
                self.dynamic_routes = []
                self.route_patterns = {}
                super().__init__(*args, **kwargs)

            def add_dynamic_route(self, pattern, viewset, basename=None):
                """添加动态路由"""
                self.dynamic_routes.append({
                    'pattern': pattern,
                    'viewset': viewset,
                    'basename': basename
                })

            def register_with_prefix(self, prefix, viewset, basename=None, dynamic_prefixes=None):
                """注册带动态前缀的路由"""
                super().register(prefix, viewset, basename)

                # 添加动态前缀
                if dynamic_prefixes:
                    for dyn_prefix in dynamic_prefixes:
                        full_prefix = f"{dyn_prefix}/{prefix}"
                        super().register(full_prefix, viewset, f"{basename}-{dyn_prefix}")

            def get_urls(self):
                """生成包含动态路由的URL"""
                urls = super().get_urls()

                # 添加动态路由URL
                for route in self.dynamic_routes:
                    viewset = route['viewset']
                    basename = route.get('basename')

                    # 为ViewSet的所有动作生成URL
                    viewset_urls = self.get_viewset_urls(viewset, route['pattern'], basename)
                    urls.extend(viewset_urls)

                return urls

            def get_viewset_urls(self, viewset, prefix, basename):
                """为ViewSet生成URL"""
                urls = []

                # 标准CRUD操作
                urls.append(url(r'^{}/$'.format(prefix), viewset.as_view({'get': 'list', 'post': 'create'}), name=f'{basename}-list'))
                urls.append(url(r'^{}/(?P<pk>[^/.]+)/$'.format(prefix), viewset.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}), name=f'{basename}-detail'))

                # 自定义动作
        for action_name, action_config in getattr(viewset, 'extra_actions', {}).items():
            if action_config.get('detail', False):
                # 详情动作
                urls.append(url(
                    r'^{}/(?P<pk>[^/.]+)/{}/$'.format(prefix, action_name),
                    viewset.as_view({action_config['method']: action_name}),
                    name=f'{basename}-{action_name}'
                ))
            else:
                # 列表动作
                urls.append(url(
                    r'^{}/{}/$'.format(prefix, action_name),
                    viewset.as_view({action_config['method']: action_name}),
                    name=f'{basename}-{action_name}'
                ))

                return urls

        # 参数化URL路由器
        class ParameterizedRouter(DefaultRouter):
            """参数化URL路由器"""

            def __init__(self, *args, **kwargs):
                self.url_parameters = {}
                super().__init__(*args, **kwargs)

            def register_with_params(self, prefix, viewset, basename=None, **params):
                """注册带参数的路由"""
                self.url_parameters[basename or viewset.__name__] = params
                super().register(prefix, viewset, basename)

            def get_urls(self):
                """生成带参数的URL"""
                urls = super().get_urls()

                # 为每个URL添加参数
        parameterized_urls = []
        for url_pattern in urls:
            for basename, params in self.url_parameters.items():
                if basename in str(url_pattern.name):
                    # 替换URL模式中的参数
                    pattern_str = str(url_pattern.pattern)
                    for param_name, param_value in params.items():
                        pattern_str = pattern_str.replace(f'{{{param_name}}}', param_value)

                    # 创建新的URL模式
                    new_pattern = type(url_pattern)(
                        pattern=pattern_str,
                        callback=url_pattern.callback,
                        name=url_pattern.name
                    )
                    parameterized_urls.append(new_pattern)

                urls.extend(parameterized_urls)
                return urls

        # 缓存路由器
        class CachedRouter(DefaultRouter):
            """缓存路由器"""

            def __init__(self, cache_timeout=300, *args, **kwargs):
                self.cache_timeout = cache_timeout
                self._cached_urls = None
                self._cache_timestamp = None
                super().__init__(*args, **kwargs)

            def get_urls(self):
                """获取缓存的URL"""
                from django.core.cache import cache
        from django.utils import timezone
        import time

        current_time = time.time()

        # 检查缓存是否有效
        if (self._cached_urls is not None and
            self._cache_timestamp is not None and
            current_time - self._cache_timestamp < self.cache_timeout):
            return self._cached_urls

        # 生成新的URL
        urls = super().get_urls()

        # 缓存URL
        self._cached_urls = urls
        self._cache_timestamp = current_time

        return urls

            def invalidate_cache(self):
                """使缓存失效"""
                self._cached_urls = None
                self._cache_timestamp = None

        # 路由中间件
        class RouterMiddleware:
            """路由中间件"""

            def __init__(self, get_response):
                self.get_response = get_response

            def __call__(self, request):
                """处理请求"""
                # 在路由匹配前执行
                self.before_routing(request)

                response = self.get_response(request)

                # 在路由处理后执行
                self.after_routing(request, response)

                return response

            def before_routing(self, request):
                """路由前处理"""
                # 记录路由信息
                request.routing_start_time = time.time()
                request.original_path = request.path

                # 路径规范化
                if request.path.endswith('/'):
                    request.path = request.path.rstrip('/')

                # API版本检测
                version = self.detect_api_version(request)
                request.api_version = version

            def after_routing(self, request, response):
                """路由后处理"""
                # 记录路由性能
                if hasattr(request, 'routing_start_time'):
                    routing_time = time.time() - request.routing_start_time
                    response['X-Routing-Time'] = f"{routing_time:.3f}s"

                # 添加API版本头
                if hasattr(request, 'api_version'):
                    response['X-API-Version'] = request.api_version

            def detect_api_version(self, request):
                """检测API版本"""
                # 从URL路径检测
                if '/api/v1/' in request.path:
                    return 'v1'
                elif '/api/v2/' in request.path:
                    return 'v2'

                # 从Header检测
                version = request.META.get('HTTP_API_VERSION')
                if version:
                    return version

                # 默认版本
                return 'v1'

        # 使用自定义路由器
        # apps/blog/urls.py
        from django.urls import path, include
        from .custom_routers import DynamicRouter, ParameterizedRouter, CachedRouter
        from . import viewsets

        # 动态路由器
        dynamic_router = DynamicRouter()
        dynamic_router.register(r'articles', viewsets.ArticleViewSet, basename='article')

        # 添加动态路由
        dynamic_router.add_dynamic_route(
            r'featured',
            viewsets.ArticleViewSet,
            basename='featured-articles'
        )

        # 参数化路由器
        param_router = ParameterizedRouter()
        param_router.register_with_params(
            r'{environment}/articles',
            viewsets.ArticleViewSet,
            basename='env-article',
            environment='prod'
        )

        # 缓存路由器
        cached_router = CachedRouter(cache_timeout=600)  # 10分钟缓存
        cached_router.register(r'categories', viewsets.CategoryViewSet, basename='category')

        urlpatterns = [
            path('', include(dynamic_router.urls)),
            path('', include(param_router.urls)),
            path('', include(cached_router.urls)),
        ]

        # 多级路由器
        class NestedRouter(DefaultRouter):
            """嵌套路由器"""

            def __init__(self, parent_router=None, parent_prefix=None, *args, **kwargs):
                self.parent_router = parent_router
                self.parent_prefix = parent_prefix
                super().__init__(*args, **kwargs)

            def register_nested(self, prefix, viewset, basename, parent_lookup='parent_id'):
                """注册嵌套路由"""
                # 创建嵌套URL模式
                nested_prefix = f'{self.parent_prefix}/(?P<{parent_lookup}>[^/.]+)/{prefix}'

                super().register(nested_prefix, viewset, basename)

        # 使用嵌套路由器
        parent_router = DefaultRouter()
        parent_router.register(r'categories', viewsets.CategoryViewSet, basename='category')

        # 为分类创建嵌套文章路由
        nested_article_router = NestedRouter(
            parent_router=parent_router,
            parent_prefix='categories'
        )
        nested_article_router.register_nested(
            r'articles',
            viewsets.ArticleViewSet,
            basename='category-articles',
            parent_lookup='category_id'
        )

        # 生成的URL:
        # categories/                    - 分类列表
        # categories/<id>/               - 分类详情
        # categories/<id>/articles/      - 分类下的文章列表
        # categories/<id>/articles/<id>/ - 分类下的文章详情

4.5 视图装饰器

01.基础认证装饰
    a.说明
        登录状态检查
        权限验证
        用户角色检查
    b.示例
        # apps/blog/decorators.py
        from functools import wraps
        from django.contrib.auth.decorators import login_required
        from django.http import JsonResponse
        from django.utils.decorators import method_decorator
        from rest_framework import status
        from django.contrib.auth import get_user_model
        import logging

        User = get_user_model()
        logger = logging.getLogger('api')

        def api_login_required(view_func):
            """API登录装饰器"""
            @wraps(view_func)
            def wrapper(request, *args, **kwargs):
                if not request.user.is_authenticated:
                    return JsonResponse({
                        'success': False,
                        'message': '需要登录才能访问此接口',
                        'error_code': 'AUTHENTICATION_REQUIRED'
                    }, status=status.HTTP_401_UNAUTHORIZED)

                return view_func(request, *args, **kwargs)
            return wrapper

        def permission_required(perm, login_url=None):
            """权限检查装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'success': False,
                            'message': '需要登录才能访问此接口',
                            'error_code': 'AUTHENTICATION_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    if not request.user.has_perm(perm):
                        logger.warning(
                            f"权限不足: 用户 {request.user.username} 尝试访问需要权限 {perm} 的接口"
                        )
                        return JsonResponse({
                            'success': False,
                            'message': '权限不足',
                            'error_code': 'PERMISSION_DENIED',
                            'required_permission': perm
                        }, status=status.HTTP_403_FORBIDDEN)

                    return view_func(request, *args, **kwargs)
                return wrapper
            return decorator

        def role_required(*roles):
            """角色检查装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'success': False,
                            'message': '需要登录才能访问此接口',
                            'error_code': 'AUTHENTICATION_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    user_roles = []
                    if hasattr(request.user, 'profile') and hasattr(request.user.profile, 'roles'):
                        user_roles = [role.name for role in request.user.profile.roles.all()]
                    elif request.user.is_staff:
                        user_roles.append('admin')
                    elif request.user.is_superuser:
                        user_roles.append('superadmin')

                    if not any(role in user_roles for role in roles):
                        logger.warning(
                            f"角色不足: 用户 {request.user.username} ({user_roles}) 尝试访问需要角色 {roles} 的接口"
                        )
                        return JsonResponse({
                            'success': False,
                            'message': '角色权限不足',
                            'error_code': 'ROLE_PERMISSION_DENIED',
                            'required_roles': list(roles),
                            'user_roles': user_roles
                        }, status=status.HTTP_403_FORBIDDEN)

                    return view_func(request, *args, **kwargs)
                return wrapper
            return decorator

        def api_key_required(api_key_header='X-API-Key'):
            """API密钥装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    # 检查Authorization头
                    auth_header = request.META.get('HTTP_AUTHORIZATION')
                    if auth_header and auth_header.startswith('Bearer '):
                        # JWT令牌认证
                        from rest_framework_simplejwt.authentication import JWTAuthentication
                        jwt_auth = JWTAuthentication()
                        try:
                            validated_token = jwt_auth.get_validated_token(auth_header[7:])
                            user = jwt_auth.get_user(validated_token)
                            request.user = user
                            return view_func(request, *args, **kwargs)
                        except Exception:
                            pass

                    # 检查API密钥
                    api_key = request.META.get(f'HTTP_{api_key_header.upper().replace("-", "_")}')
                    if not api_key:
                        return JsonResponse({
                            'success': False,
                            'message': 'API密钥缺失',
                            'error_code': 'API_KEY_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    # 验证API密钥
                    from .models import APIKey
                    try:
                        key_obj = APIKey.objects.get(key=api_key, is_active=True)
                        request.api_user = key_obj.user
                        request.api_key = key_obj

                        # 更新最后使用时间
                        key_obj.last_used_at = timezone.now()
                        key_obj.usage_count += 1
                        key_obj.save(update_fields=['last_used_at', 'usage_count'])

                        return view_func(request, *args, **kwargs)
                    except APIKey.DoesNotExist:
                        return JsonResponse({
                            'success': False,
                            'message': '无效的API密钥',
                            'error_code': 'INVALID_API_KEY'
                        }, status=status.HTTP_401_UNAUTHORIZED)
                return wrapper
            return decorator

        def ownership_required(model_class, lookup_field='pk', owner_field='author'):
            """对象所有权装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'success': False,
                            'message': '需要登录才能访问此接口',
                            'error_code': 'AUTHENTICATION_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    # 获取对象ID
                    object_id = kwargs.get(lookup_field)
                    if not object_id:
                        return JsonResponse({
                            'success': False,
                            'message': '缺少对象ID',
                            'error_code': 'MISSING_OBJECT_ID'
                        }, status=status.HTTP_400_BAD_REQUEST)

                    try:
                        # 获取对象
                        obj = model_class.objects.get(id=object_id)
                        owner = getattr(obj, owner_field)

                        # 检查所有权
                        if owner != request.user and not request.user.is_staff:
                            logger.warning(
                                f"所有权检查失败: 用户 {request.user.username} 尝试访问不属于他的 {model_class.__name__} 对象"
                            )
                            return JsonResponse({
                                'success': False,
                                'message': '只能访问自己的资源',
                                'error_code': 'OWNERSHIP_REQUIRED'
                            }, status=status.HTTP_403_FORBIDDEN)

                        # 将对象添加到请求上下文
                        request.object = obj
                        return view_func(request, *args, **kwargs)

                    except model_class.DoesNotExist:
                        return JsonResponse({
                            'success': False,
                            'message': f'{model_class.__name__}对象不存在',
                            'error_code': 'OBJECT_NOT_FOUND'
                        }, status=status.HTTP_404_NOT_FOUND)
                return wrapper
            return decorator

        class class_view_decorator(object):
            """类视图装饰器"""

            def __init__(self, decorator):
                self.decorator = decorator

            def __call__(self, cls):
                """装饰类视图"""
                # 检查是否为基于类的视图
                if hasattr(cls, 'as_view'):
                    # 装饰as_view方法返回的视图函数
                    original_view = cls.as_view()

                    @wraps(original_view)
                    def decorated_view(*args, **kwargs):
                        return self.decorator(original_view)(*args, **kwargs)

                    cls.as_view = lambda: decorated_view

                return cls

        # 使用示例
        @class_view_decorator(api_login_required)
        class ProtectedView(View):
            """受保护的视图"""
            def get(self, request):
                return JsonResponse({'message': '访问成功'})

        # 或者直接装饰方法
        @method_decorator(api_login_required, name='dispatch')
        class ArticleDetailView(View):
            """文章详情视图"""
            def get(self, request, pk):
                # 只有登录用户才能访问
                pass

        # 组合装饰器
        def admin_required(view_func):
            """管理员权限装饰器"""
            return role_required('admin', 'superadmin')(permission_required('blog.can_manage_articles')(view_func))

        @method_decorator(admin_required, name='dispatch')
        class AdminArticleView(View):
            """管理员文章视图"""
            def get(self, request):
                # 只有管理员才能访问
                pass

        # API使用示例
        from rest_framework.views import APIView
        from rest_framework.response import Response

        class ProtectedAPI(APIView):
        """受保护的API视图"""

        @method_decorator(api_login_required)
        def get(self, request):
            """需要登录的GET请求"""
            return Response({'message': '访问成功'})

        @method_decorator(role_required('admin'))
        def post(self, request):
            """需要管理员角色的POST请求"""
            return Response({'message': '管理员操作成功'})

        @method_decorator(ownership_required(Article))
        def put(self, request, pk):
            """需要所有权的PUT请求"""
            article = request.object
            return Response({'message': f'更新文章 {article.title} 成功'})

        @method_decorator(api_key_required())
        def delete(self, request, pk):
            """需要API密钥的DELETE请求"""
            return Response({'message': 'API密钥验证成功'})

02.权限装饰器
    a.说明
        功能权限检查
        资源访问控制
        条件权限验证
    b.示例
        # apps/blog/permission_decorators.py
        from functools import wraps
        from django.http import JsonResponse
        from django.core.exceptions import PermissionDenied
        from rest_framework import status
        import logging

        logger = logging.getLogger('permissions')

        def has_permission(perm, obj=None):
            """权限检查装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'success': False,
                            'message': '需要登录才能访问此接口',
                            'error_code': 'AUTHENTICATION_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    # 检查全局权限
                    if obj is None:
                        if not request.user.has_perm(perm):
                            logger.warning(f"权限不足: {request.user.username} 缺少权限 {perm}")
                            return JsonResponse({
                                'success': False,
                                'message': '权限不足',
                                'error_code': 'PERMISSION_DENIED',
                                'required_permission': perm
                            }, status=status.HTTP_403_FORBIDDEN)
                    else:
                        # 检查对象级权限
                        if not request.user.has_perm(perm, obj):
                            logger.warning(f"对象权限不足: {request.user.username} 缺少对象权限 {perm}")
                            return JsonResponse({
                                'success': False,
                                'message': '对象权限不足',
                                'error_code': 'OBJECT_PERMISSION_DENIED',
                                'required_permission': perm
                            }, status=status.HTTP_403_FORBIDDEN)

                    return view_func(request, *args, **kwargs)
                return wrapper
            return decorator

        def check_article_permission(action):
            """文章权限检查装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, article_id=None, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'success': False,
                            'message': '需要登录才能访问此接口',
                            'error_code': 'AUTHENTICATION_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    try:
                        from .models import Article
                        article = Article.objects.get(id=article_id)
                    except Article.DoesNotExist:
                        return JsonResponse({
                            'success': False,
                            'message': '文章不存在',
                            'error_code': 'ARTICLE_NOT_FOUND'
                        }, status=status.HTTP_404_NOT_FOUND)

                    # 检查文章权限
                    can_access = False
                    permission_reason = ''

                    if action == 'read':
                        # 阅读权限检查
                        if article.status == 'published':
                            can_access = True
                        elif article.status == 'draft':
                            if article.author == request.user or request.user.is_staff:
                                can_access = True
                            else:
                                permission_reason = '只能访问已发布的文章'
                        elif article.status == 'private':
                            if article.author == request.user or request.user.is_superuser:
                                can_access = True
                            else:
                                permission_reason = '私有文章只能访问自己的'

                    elif action == 'edit':
                        # 编辑权限检查
                        if article.author == request.user:
                            can_access = True
                        elif request.user.is_staff and article.status != 'private':
                            can_access = True
                        else:
                            permission_reason = '只能编辑自己的文章或管理员可编辑公开文章'

                    elif action == 'delete':
                        # 删除权限检查
                        if article.author == request.user:
                            can_access = True
                        elif request.user.is_superuser:
                            can_access = True
                        else:
                            permission_reason = '只有文章作者或超级管理员可以删除文章'

                    elif action == 'publish':
                        # 发布权限检查
                        if article.author == request.user and request.user.has_perm('blog.can_publish_articles'):
                            can_access = True
                        elif request.user.is_staff:
                            can_access = True
                        else:
                            permission_reason = '需要发布权限或管理员权限'

                    elif action == 'manage':
                        # 管理权限检查
                        if request.user.is_staff:
                            can_access = True
                        else:
                            permission_reason = '需要管理员权限'

                    if not can_access:
                        logger.warning(f"文章权限不足: {request.user.username} 尝试对文章 {article.id} 进行 {action} 操作")
                        return JsonResponse({
                            'success': False,
                            'message': permission_reason,
                            'error_code': 'ARTICLE_PERMISSION_DENIED',
                            'action': action
                        }, status=status.HTTP_403_FORBIDDEN)

                    # 将文章对象添加到请求中
                    request.article = article
                    return view_func(request, *args, **kwargs)

                return wrapper
            return decorator

        def check_user_permission(action, target_user_id=None):
            """用户权限检查装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, user_id=None, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'success': False,
                            'message': '需要登录才能访问此接口',
                            'error_code': 'AUTHENTICATION_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    # 确定目标用户
                    target_id = target_user_id or user_id
                    if target_id == str(request.user.id) and action in ['view', 'edit']:
                        # 用户操作自己的资料,允许
                        target_user = request.user
                    else:
                        try:
                            target_user = User.objects.get(id=target_id)
                        except User.DoesNotExist:
                            return JsonResponse({
                                'success': False,
                                'message': '用户不存在',
                                'error_code': 'USER_NOT_FOUND'
                            }, status=status.HTTP_404_NOT_FOUND)

                    # 检查用户权限
                    can_access = False
                    permission_reason = ''

                    if action == 'view':
                        # 查看权限检查
                        if target_user.profile.is_public:
                            can_access = True
                        elif target_user == request.user:
                            can_access = True
                        elif request.user.is_staff:
                            can_access = True
                        else:
                            permission_reason = '只能查看公开的用户资料'

                    elif action == 'edit':
                        # 编辑权限检查
                        if target_user == request.user:
                            can_access = True
                        elif request.user.is_staff:
                            can_access = True
                        else:
                            permission_reason = '只能编辑自己的资料或管理员可编辑其他资料'

                    elif action == 'manage':
                        # 管理权限检查
                        if request.user.is_staff:
                            can_access = True
                        else:
                            permission_reason = '需要管理员权限'

                    elif action == 'follow':
                        # 关注权限检查
                        if target_user != request.user:
                            can_access = True
                        else:
                            permission_reason = '不能关注自己'

                    elif action == 'block':
                        # 屏蔽权限检查
                        if target_user != request.user:
                            can_access = True
                        else:
                            permission_reason = '不能屏蔽自己'

                    if not can_access:
                        logger.warning(f"用户权限不足: {request.user.username} 尝试对用户 {target_user.id} 进行 {action} 操作")
                        return JsonResponse({
                            'success': False,
                            'message': permission_reason,
                            'error_code': 'USER_PERMISSION_DENIED',
                            'action': action
                        }, status=status.HTTP_403_FORBIDDEN)

                    # 将目标用户添加到请求中
                    request.target_user = target_user
                    return view_func(request, *args, **kwargs)

                return wrapper
            return decorator

        def check_category_permission(action):
            """分类权限检查装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, category_id=None, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'success': False,
                            'message': '需要登录才能访问此接口',
                            'error_code': 'AUTHENTICATION_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    try:
                        from .models import Category
                        category = Category.objects.get(id=category_id)
                    except Category.DoesNotExist:
                        return JsonResponse({
                            'success': False,
                            'message': '分类不存在',
                            'error_code': 'CATEGORY_NOT_FOUND'
                        }, status=status.HTTP_404_NOT_FOUND)

                    # 检查分类权限
                    can_access = False
                    permission_reason = ''

                    if action == 'read':
                        # 阅读权限检查
                        if category.is_active:
                            can_access = True
                        elif request.user.is_staff:
                            can_access = True
                        else:
                            permission_reason = '只能查看激活的分类'

                    elif action == 'edit':
                        # 编辑权限检查
                        if request.user.has_perm('blog.can_manage_categories'):
                            can_access = True
                        elif request.user.is_staff:
                            can_access = True
                        else:
                            permission_reason = '需要分类管理权限'

                    elif action == 'delete':
                        # 删除权限检查
                        if request.user.has_perm('blog.can_delete_categories'):
                            can_access = True
                        elif request.user.is_superuser:
                            can_access = True
                        else:
                            permission_reason = '需要分类删除权限'

                    if not can_access:
                        logger.warning(f"分类权限不足: {request.user.username} 尝试对分类 {category.id} 进行 {action} 操作")
                        return JsonResponse({
                            'success': False,
                            'message': permission_reason,
                            'error_code': 'CATEGORY_PERMISSION_DENIED',
                            'action': action
                        }, status=status.HTTP_403_FORBIDDEN)

                    # 将分类对象添加到请求中
                    request.category = category
                    return view_func(request, *args, **kwargs)

                return wrapper
            return decorator

        def conditional_permission(condition_func, error_message='权限不足'):
            """条件权限装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'success': False,
                            'message': '需要登录才能访问此接口',
                            'error_code': 'AUTHENTICATION_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    if not condition_func(request, *args, **kwargs):
                        logger.warning(f"条件权限检查失败: {request.user.username}")
                        return JsonResponse({
                            'success': False,
                            'message': error_message,
                            'error_code': 'CONDITIONAL_PERMISSION_DENIED'
                        }, status=status.HTTP_403_FORBIDDEN)

                    return view_func(request, *args, **kwargs)
                return wrapper
            return decorator

        # 权限检查函数
        def is_article_author_or_staff(request, article_id):
            """检查是否为文章作者或管理员"""
            from .models import Article
            try:
                article = Article.objects.get(id=article_id)
                return article.author == request.user or request.user.is_staff
            except Article.DoesNotExist:
                return False

        def is_content_manager(request):
            """检查是否为内容管理员"""
            return (request.user.has_perm('blog.can_manage_articles') or
                    request.user.has_perm('blog.can_manage_categories') or
                    request.user.is_staff)

        def can_publish_content(request):
            """检查是否可以发布内容"""
            return (request.user.has_perm('blog.can_publish_articles') or
                    request.user.is_staff)

        # 高级权限装饰器组合
        def owner_or_permission(permission):
            """所有者或特定权限装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'success': False,
                            'message': '需要登录才能访问此接口',
                            'error_code': 'AUTHENTICATION_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    # 检查对象所有权或权限
                    obj_id = kwargs.get('pk') or kwargs.get('id')
                    model_class = view_func.model_class if hasattr(view_func, 'model_class') else None

                    if obj_id and model_class:
                        try:
                            obj = model_class.objects.get(id=obj_id)
                            owner = getattr(obj, 'author', getattr(obj, 'user', None))

                            if owner == request.user or request.user.has_perm(permission):
                                request.object = obj
                                return view_func(request, *args, **kwargs)
                            else:
                                return JsonResponse({
                                    'success': False,
                                    'message': '需要对象所有权或特定权限',
                                    'error_code': 'OWNER_OR_PERMISSION_REQUIRED',
                                    'required_permission': permission
                                }, status=status.HTTP_403_FORBIDDEN)
                        except model_class.DoesNotExist:
                            return JsonResponse({
                                'success': False,
                                'message': '对象不存在',
                                'error_code': 'OBJECT_NOT_FOUND'
                            }, status=status.HTTP_404_NOT_FOUND)
                    else:
                        # 检查全局权限
                        if request.user.has_perm(permission):
                            return view_func(request, *args, **kwargs)
                        else:
                            return JsonResponse({
                                'success': False,
                                'message': '需要特定权限',
                                'error_code': 'PERMISSION_REQUIRED',
                                'required_permission': permission
                            }, status=status.HTTP_403_FORBIDDEN)
                return wrapper
            return decorator

        # 权限装饰器使用示例
        from rest_framework.views import APIView
        from rest_framework.response import Response

        class ArticleAPI(APIView):
            """文章API视图"""
            model_class = Article

            @method_decorator(check_article_permission('read'))
            def get(self, request, article_id):
                """读取文章"""
                article = request.article
                return Response({'title': article.title, 'content': article.content})

            @method_decorator(check_article_permission('edit'))
            def put(self, request, article_id):
                """编辑文章"""
                article = request.article
                # 更新逻辑
                return Response({'message': '文章更新成功'})

            @method_decorator(owner_or_permission('blog.can_delete_articles'))
            def delete(self, request, article_id):
                """删除文章"""
                article = request.object
                # 删除逻辑
                return Response({'message': '文章删除成功'})

            @method_decorator(conditional_permission(is_content_manager, '需要内容管理权限'))
            def get(self, request):
                """获取文章列表(需要内容管理权限)"""
                # 获取逻辑
                return Response({'message': '获取文章列表成功'})

        # 动态权限装饰器
        def dynamic_permission(permission_func):
            """动态权限装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'success': False,
                            'message': '需要登录才能访问此接口',
                            'error_code': 'AUTHENTICATION_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    # 动态检查权限
                    has_permission = permission_func(request, *args, **kwargs)
                    if not has_permission:
                        return JsonResponse({
                            'success': False,
                            'message': '权限不足',
                            'error_code': 'DYNAMIC_PERMISSION_DENIED'
                        }, status=status.HTTP_403_FORBIDDEN)

                    return view_func(request, *args, **kwargs)
                return wrapper
            return decorator

        # 使用动态权限装饰器
        @dynamic_permission(lambda request, article_id: (
            Article.objects.get(id=article_id).author == request.user or
            request.user.has_perm('blog.can_edit_articles')
        ))
        def edit_article(request, article_id):
            """动态权限检查的编辑文章函数"""
            pass

03.缓存装饰器
    a.说明
        响应缓存控制
        查询结果缓存
        缓存失效管理
    b.示例
        # apps/blog/cache_decorators.py
        from functools import wraps
        from django.core.cache import cache
        from django.http import JsonResponse
        from rest_framework import status
        from django.utils.decorators import method_decorator
        from django.utils import timezone
        import hashlib
        import json
        import logging

        logger = logging.getLogger('cache')

        def cache_response(timeout=300, key_func=None, cache_params=None):
            """响应缓存装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    # 检查是否使用缓存
                    no_cache = request.GET.get('no_cache', 'false').lower() == 'true'
                    if no_cache:
                        return view_func(request, *args, **kwargs)

                    # 生成缓存键
                    if key_func:
                        cache_key = key_func(request, *args, **kwargs)
                    else:
                        cache_key = generate_cache_key(request, view_func, args, kwargs, cache_params)

                    # 尝试从缓存获取
                    cached_data = cache.get(cache_key)
                    if cached_data is not None:
                        logger.info(f"Cache hit for key: {cache_key}")
                        return JsonResponse(cached_data)

                    # 执行视图函数
                    response = view_func(request, *args, **kwargs)

                    # 只缓存成功的JSON响应
                    if (hasattr(response, 'status_code') and response.status_code == 200 and
                        hasattr(response, 'data') and isinstance(response.data, dict)):
                        cache.set(cache_key, response.data, timeout)
                        logger.info(f"Cache set for key: {cache_key}, timeout: {timeout}")

                    return response
                return wrapper
            return decorator

        def generate_cache_key(request, view_func, args, kwargs, cache_params=None):
            """生成缓存键"""
            # 基础键名
            key_parts = [
                view_func.__module__,
                view_func.__name__,
                request.method,
                request.get_full_path(),
            ]

            # 添加用户信息
            if hasattr(request, 'user') and request.user.is_authenticated:
                key_parts.append(f"user_{request.user.id}")
            else:
                key_parts.append("anonymous")

            # 添加查询参数
            query_params = sorted(request.GET.items())
            if query_params:
                key_parts.extend(f"{k}:{v}" for k, v in query_params)

            # 添加自定义缓存参数
            if cache_params:
                for param in cache_params:
                    if hasattr(request, param):
                        value = getattr(request, param)
                        key_parts.append(f"{param}:{value}")

            # 生成MD5哈希
            key_string = "|".join(str(part) for part in key_parts)
            return hashlib.md5(key_string.encode()).hexdigest()

        def cache_queryset(timeout=300, key_prefix=None):
            """查询集缓存装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    # 生成缓存键
                    cache_key = f"qs_{key_prefix or view_func.__name__}_{generate_cache_key(request, view_func, args, kwargs)}"

                    # 检查缓存
                    cached_queryset = cache.get(cache_key)
                    if cached_queryset is not None:
                        logger.info(f"QuerySet cache hit for key: {cache_key}")
                        request.cached_queryset = cached_queryset
                        return view_func(request, *args, **kwargs)

                    # 执行视图函数(会生成查询集)
                    response = view_func(request, *args, **kwargs)

                    # 缓存查询集(如果存在)
                    if hasattr(response, 'queryset'):
                        # 将查询集转换为可缓存的格式
                        queryset_data = {
                            'model': response.queryset.model.__name__,
                            'sql': str(response.queryset.query),
                            'data': list(response.queryset.values())
                        }
                        cache.set(cache_key, queryset_data, timeout)
                        logger.info(f"QuerySet cache set for key: {cache_key}")

                    return response
                return wrapper
            return decorator

        def cache_user_data(timeout=600):
            """用户数据缓存装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return view_func(request, *args, **kwargs)

                    # 生成用户相关缓存键
                    cache_key = f"user_data_{request.user.id}_{view_func.__name__}"

                    # 检查缓存
                    cached_data = cache.get(cache_key)
                    if cached_data is not None:
                        logger.info(f"User data cache hit for user {request.user.id}")
                        return JsonResponse(cached_data)

                    # 执行视图函数
                    response = view_func(request, *args, **kwargs)

                    # 缓存用户数据
                    if (hasattr(response, 'status_code') and response.status_code == 200 and
                        hasattr(response, 'data')):
                        cache.set(cache_key, response.data, timeout)
                        logger.info(f"User data cache set for user {request.user.id}")

                    return response
                return wrapper
            return decorator

        def cache_api_stats(timeout=60):
            """API统计缓存装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    cache_key = f"api_stats_{view_func.__name__}"

                    # 检查缓存
                    cached_stats = cache.get(cache_key)
                    if cached_stats is not None:
                        logger.info(f"API stats cache hit for {view_func.__name__}")
                        return JsonResponse(cached_stats)

                    # 执行视图函数
                    response = view_func(request, *args, **kwargs)

                    # 缓存统计数据
                    if (hasattr(response, 'status_code') and response.status_code == 200 and
                        hasattr(response, 'data')):
                        cache.set(cache_key, response.data, timeout)
                        logger.info(f"API stats cache set for {view_func.__name__}")

                    return response
                return wrapper
            return decorator

        def conditional_cache(condition_func, timeout=300):
            """条件缓存装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    # 检查缓存条件
                    should_cache = condition_func(request, *args, **kwargs)
                    if not should_cache:
                        return view_func(request, *args, **kwargs)

                    # 应用缓存装饰器
                    cache_decorator = cache_response(timeout=timeout)
                    return cache_decorator(view_func)(request, *args, **kwargs)
                return wrapper
            return decorator

        def invalidate_cache(patterns=None, keys=None):
            """缓存失效装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    # 执行视图函数
                    response = view_func(request, *args, **kwargs)

                    # 失效指定缓存
                    if patterns:
                        for pattern in patterns:
                            from django.core.cache import cache
                            # 这里应该实现模式匹配删除缓存
                            # 简化版本:删除以模式开头的所有键
                            cache_keys = cache.keys(f"{pattern}*")
                            for key in cache_keys:
                                cache.delete(key)
                            logger.info(f"Invalidated cache pattern: {pattern}")

                    if keys:
                        for key in keys:
                            cache.delete(key)
                            logger.info(f"Invalidated cache key: {key}")

                    return response
                return wrapper
            return decorator

        class CacheMiddleware:
            """缓存中间件"""

            def __init__(self, get_response):
                self.get_response = get_response

            def __call__(self, request):
                # 请求处理前的缓存逻辑
                self.process_request(request)

                response = self.get_response(request)

                # 响应处理后的缓存逻辑
                self.process_response(request, response)

                return response

            def process_request(self, request):
                """处理请求缓存逻辑"""
                # 添加缓存信息到请求
                request.cache_info = {
                    'hit': False,
                    'key': None,
                    'timeout': None
                }

            def process_response(self, request, response):
                """处理响应缓存逻辑"""
                # 添加缓存头
                if hasattr(request, 'cache_info') and request.cache_info['hit']:
                    response['X-Cache'] = 'HIT'
                    response['X-Cache-Key'] = request.cache_info['key']
                else:
                    response['X-Cache'] = 'MISS'

        # 缓存工具函数
        def get_article_cache_key(article_id, user_id=None, view_name='detail'):
            """获取文章缓存键"""
            key_parts = ['article', str(article_id), view_name]
            if user_id:
                key_parts.append(f'user_{user_id}')
            return "_".join(key_parts)

        def invalidate_article_cache(article_id):
            """失效文章相关缓存"""
            patterns = [
                f"article_{article_id}_*",
                f"qs_*article*{article_id}*",
                f"user_data_*",
                f"api_stats_*",
            ]

            for pattern in patterns:
                # 这里应该实现更高效的缓存删除
                cache_keys = cache.keys(f"{pattern}*")
                for key in cache_keys:
                    cache.delete(key)
                logger.info(f"Invalidated article cache: {pattern}")

        def invalidate_user_cache(user_id):
            """失效用户相关缓存"""
            patterns = [
                f"user_data_{user_id}_*",
                f"qs_*user*{user_id}*",
            ]

            for pattern in patterns:
                cache_keys = cache.keys(f"{pattern}*")
                for key in cache_keys:
                    cache.delete(key)
                logger.info(f"Invalidated user cache: {pattern}")

        # 缓存装饰器使用示例
        from rest_framework.views import APIView
        from rest_framework.response import Response

        class ArticleCacheAPI(APIView):
            """带缓存的文章API"""

            @method_decorator(cache_response(timeout=300))
            def get(self, request, article_id):
                """获取文章详情(缓存300秒)"""
                # 获取文章逻辑
                return Response({'title': 'Article Title', 'content': 'Article Content'})

            @method_decorator(conditional_cache(
                condition_func=lambda request, **kwargs: request.GET.get('cache', 'true').lower() == 'true',
                timeout=600
            ))
            def list(self, request):
                """获取文章列表(条件缓存)"""
                # 获取文章列表逻辑
                return Response({'articles': []})

            @method_decorator(cache_user_data(timeout=600))
            def get_user_articles(self, request):
                """获取用户文章(缓存用户数据)"""
                # 获取用户文章逻辑
                return Response({'user_articles': []})

            @method_decorator(invalidate_cache(
                patterns=['article_*', 'qs_*'],
                keys=['api_stats']
            ))
            def post(self, request):
                """创建文章(失效相关缓存)"""
                # 创建文章逻辑
                return Response({'message': 'Article created'}, status=201)

        # 高级缓存装饰器
        def smart_cache(timeout=300, vary_on=None):
            """智能缓存装饰器"""
            def decorator(view_func):
                @wraps(view_func)
                def wrapper(request, *args, **kwargs):
                    # 动态确定缓存时间
                    cache_timeout = timeout

                    # 根据请求方法调整缓存时间
                    if request.method == 'GET':
                        cache_timeout = timeout
                    elif request.method == 'POST':
                        cache_timeout = timeout // 2
                    else:
                        cache_timeout = 0  # 不缓存

                    if cache_timeout == 0:
                        return view_func(request, *args, **kwargs)

                    # 生成缓存键(考虑vary_on参数)
                    def key_func(request, *args, **kwargs):
                        key_parts = [view_func.__name__, request.method]

                        # 添加vary参数
                        if vary_on:
                            for param in vary_on:
                                if hasattr(request, param):
                                    value = getattr(request, param)
                                    if callable(value):
                                        value = value()
                                    key_parts.append(f"{param}:{value}")

                        return hashlib.md5("|".join(str(part) for part in key_parts).encode()).hexdigest()

                    # 应用缓存装饰器
                    cache_decorator = cache_response(timeout=cache_timeout, key_func=key_func)
                    return cache_decorator(view_func)(request, *args, **kwargs)
                return wrapper
            return decorator

        # 使用智能缓存装饰器
        @smart_cache(timeout=300, vary_on=['user', 'language'])
        def get_article_list(request):
            """智能缓存的获取文章列表"""
            # 获取文章列表逻辑
            pass

5 认证与权限

5.1 认证系统

01.Session认证
    a.说明
        Django Session集成
        CSRF保护机制
        会话管理配置
    b.示例
        # apps/authentication/session_auth.py
        from django.contrib.auth import authenticate, login, logout
        from django.contrib.auth.decorators import login_required
        from django.utils.decorators import method_decorator
        from django.views.decorators.csrf import ensure_csrf_cookie
        from django.views.decorators.cache import never_cache
        from rest_framework import status, permissions
        from rest_framework.decorators import api_view, permission_classes
        from rest_framework.response import Response
        from rest_framework.views import APIView
        from django.http import JsonResponse
        from django.middleware.csrf import get_token
        import logging

        logger = logging.getLogger('authentication')

        class SessionAuthenticationView(APIView):
            """Session认证视图基类"""
            authentication_classes = []  # 使用Django的默认session认证
            permission_classes = [permissions.IsAuthenticated]

            def dispatch(self, request, *args, **kwargs):
                """重写dispatch方法处理CSRF"""
                # 对于API请求,可能需要豁免CSRF
                if request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
                    # 检查是否为API请求
                    if request.path.startswith('/api/'):
                        # API请求可以使用CSRF豁免,但建议使用CSRF Token
                        if request.META.get('HTTP_X_CSRFTOKEN'):
                            return super().dispatch(request, *args, **kwargs)
                        else:
                            return Response({
                                'success': False,
                                'message': 'CSRF token缺失',
                                'error_code': 'CSRF_TOKEN_MISSING'
                            }, status=status.HTTP_403_FORBIDDEN)

                return super().dispatch(request, *args, **kwargs)

            @ensure_csrf_cookie
            def get(self, request):
                """获取CSRF Token"""
                csrf_token = get_token(request)
                return Response({
                    'success': True,
                    'message': 'CSRF Token获取成功',
                    'data': {
                        'csrf_token': csrf_token,
                        'user_authenticated': request.user.is_authenticated,
                        'username': request.user.username if request.user.is_authenticated else None
                    }
                })

        @api_view(['POST'])
        @permission_classes([permissions.AllowAny])
        @never_cache
        def session_login(request):
            """Session登录接口"""
            username = request.data.get('username')
            password = request.data.get('password')
            remember_me = request.data.get('remember_me', False)

            if not username or not password:
                return Response({
                    'success': False,
                    'message': '用户名和密码不能为空',
                    'error_code': 'MISSING_CREDENTIALS'
                }, status=status.HTTP_400_BAD_REQUEST)

            # 验证CSRF Token
            csrf_token = request.META.get('HTTP_X_CSRFTOKEN')
            if not csrf_token:
                return Response({
                    'success': False,
                    'message': 'CSRF token缺失',
                    'error_code': 'CSRF_TOKEN_MISSING'
                }, status=status.HTTP_403_FORBIDDEN)

            try:
                # 认证用户
                user = authenticate(request, username=username, password=password)

                if user is not None:
                    if user.is_active:
                        # 设置会话过期时间
                        if remember_me:
                            request.session.set_expiry(30 * 24 * 60 * 60)  # 30天
                        else:
                            request.session.set_expiry(0)  # 浏览器关闭时过期

                        # 记录登录信息
                        login(request, user)

                        # 更新最后登录时间
                        from django.utils import timezone
                        user.last_login = timezone.now()
                        user.save(update_fields=['last_login'])

                        # 记录登录日志
                        logger.info(f"用户 {username} 登录成功,IP: {get_client_ip(request)}")

                        # 创建用户会话记录
                        create_user_session(user, request)

                        return Response({
                            'success': True,
                            'message': '登录成功',
                            'data': {
                                'user': {
                                    'id': user.id,
                                    'username': user.username,
                                    'email': user.email,
                                    'first_name': user.first_name,
                                    'last_name': user.last_name,
                                    'is_staff': user.is_staff,
                                    'is_superuser': user.is_superuser,
                                    'last_login': user.last_login.isoformat() if user.last_login else None,
                                },
                                'session_expires': request.session.get_expiry_age(),
                                'permissions': get_user_permissions(user)
                            }
                        }, status=status.HTTP_200_OK)
                    else:
                        logger.warning(f"用户 {username} 账户已被禁用,登录失败")
                        return Response({
                            'success': False,
                            'message': '账户已被禁用',
                            'error_code': 'ACCOUNT_DISABLED'
                        }, status=status.HTTP_403_FORBIDDEN)
                else:
                    logger.warning(f"用户 {username} 登录失败:用户名或密码错误,IP: {get_client_ip(request)}")
                    return Response({
                        'success': False,
                        'message': '用户名或密码错误',
                        'error_code': 'INVALID_CREDENTIALS'
                    }, status=status.HTTP_401_UNAUTHORIZED)

            except Exception as e:
                logger.error(f"登录过程中发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '登录失败,请稍后重试',
                    'error_code': 'LOGIN_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def session_logout(request):
            """Session登出接口"""
            try:
                # 记录用户会话信息
                user_id = request.user.id
                session_key = request.session.session_key

                # 执行登出
                logout(request)

                # 清除用户会话记录
                clear_user_session(user_id, session_key)

                logger.info(f"用户 {request.user.username} 登出成功")

                return Response({
                    'success': True,
                    'message': '登出成功'
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"登出过程中发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '登出失败',
                    'error_code': 'LOGOUT_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['GET'])
        @permission_classes([permissions.IsAuthenticated])
        def session_status(request):
            """检查Session状态"""
            try:
                # 检查会话有效性
                if not request.user.is_authenticated:
                    return Response({
                        'success': False,
                        'message': '会话已过期',
                        'error_code': 'SESSION_EXPIRED'
                    }, status=status.HTTP_401_UNAUTHORIZED)

                # 获取会话信息
                session_info = {
                    'user_id': request.user.id,
                    'username': request.user.username,
                    'session_key': request.session.session_key,
                    'session_created': request.session.get('session_created'),
                    'session_last_activity': request.session.get('session_last_activity'),
                    'expires_in': request.session.get_expiry_age(),
                    'is_persistent': request.session.get_expiry_age() > 0
                }

                return Response({
                    'success': True,
                    'message': '会话有效',
                    'data': session_info
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"检查会话状态时发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '检查会话状态失败',
                    'error_code': 'SESSION_CHECK_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def refresh_session(request):
            """刷新Session"""
            try:
                # 刷新会话过期时间
                request.session.set_expiry(request.session.get_expiry_age())

                # 更新最后活动时间
                request.session['session_last_activity'] = timezone.now().isoformat()
                request.session.save()

                logger.info(f"用户 {request.user.username} 会话刷新成功")

                return Response({
                    'success': True,
                    'message': '会话刷新成功',
                    'data': {
                        'expires_in': request.session.get_expiry_age(),
                        'session_last_activity': request.session.get('session_last_activity')
                    }
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"刷新会话时发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '会话刷新失败',
                    'error_code': 'SESSION_REFRESH_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 辅助函数
        def get_client_ip(request):
            """获取客户端IP地址"""
            x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
            if x_forwarded_for:
                ip = x_forwarded_for.split(',')[0]
            else:
                ip = request.META.get('REMOTE_ADDR')
            return ip

        def get_user_permissions(user):
            """获取用户权限列表"""
            permissions = []
            for perm in user.get_all_permissions():
                app_label, codename = perm.split('.')
                permissions.append({
                    'app_label': app_label,
                    'codename': codename,
                    'name': perm
                })
            return permissions

        def create_user_session(user, request):
            """创建用户会话记录"""
            from .models import UserSession
            import uuid

            UserSession.objects.create(
                user=user,
                session_key=request.session.session_key,
                ip_address=get_client_ip(request),
                user_agent=request.META.get('HTTP_USER_AGENT', ''),
                session_token=str(uuid.uuid4()),
                is_active=True,
                created_at=timezone.now(),
                last_activity=timezone.now()
            )

            # 在session中记录创建时间
            request.session['session_created'] = timezone.now().isoformat()
            request.session['session_last_activity'] = timezone.now().isoformat()

        def clear_user_session(user_id, session_key):
            """清除用户会话记录"""
            from .models import UserSession

            UserSession.objects.filter(
                user_id=user_id,
                session_key=session_key
            ).update(is_active=False, ended_at=timezone.now())

        # 会话中间件
        class SessionMiddleware:
            """会话管理中间件"""

            def __init__(self, get_response):
                self.get_response = get_response

            def __call__(self, request):
                """处理请求"""
                response = self.get_response(request)

                # 更新会话活动时间
                if request.user.is_authenticated and hasattr(request, 'session'):
                    request.session['session_last_activity'] = timezone.now().isoformat()
                    request.session.save()

                return response

        # 在settings.py中配置Session认证
        """
        REST_FRAMEWORK = {
            'DEFAULT_AUTHENTICATION_CLASSES': [
                'rest_framework.authentication.SessionAuthentication',
            ],
            'DEFAULT_PERMISSION_CLASSES': [
                'rest_framework.permissions.IsAuthenticated',
            ],
        }

        # Session配置
        SESSION_COOKIE_AGE = 1209600  # 2周(秒)
        SESSION_COOKIE_HTTPONLY = True
        SESSION_COOKIE_SECURE = True  # HTTPS环境下启用
        SESSION_COOKIE_SAMESITE = 'Lax'
        CSRF_COOKIE_HTTPONLY = True
        CSRF_COOKIE_SECURE = True
        CSRF_TRUSTED_ORIGINS = ['https://yourdomain.com']
        """

02.Token认证
    a.说明
        DRF Token机制
        Token生成和验证
        Token安全管理
    b.示例
        # apps/authentication/token_auth.py
        from rest_framework.authtoken.models import Token
        from rest_framework.authtoken.views import ObtainAuthToken
        from rest_framework.authtoken.serializers import AuthTokenSerializer
        from rest_framework.response import Response
        from rest_framework import status, permissions
        from rest_framework.decorators import api_view, permission_classes
        from django.contrib.auth import authenticate
        from django.utils import timezone
        from django.conf import settings
        import secrets
        import logging

        logger = logging.getLogger('authentication')

        class CustomObtainAuthToken(ObtainAuthToken):
            """自定义Token获取视图"""

            def post(self, request, *args, **kwargs):
                """获取Token"""
                serializer = self.serializer_class(data=request.data,
                                               context={'request': request})
                serializer.is_valid(raise_exception=True)
                user = serializer.validated_data['user']

                # 获取或创建Token
                token, created = Token.objects.get_or_create(user=user)

                # 如果是新的Token或需要刷新,生成新的Token key
                if created or request.data.get('refresh_token'):
                    # 删除旧Token
                    if token.key:
                        token.delete()

                    # 生成新的Token
                    token.key = self.generate_token_key()
                    token.created = timezone.now()
                    token.save()

                # 记录Token使用日志
                if created:
                    logger.info(f"用户 {user.username} 创建新Token")
                else:
                    logger.info(f"用户 {user.username} 获取Token")

                return Response({
                    'success': True,
                    'message': 'Token获取成功',
                    'data': {
                        'token': token.key,
                        'user_id': user.id,
                        'username': user.username,
                        'created': token.created.isoformat(),
                        'expires_at': self.get_token_expires_at(token).isoformat() if hasattr(token, 'expires_at') else None,
                    }
                }, status=status.HTTP_200_OK)

            def generate_token_key(self):
                """生成安全的Token key"""
                return secrets.token_urlsafe(40)

            def get_token_expires_at(self, token):
                """获取Token过期时间"""
                if hasattr(token, 'expires_at'):
                    return token.expires_at

                # 如果Token没有过期时间字段,使用默认过期时间
                from datetime import timedelta
                return token.created + timedelta(hours=getattr(settings, 'TOKEN_EXPIRE_HOURS', 24))

        @api_view(['POST'])
        @permission_classes([permissions.AllowAny])
        def refresh_token(request):
            """刷新Token"""
            token_key = request.data.get('token')

            if not token_key:
                return Response({
                    'success': False,
                    'message': 'Token不能为空',
                    'error_code': 'TOKEN_REQUIRED'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                # 获取并验证Token
                token = Token.objects.select_related('user').get(key=token_key)

                if not token.user.is_active:
                    return Response({
                        'success': False,
                        'message': '用户账户已被禁用',
                        'error_code': 'ACCOUNT_DISABLED'
                    }, status=status.HTTP_403_FORBIDDEN)

                # 检查Token是否过期
                if hasattr(token, 'expires_at') and token.expires_at < timezone.now():
                    return Response({
                        'success': False,
                        'message': 'Token已过期',
                        'error_code': 'TOKEN_EXPIRED'
                    }, status=status.HTTP_401_UNAUTHORIZED)

                # 生成新的Token
                old_key = token.key
                token.key = secrets.token_urlsafe(40)
                token.created = timezone.now()
                token.save()

                # 记录Token刷新日志
                logger.info(f"用户 {token.user.username} Token刷新成功")

                return Response({
                    'success': True,
                    'message': 'Token刷新成功',
                    'data': {
                        'token': token.key,
                        'user_id': token.user.id,
                        'username': token.user.username,
                        'created': token.created.isoformat(),
                        'old_token': old_key  # 用于客户端清理
                    }
                }, status=status.HTTP_200_OK)

            except Token.DoesNotExist:
                return Response({
                    'success': False,
                    'message': '无效的Token',
                    'error_code': 'INVALID_TOKEN'
                }, status=status.HTTP_401_UNAUTHORIZED)

            except Exception as e:
                logger.error(f"刷新Token时发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': 'Token刷新失败',
                    'error_code': 'TOKEN_REFRESH_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def revoke_token(request):
            """撤销Token"""
            try:
                # 获取当前用户的Token
                token = Token.objects.get(user=request.user)

                # 删除Token
                token.delete()

                logger.info(f"用户 {request.user.username} Token撤销成功")

                return Response({
                    'success': True,
                    'message': 'Token撤销成功'
                }, status=status.HTTP_200_OK)

            except Token.DoesNotExist:
                return Response({
                    'success': False,
                    'message': 'Token不存在',
                    'error_code': 'TOKEN_NOT_FOUND'
                }, status=status.HTTP_404_NOT_FOUND)

            except Exception as e:
                logger.error(f"撤销Token时发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': 'Token撤销失败',
                    'error_code': 'TOKEN_REVOKE_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['GET'])
        @permission_classes([permissions.IsAuthenticated])
        def token_info(request):
            """获取Token信息"""
            try:
                # 获取当前用户的Token
                token = Token.objects.get(user=request.user)

                token_info = {
                    'user_id': token.user.id,
                    'username': token.user.username,
                    'created': token.created.isoformat(),
                }

                # 如果有过期时间字段
                if hasattr(token, 'expires_at'):
                    token_info['expires_at'] = token.expires_at.isoformat()
                    token_info['expires_in_seconds'] = int((token.expires_at - timezone.now()).total_seconds())
                    token_info['is_expired'] = token.expires_at < timezone.now()

                return Response({
                    'success': True,
                    'message': 'Token信息获取成功',
                    'data': token_info
                }, status=status.HTTP_200_OK)

            except Token.DoesNotExist:
                return Response({
                    'success': False,
                    'message': 'Token不存在',
                    'error_code': 'TOKEN_NOT_FOUND'
                }, status=status.HTTP_404_NOT_FOUND)

            except Exception as e:
                logger.error(f"获取Token信息时发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '获取Token信息失败',
                    'error_code': 'TOKEN_INFO_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def verify_token(request):
            """验证Token有效性"""
            try:
                # 获取当前用户的Token
                token = Token.objects.get(user=request.user)

                # 检查Token是否过期
                is_expired = False
                if hasattr(token, 'expires_at'):
                    is_expired = token.expires_at < timezone.now()

                # 检查用户是否活跃
                is_user_active = token.user.is_active

                return Response({
                    'success': True,
                    'message': 'Token验证成功',
                    'data': {
                        'valid': not is_expired and is_user_active,
                        'expired': is_expired,
                        'user_active': is_user_active,
                        'created': token.created.isoformat(),
                    }
                }, status=status.HTTP_200_OK)

            except Token.DoesNotExist:
                return Response({
                    'success': False,
                    'message': 'Token不存在',
                    'error_code': 'TOKEN_NOT_FOUND'
                }, status=status.HTTP_404_NOT_FOUND)

            except Exception as e:
                logger.error(f"验证Token时发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': 'Token验证失败',
                    'error_code': 'TOKEN_VERIFY_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 扩展Token模型以支持过期时间
        # apps/authentication/models.py
        from django.db import models
        from django.contrib.auth import get_user_model
        from django.utils import timezone
        from rest_framework.authtoken.models import Token

        User = get_user_model()

        class ExpiringToken(Token):
            """带过期时间的Token模型"""

            expires_at = models.DateTimeField(
                verbose_name='过期时间',
                help_text='Token过期时间'
            )

            class Meta:
                verbose_name = '过期Token'
                verbose_name_plural = '过期Tokens'

            def save(self, *args, **kwargs):
                # 如果没有设置过期时间,使用默认值
                if not self.expires_at:
                    from django.conf import settings
                    hours = getattr(settings, 'TOKEN_EXPIRE_HOURS', 24)
                    self.expires_at = timezone.now() + timezone.timedelta(hours=hours)

                super().save(*args, **kwargs)

            @property
            def is_expired(self):
                """检查Token是否过期"""
                return self.expires_at < timezone.now()

            def refresh(self):
                """刷新Token过期时间"""
                from django.conf import settings
                hours = getattr(settings, 'TOKEN_EXPIRE_HOURS', 24)
                self.expires_at = timezone.now() + timezone.timedelta(hours=hours)
                self.save(update_fields=['expires_at'])

        # 自定义Token认证后端
        # apps/authentication/authentication.py
        from rest_framework.authentication import TokenAuthentication
        from rest_framework.authtoken.models import Token
        from django.utils import timezone

        class ExpiringTokenAuthentication(TokenAuthentication):
            """带过期时间检查的Token认证"""

            def authenticate_credentials(self, key):
                """验证Token凭证"""
                model = Token
                try:
                    # 尝试获取过期Token
                    token = model.objects.select_related('user').get(key=key)
                except model.DoesNotExist:
                    # 尝试获取标准Token
                    try:
                        token = Token.objects.select_related('user').get(key=key)
                    except Token.DoesNotExist:
                        return None

                # 检查Token是否过期
                if hasattr(token, 'expires_at') and token.expires_at < timezone.now():
                    return None

                # 检查用户是否活跃
                if not token.user.is_active:
                    return None

                return (token.user, token)

        # Token管理命令
        # apps/authentication/management/commands/cleanup_expired_tokens.py
        from django.core.management.base import BaseCommand
        from django.utils import timezone
        from rest_framework.authtoken.models import Token
        from apps.authentication.models import ExpiringToken
        import logging

        logger = logging.getLogger('management')

        class Command(BaseCommand):
            help = '清理过期的Token'

            def add_arguments(self, parser):
                parser.add_argument(
                    '--dry-run',
                    action='store_true',
                    help='只显示将要删除的Token,不实际删除'
                )

            def handle(self, *args, **options):
                dry_run = options['dry_run']

                # 清理过期的ExpiringToken
                expired_expiring_tokens = ExpiringToken.objects.filter(
                    expires_at__lt=timezone.now()
                )

                count = expired_expiring_tokens.count()
                if dry_run:
                    self.stdout.write(f'将删除 {count} 个过期的ExpiringToken')
                else:
                    expired_expiring_tokens.delete()
                    self.stdout.write(f'已删除 {count} 个过期的ExpiringToken')

                # 清理超过30天的标准Token(可根据需要调整)
                from datetime import timedelta
                cutoff_date = timezone.now() - timedelta(days=30)

                old_standard_tokens = Token.objects.filter(
                    created__lt=cutoff_date
                ).exclude(
                    pk__in=ExpiringToken.objects.values_list('pk', flat=True)
                )

                standard_count = old_standard_tokens.count()
                if dry_run:
                    self.stdout.write(f'将删除 {standard_count} 个超过30天的标准Token')
                else:
                    old_standard_tokens.delete()
                    self.stdout.write(f'已删除 {standard_count} 个超过30天的标准Token')

                total_count = count + standard_count
                self.stdout.write(self.style.SUCCESS(f'清理完成,总共处理了 {total_count} 个Token'))

        # 在settings.py中配置Token认证
        """
        REST_FRAMEWORK = {
            'DEFAULT_AUTHENTICATION_CLASSES': [
                'apps.authentication.authentication.ExpiringTokenAuthentication',
                'rest_framework.authentication.SessionAuthentication',
            ],
        }

        # Token过期配置
        TOKEN_EXPIRE_HOURS = 24  # Token过期时间(小时)
        MAX_TOKENS_PER_USER = 5  # 每个用户最大Token数量
        """

03.JWT认证
    a.说明
        JSON Web Token原理
        Token结构分析
        签名验证机制
    b.示例
        # apps/authentication/jwt_auth.py
        from rest_framework_simplejwt.tokens import RefreshToken, AccessToken, OutstandingToken, BlacklistedToken
        from rest_framework_simplejwt.settings import api_settings
        from rest_framework_simplejwt.exceptions import TokenError, InvalidToken
        from rest_framework_simplejwt.authentication import JWTAuthentication
        from rest_framework import status, permissions
        from rest_framework.decorators import api_view, permission_classes
        from rest_framework.response import Response
        from django.contrib.auth import authenticate
        from django.utils import timezone
        from django.conf import settings
        import jwt
        import logging

        logger = logging.getLogger('authentication')

        class CustomJWTAuthentication(JWTAuthentication):
            """自定义JWT认证"""

            def authenticate(self, request):
                """自定义认证逻辑"""
                # 获取JWT Token
                header = self.get_header(request)
                if header is None:
                    return None

                raw_token = self.get_raw_token(header)
                if raw_token is None:
                    return None

                # 验证Token
                validated_token = self.get_validated_token(raw_token)

                # 检查Token是否在黑名单中
                if self.is_token_blacklisted(validated_token):
                    return None

                # 获取用户
                user = self.get_user(validated_token)

                # 检查用户状态
                if not user.is_active:
                    return None

                # 检查用户权限(可选)
                if not self.check_user_permissions(user, request):
                    return None

                return (user, validated_token)

            def is_token_blacklisted(self, validated_token):
                """检查Token是否在黑名单中"""
                try:
                    jti = validated_token.get('jti')
                    if jti:
                        return BlacklistedToken.objects.filter(token_jti=jti).exists()
                except Exception:
                    pass
                return False

            def check_user_permissions(self, user, request):
                """检查用户权限"""
                # 可以在这里实现自定义权限检查逻辑
                # 例如:检查用户角色、组织权限等
                return True

        @api_view(['POST'])
        @permission_classes([permissions.AllowAny])
        def jwt_login(request):
            """JWT登录接口"""
            username = request.data.get('username')
            password = request.data.get('password')
            device_info = request.data.get('device_info', {})

            if not username or not password:
                return Response({
                    'success': False,
                    'message': '用户名和密码不能为空',
                    'error_code': 'MISSING_CREDENTIALS'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                # 认证用户
                user = authenticate(request, username=username, password=password)

                if user is not None:
                    if user.is_active:
                        # 生成JWT Token
                        refresh = RefreshToken.for_user(user)
                        access = refresh.access_token

                        # 记录设备信息
                        device_id = record_device_info(user, device_info, request)

                        # 获取用户权限
                        user_permissions = get_user_permissions(user)
                        user_roles = get_user_roles(user)

                        # 更新最后登录时间
                        user.last_login = timezone.now()
                        user.save(update_fields=['last_login'])

                        # 记录登录日志
                        logger.info(f"用户 {username} JWT登录成功,设备ID: {device_id}")

                        return Response({
                            'success': True,
                            'message': '登录成功',
                            'data': {
                                'access_token': str(access),
                                'refresh_token': str(refresh),
                                'token_type': 'Bearer',
                                'expires_in': api_settings.ACCESS_TOKEN_LIFETIME.total_seconds(),
                                'refresh_expires_in': api_settings.REFRESH_TOKEN_LIFETIME.total_seconds(),
                                'user': {
                                    'id': user.id,
                                    'username': user.username,
                                    'email': user.email,
                                    'first_name': user.first_name,
                                    'last_name': user.last_name,
                                    'is_staff': user.is_staff,
                                    'is_superuser': user.is_superuser,
                                    'last_login': user.last_login.isoformat() if user.last_login else None,
                                    'permissions': user_permissions,
                                    'roles': user_roles,
                                },
                                'device_id': device_id,
                                'issued_at': timezone.now().isoformat(),
                            }
                        }, status=status.HTTP_200_OK)
                    else:
                        logger.warning(f"用户 {username} 账户已被禁用,JWT登录失败")
                        return Response({
                            'success': False,
                            'message': '账户已被禁用',
                            'error_code': 'ACCOUNT_DISABLED'
                        }, status=status.HTTP_403_FORBIDDEN)
                else:
                    logger.warning(f"用户 {username} JWT登录失败:用户名或密码错误")
                    return Response({
                        'success': False,
                        'message': '用户名或密码错误',
                        'error_code': 'INVALID_CREDENTIALS'
                    }, status=status.HTTP_401_UNAUTHORIZED)

            except Exception as e:
                logger.error(f"JWT登录过程中发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '登录失败,请稍后重试',
                    'error_code': 'LOGIN_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['POST'])
        @permission_classes([permissions.AllowAny])
        def jwt_refresh(request):
            """刷新JWT Token"""
            refresh_token = request.data.get('refresh_token')

            if not refresh_token:
                return Response({
                    'success': False,
                    'message': 'Refresh token不能为空',
                    'error_code': 'REFRESH_TOKEN_REQUIRED'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                # 验证Refresh Token
                refresh = RefreshToken(refresh_token)

                # 检查Refresh Token是否在黑名单中
                if BlacklistedToken.objects.filter(token=refresh).exists():
                    return Response({
                        'success': False,
                        'message': 'Refresh token已失效',
                        'error_code': 'BLACKLISTED_REFRESH_TOKEN'
                    }, status=status.HTTP_401_UNAUTHORIZED)

                # 生成新的Access Token
                access = refresh.access_token

                return Response({
                    'success': True,
                    'message': 'Token刷新成功',
                    'data': {
                        'access_token': str(access),
                        'token_type': 'Bearer',
                        'expires_in': api_settings.ACCESS_TOKEN_LIFETIME.total_seconds(),
                        'issued_at': timezone.now().isoformat(),
                    }
                }, status=status.HTTP_200_OK)

            except TokenError as e:
                logger.warning(f"JWT刷新失败: {str(e)}")
                return Response({
                    'success': False,
                    'message': 'Refresh token无效或已过期',
                    'error_code': 'INVALID_REFRESH_TOKEN'
                }, status=status.HTTP_401_UNAUTHORIZED)

            except Exception as e:
                logger.error(f"JWT刷新过程中发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': 'Token刷新失败',
                    'error_code': 'TOKEN_REFRESH_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def jwt_logout(request):
            """JWT登出接口"""
            refresh_token = request.data.get('refresh_token')

            try:
                if refresh_token:
                    # 将Refresh Token加入黑名单
                    refresh = RefreshToken(refresh_token)
                    refresh.blacklist()

                    logger.info(f"用户 {request.user.username} JWT登出成功,Refresh Token已加入黑名单")

                return Response({
                    'success': True,
                    'message': '登出成功'
                }, status=status.HTTP_200_OK)

            except TokenError as e:
                logger.warning(f"JWT登出失败: {str(e)}")
                return Response({
                    'success': False,
                    'message': '登出失败',
                    'error_code': 'LOGOUT_ERROR'
                }, status=status.HTTP_400_BAD_REQUEST)

            except Exception as e:
                logger.error(f"JWT登出过程中发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '登出失败',
                    'error_code': 'LOGOUT_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def jwt_logout_all(request):
            """JWT登出所有设备"""
            try:
                # 将用户的所有Token加入黑名单
                OutstandingToken.objects.filter(user=request.user).delete()

                # 或者将所有活跃的Refresh Token加入黑名单
                user_tokens = OutstandingToken.objects.filter(user=request.user)
                for token in user_tokens:
                    token.blacklist()

                logger.info(f"用户 {request.user.username} JWT登出所有设备成功")

                return Response({
                    'success': True,
                    'message': '已登出所有设备'
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"JWT登出所有设备时发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '登出所有设备失败',
                    'error_code': 'LOGOUT_ALL_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['GET'])
        @permission_classes([permissions.IsAuthenticated])
        def jwt_token_info(request):
            """获取JWT Token信息"""
            try:
                # 从请求中获取Token
                auth_header = request.META.get('HTTP_AUTHORIZATION')
                if not auth_header or not auth_header.startswith('Bearer '):
                    return Response({
                        'success': False,
                        'message': '无法获取Token',
                        'error_code': 'TOKEN_NOT_FOUND'
                    }, status=status.HTTP_400_BAD_REQUEST)

                token_string = auth_header[7:]  # 移除 'Bearer ' 前缀

                # 解码Token
                try:
                    decoded_token = jwt.decode(
                        token_string,
                        settings.SECRET_KEY,
                        algorithms=[settings.SIMPLE_JWT['ALGORITHM']],
                        options={'verify_exp': False}
                    )
                except jwt.DecodeError:
                    return Response({
                        'success': False,
                        'message': 'Token格式无效',
                        'error_code': 'INVALID_TOKEN_FORMAT'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 获取Token过期时间
                exp_timestamp = decoded_token.get('exp')
                exp_time = timezone.datetime.fromtimestamp(exp_timestamp, tz=timezone.utc) if exp_timestamp else None

                # 计算剩余时间
                time_remaining = 0
                if exp_time:
                    time_remaining = int((exp_time - timezone.now()).total_seconds())

                # 获取用户活跃设备
                active_devices = get_user_active_devices(request.user)

                return Response({
                    'success': True,
                    'message': 'Token信息获取成功',
                    'data': {
                        'user_id': request.user.id,
                        'username': request.user.username,
                        'token_id': decoded_token.get('jti'),
                        'issued_at': timezone.datetime.fromtimestamp(
                            decoded_token.get('iat'),
                            tz=timezone.utc
                        ).isoformat() if decoded_token.get('iat') else None,
                        'expires_at': exp_time.isoformat() if exp_time else None,
                        'time_remaining': time_remaining,
                        'is_expired': time_remaining <= 0,
                        'active_devices': active_devices,
                    }
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"获取JWT Token信息时发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '获取Token信息失败',
                    'error_code': 'TOKEN_INFO_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['GET'])
        @permission_classes([permissions.IsAuthenticated])
        def jwt_active_sessions(request):
            """获取用户活跃会话"""
            try:
                # 获取用户的所有活跃Token
                outstanding_tokens = OutstandingToken.objects.filter(user=request.user).order_by('-created_at')

                sessions = []
                for token in outstanding_tokens:
                    try:
                        # 解码Token获取信息
                        decoded_token = jwt.decode(
                            token.token,
                            settings.SECRET_KEY,
                            algorithms=[settings.SIMPLE_JWT['ALGORITHM']],
                            options={'verify_exp': False}
                        )

                        exp_time = timezone.datetime.fromtimestamp(
                            decoded_token.get('exp'),
                            tz=timezone.utc
                        ) if decoded_token.get('exp') else None

                        is_expired = exp_time and exp_time < timezone.now()

                        sessions.append({
                            'token_id': token.id,
                            'token_jti': decoded_token.get('jti'),
                            'created_at': token.created_at.isoformat(),
                            'expires_at': exp_time.isoformat() if exp_time else None,
                            'is_expired': is_expired,
                            'current_device': token.token_jti == request.auth.get('jti') if request.auth else False,
                        })

                    except jwt.DecodeError:
                        # 无法解码的Token
                        sessions.append({
                            'token_id': token.id,
                            'token_jti': None,
                            'created_at': token.created_at.isoformat(),
                            'expires_at': None,
                            'is_expired': True,
                            'current_device': False,
                        })

                return Response({
                    'success': True,
                    'message': '活跃会话获取成功',
                    'data': {
                        'sessions': sessions,
                        'total_sessions': len(sessions),
                        'active_sessions': len([s for s in sessions if not s['is_expired']]),
                    }
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"获取用户活跃会话时发生错误: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '获取活跃会话失败',
                    'error_code': 'ACTIVE_SESSIONS_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 辅助函数
        def get_client_ip(request):
            """获取客户端IP地址"""
            x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
            if x_forwarded_for:
                ip = x_forwarded_for.split(',')[0]
            else:
                ip = request.META.get('REMOTE_ADDR')
            return ip

        def get_user_permissions(user):
            """获取用户权限列表"""
            permissions = []
            for perm in user.get_all_permissions():
                app_label, codename = perm.split('.')
                permissions.append({
                    'app_label': app_label,
                    'codename': codename,
                    'name': perm
                })
            return permissions

        def get_user_roles(user):
            """获取用户角色"""
            roles = []
            if hasattr(user, 'profile'):
                roles = [role.name for role in user.profile.roles.all()]

            if user.is_staff:
                roles.append('staff')
            if user.is_superuser:
                roles.append('superuser')

            return roles

        def record_device_info(user, device_info, request):
            """记录设备信息"""
            from .models import DeviceInfo
            import uuid

            # 解析设备信息
            user_agent = request.META.get('HTTP_USER_AGENT', '')
            ip_address = get_client_ip(request)

            # 创建或更新设备信息
            device, created = DeviceInfo.objects.update_or_create(
                user=user,
                device_id=device_info.get('device_id') or str(uuid.uuid4()),
                defaults={
                    'device_type': device_info.get('device_type', 'unknown'),
                    'device_name': device_info.get('device_name', ''),
                    'platform': device_info.get('platform', ''),
                    'browser': device_info.get('browser', ''),
                    'ip_address': ip_address,
                    'user_agent': user_agent,
                    'last_used_at': timezone.now(),
                    'is_active': True,
                }
            )

            if not created:
                # 更新最后使用时间
                device.last_used_at = timezone.now()
                device.save(update_fields=['last_used_at'])

            return device.device_id

        def get_user_active_devices(user):
            """获取用户活跃设备"""
            from .models import DeviceInfo

            devices = DeviceInfo.objects.filter(
                user=user,
                is_active=True
            ).order_by('-last_used_at')

            return [
                {
                    'device_id': device.device_id,
                    'device_type': device.device_type,
                    'device_name': device.device_name,
                    'last_used_at': device.last_used_at.isoformat(),
                }
                for device in devices
            ]

        # JWT自定义声明
        # apps/authentication/jwt_claims.py
        from rest_framework_simplejwt.tokens import RefreshToken
        from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
        rest_framework.exceptions import ValidationError

        class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
            """自定义JWT Token序列化器"""

            def validate(self, attrs):
                """验证并生成带有自定义声明的Token"""
                data = super().validate(attrs)

                # 添加自定义声明到Access Token
                refresh = RefreshToken.for_user(self.user)
                access = refresh.access_token

                # 自定义声明
                custom_claims = {
                    'user_id': self.user.id,
                    'username': self.user.username,
                    'email': self.user.email,
                    'is_staff': self.user.is_staff,
                    'roles': get_user_roles(self.user),
                    'permissions': get_user_permissions(self.user),
                    'device_info': attrs.get('device_info', {}),
                    'issued_at': timezone.now().isoformat(),
                }

                # 将自定义声明添加到Token中
                for claim, value in custom_claims.items():
                    access[claim] = value

                data['access'] = str(access)
                data['refresh'] = str(refresh)

                return data

        # JWT中间件
        # apps/authentication/middleware.py
        from django.utils.deprecation import MiddlewareMixin

        class JWTRefreshMiddleware(MiddlewareMixin):
            """JWT刷新中间件"""

            def process_request(self, request):
                """处理请求"""
                # 检查是否为API请求
                if not request.path.startswith('/api/'):
                    return

                # 检查Authorization头
                auth_header = request.META.get('HTTP_AUTHORIZATION')
                if not auth_header or not auth_header.startswith('Bearer '):
                    return

                try:
                    # 获取Token
                    token_string = auth_header[7:]

                    # 解码Token检查过期时间
                    decoded_token = jwt.decode(
                        token_string,
                        settings.SECRET_KEY,
                        algorithms=[settings.SIMPLE_JWT['ALGORITHM']],
                        options={'verify_exp': True}
                    )

                    # 检查Token是否即将过期(剩余时间少于5分钟)
                    exp_timestamp = decoded_token.get('exp')
                    if exp_timestamp:
                        exp_time = timezone.datetime.fromtimestamp(exp_timestamp, tz=timezone.utc)
                        time_remaining = (exp_time - timezone.now()).total_seconds()

                        if time_remaining < 300:  # 5分钟
                            # 在响应头中添加提示,客户端应该刷新Token
                            request.jwt_refresh_needed = True

                except (jwt.DecodeError, jwt.ExpiredSignatureError):
                    # Token无效或已过期,由认证中间件处理
                    pass
                except Exception:
                    # 其他错误,忽略
                    pass

            def process_response(self, request, response):
                """处理响应"""
                if hasattr(request, 'jwt_refresh_needed') and request.jwt_refresh_needed:
                    response['X-JWT-Refresh-Needed'] = 'true'

                return response

        # 在settings.py中配置JWT
        """
        # JWT配置
        SIMPLE_JWT = {
            'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
            'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
            'ROTATE_REFRESH_TOKENS': True,
            'BLACKLIST_AFTER_ROTATION': True,
            'ALGORITHM': 'HS256',
            'SIGNING_KEY': SECRET_KEY,
            'VERIFYING_KEY': 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_EXP_CLAIM': 'refresh_exp',
            'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
            'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
        }

        REST_FRAMEWORK = {
            'DEFAULT_AUTHENTICATION_CLASSES': [
                'apps.authentication.jwt_auth.CustomJWTAuthentication',
                'apps.authentication.token_auth.ExpiringTokenAuthentication',
                'rest_framework.authentication.SessionAuthentication',
            ],
        }

        # 中间件配置
        MIDDLEWARE = [
            # ... 其他中间件
            'apps.authentication.middleware.JWTRefreshMiddleware',
        ]
        """

5.2 权限控制

01.IsAuthenticated
    a.说明
        登录状态检查
        认证用户访问控制
        权限继承机制
    b.示例
        # apps/permissions/base_permissions.py
        from rest_framework import permissions
        from django.contrib.auth.models import AnonymousUser
        import logging

        logger = logging.getLogger('permissions')

        class IsAuthenticatedCustom(permissions.BasePermission):
            """自定义认证权限类"""

            def has_permission(self, request, view):
                """检查用户是否已认证"""
                is_authenticated = request.user and request.user.is_authenticated

                # 记录权限检查日志
                if not is_authenticated:
                    logger.warning(f"未认证用户尝试访问 {view.__class__.__name__}")

                return is_authenticated

            def has_object_permission(self, request, view, obj):
                """检查对象级权限"""
                # 对于基础认证,对象级权限与请求级权限相同
                return self.has_permission(request, view)

        class IsAdminUser(permissions.BasePermission):
            """管理员权限类"""

            def has_permission(self, request, view):
                """检查用户是否为管理员"""
                is_admin = (
                    request.user and
                    request.user.is_authenticated and
                    (request.user.is_staff or request.user.is_superuser)
                )

                if not is_admin:
                    logger.warning(f"非管理员用户 {request.user} 尝试访问管理员接口 {view.__class__.__name__}")

                return is_admin

            def has_object_permission(self, request, view, obj):
                """检查对象级管理员权限"""
                return self.has_permission(request, view)

        class IsSuperUser(permissions.BasePermission):
            """超级用户权限类"""

            def has_permission(self, request, view):
                """检查用户是否为超级用户"""
                is_superuser = (
                    request.user and
                    request.user.is_authenticated and
                    request.user.is_superuser
                )

                if not is_superuser:
                    logger.warning(f"非超级用户 {request.user} 尝试访问超级用户接口 {view.__class__.__name__}")

                return is_superuser

        class IsOwnerOrReadOnly(permissions.BasePermission):
            """所有者或只读权限类"""

            def has_object_permission(self, request, view, obj):
                """检查对象级权限:所有者可写,其他用户只读"""
                # 读取权限对所有认证用户开放
                if request.method in permissions.SAFE_METHODS:
                    return request.user and request.user.is_authenticated

                # 写入权限只对对象所有者开放
                return obj.author == request.user

            def has_permission(self, request, view):
                """检查请求级权限"""
                # 读取权限对所有用户开放(包括未认证用户)
                if request.method in permissions.SAFE_METHODS:
                    return True

                # 写入权限需要认证
                return request.user and request.user.is_authenticated

        class IsStaffOrReadOnly(permissions.BasePermission):
            """员工或只读权限类"""

            def has_permission(self, request, view):
                """检查请求级权限"""
                # 读取权限对所有用户开放
                if request.method in permissions.SAFE_METHODS:
                    return True

                # 写入权限需要员工权限
                return request.user and request.user.is_authenticated and request.user.is_staff

            def has_object_permission(self, request, view, obj):
                """检查对象级权限"""
                # 读取权限对所有认证用户开放
                if request.method in permissions.SAFE_METHODS:
                    return request.user and request.user.is_authenticated

                # 写入权限需要员工权限
                return request.user and request.user.is_authenticated and request.user.is_staff

        class IsActiveUser(permissions.BasePermission):
            """活跃用户权限类"""

            def has_permission(self, request, view):
                """检查用户是否为活跃用户"""
                is_active = (
                    request.user and
                    request.user.is_authenticated and
                    request.user.is_active
                )

                if not is_active and request.user.is_authenticated:
                    logger.warning(f"非活跃用户 {request.user} 尝试访问接口 {view.__class__.__name__}")

                return is_active

        class IsEmailVerified(permissions.BasePermission):
            """邮箱已验证权限类"""

            def has_permission(self, request, view):
                """检查用户邮箱是否已验证"""
                if not request.user or not request.user.is_authenticated:
                    return False

                # 检查用户模型是否有email_verified字段
                has_email_verified = hasattr(request.user, 'email_verified')
                if has_email_verified:
                    is_verified = request.user.email_verified
                else:
                    # 如果没有email_verified字段,假设邮箱已验证
                    is_verified = True

                if not is_verified:
                    logger.warning(f"邮箱未验证用户 {request.user} 尝试访问接口 {view.__class__.__name__}")

                return is_verified

        class HasValidProfile(permissions.BasePermission):
            """有有效资料的权限类"""

            def has_permission(self, request, view):
                """检查用户是否有有效资料"""
                if not request.user or not request.user.is_authenticated:
                    return False

                # 检查用户是否有资料对象
                has_profile = hasattr(request.user, 'profile')
                if not has_profile:
                    return False

                profile = request.user.profile

                # 检查资料是否完整
                required_fields = ['first_name', 'last_name', 'phone']
                is_complete = all(getattr(profile, field) for field in required_fields)

                if not is_complete:
                    logger.warning(f"资料不完整用户 {request.user} 尝试访问接口 {view.__class__.__name__}")

                return is_complete

        class AgeRestrictedPermission(permissions.BasePermission):
            """年龄限制权限类"""

            def __init__(self, min_age=18):
                self.min_age = min_age

            def has_permission(self, request, view):
                """检查用户年龄是否满足要求"""
                if not request.user or not request.user.is_authenticated:
                    return False

                # 检查用户是否有出生日期
                if not hasattr(request.user, 'birth_date') or not request.user.birth_date:
                    return False

                from datetime import date
                today = date.today()
                age = today.year - request.user.birth_date.year - (
                    (today.month, today.day) < (request.user.birth_date.month, request.user.birth_date.day)
                )

                is_age_ok = age >= self.min_age

                if not is_age_ok:
                    logger.warning(f"年龄不足用户 {request.user} ({age}岁) 尝试访问需要 {self.min_age}+ 岁的接口 {view.__class__.__name__}")

                return is_age_ok

        # 组合权限类
        class IsOwnerOrStaff(permissions.BasePermission):
            """所有者或员工权限类"""

            def has_object_permission(self, request, view, obj):
                """检查对象级权限"""
                # 读取权限对所有认证用户开放
                if request.method in permissions.SAFE_METHODS:
                    return request.user and request.user.is_authenticated

                # 写入权限对对象所有者或员工开放
                return (
                    request.user and
                    request.user.is_authenticated and
                    (obj.author == request.user or request.user.is_staff)
                )

            def has_permission(self, request, view):
                """检查请求级权限"""
                # 读取权限对所有用户开放
                if request.method in permissions.SAFE_METHODS:
                    return True

                # 写入权限需要认证
                return request.user and request.user.is_authenticated

        class IsOwnerOrAdmin(permissions.BasePermission):
            """所有者或管理员权限类"""

            def has_object_permission(self, request, view, obj):
                """检查对象级权限"""
                return (
                    request.user and
                    request.user.is_authenticated and
                    (obj.author == request.user or request.user.is_superuser)
                )

        # 时间段权限类
        class TimeBasedPermission(permissions.BasePermission):
            """基于时间的权限类"""

            def __init__(self, start_hour=9, end_hour=17, timezone_offset=0):
                self.start_hour = start_hour
                self.end_hour = end_hour
                self.timezone_offset = timezone_offset

            def has_permission(self, request, view):
                """检查当前时间是否在允许的时间范围内"""
                from datetime import datetime, timezone, timedelta

                # 获取当前时间(考虑时区偏移)
                now = datetime.now(timezone.utc) + timedelta(hours=self.timezone_offset)
                current_hour = now.hour

                is_time_allowed = self.start_hour <= current_hour <= self.end_hour

                if not is_time_allowed:
                    logger.warning(f"用户 {request.user} 在禁止时间段 {current_hour}时 尝试访问接口 {view.__class__.__name__}")

                return is_time_allowed

        # 权限使用示例
        from rest_framework import generics, viewsets
        from .models import Article, Comment

        class ArticleViewSet(viewsets.ModelViewSet):
            """文章ViewSet - 使用多种权限类"""

            queryset = Article.objects.all()
            serializer_class = ArticleSerializer

            def get_permissions(self):
                """根据动作选择不同的权限类"""
                if self.action == 'list':
                    permission_classes = [permissions.AllowAny]  # 所有人都可以查看文章列表
                elif self.action == 'retrieve':
                    permission_classes = [permissions.AllowAny]  # 所有人都可以查看文章详情
                elif self.action in ['create', 'update', 'partial_update']:
                    permission_classes = [IsAuthenticatedCustom, IsEmailVerified]  # 需要登录和邮箱验证
                elif self.action == 'destroy':
                    permission_classes = [IsAuthenticatedCustom, IsOwnerOrAdmin]  # 所有者或管理员可删除
                elif self.action == 'publish':
                    permission_classes = [IsAuthenticatedCustom, IsStaffOrReadOnly]  # 员工可发布
                else:
                    permission_classes = [IsAuthenticatedCustom]

                return [permission() for permission in permission_classes]

            def get_object(self):
                """获取文章对象时应用对象级权限"""
                obj = super().get_object()

                # 根据文章状态应用不同的权限检查
                if obj.status == 'draft':
                    # 草稿只能作者和员工查看
                    if not (obj.author == self.request.user or self.request.user.is_staff):
                        from rest_framework.exceptions import PermissionDenied
                        raise PermissionDenied("您没有权限查看此草稿文章")

                return obj

        class CommentViewSet(viewsets.ModelViewSet):
            """评论ViewSet - 使用年龄限制权限"""

            queryset = Comment.objects.all()
            serializer_class = CommentSerializer

            def get_permissions(self):
                """根据内容类型选择权限"""
                if self.action == 'list':
                    permission_classes = [permissions.AllowAny]
                elif self.action in ['create']:
                    # 检查文章内容类型,如果包含成人内容,需要年龄限制
                    article_id = self.request.data.get('article')
                    if article_id:
                        try:
                            article = Article.objects.get(id=article_id)
                            if article.content_type == 'adult':
                                permission_classes = [IsAuthenticatedCustom, AgeRestrictedPermission(min_age=18)]
                            else:
                                permission_classes = [IsAuthenticatedCustom]
                        except Article.DoesNotExist:
                            permission_classes = [IsAuthenticatedCustom]
                    else:
                        permission_classes = [IsAuthenticatedCustom]
                elif self.action in ['update', 'partial_update', 'destroy']:
                    permission_classes = [IsAuthenticatedCustom, IsOwnerOrReadOnly]
                else:
                    permission_classes = [permissions.AllowAny]

                return [permission() for permission in permission_classes]

        # 权限装饰器(用于函数视图)
        def permission_required(permission_classes):
            """权限装饰器"""
            def decorator(view_func):
                def wrapper(request, *args, **kwargs):
                    for permission_class in permission_classes:
                        permission = permission_class()

                        # 检查权限
                        if hasattr(permission, 'has_permission'):
                            if not permission.has_permission(request, None):
                                from rest_framework.exceptions import PermissionDenied
                                raise PermissionDenied(f"需要权限: {permission_class.__name__}")

                        # 如果有对象,检查对象级权限
                        if 'pk' in kwargs and hasattr(permission, 'has_object_permission'):
                            try:
                                # 这里需要根据具体情况获取对象
                                # 简化版本,实际使用时需要更复杂的逻辑
                                obj = None  # 需要实现对象获取逻辑
                                if obj and not permission.has_object_permission(request, None, obj):
                                    from rest_framework.exceptions import PermissionDenied
                                    raise PermissionDenied(f"需要对象级权限: {permission_class.__name__}")
                            except Exception:
                                pass

                    return view_func(request, *args, **kwargs)
                return wrapper
            return decorator

        # 使用权限装饰器
        @permission_required([IsAuthenticatedCustom, IsAdminUser])
        def admin_only_view(request):
            """只有管理员可访问的视图"""
            return Response({'message': '管理员专用'})

        # 权限检查工具函数
        def check_user_permission(user, permission_codename):
            """检查用户是否有特定权限"""
            return user.has_perm(f'app.{permission_codename}')

        def check_user_any_permission(user, permission_codenames):
            """检查用户是否有任意一个权限"""
            return any(user.has_perm(f'app.{perm}') for perm in permission_codenames)

        def check_user_all_permissions(user, permission_codenames):
            """检查用户是否有所有权限"""
            return all(user.has_perm(f'app.{perm}') for perm in permission_codenames)

        # 动态权限类
        class DynamicPermission(permissions.BasePermission):
            """动态权限类"""

            def __init__(self, permission_func):
                self.permission_func = permission_func

            def has_permission(self, request, view):
                """动态检查权限"""
                return self.permission_func(request, view)

            def has_object_permission(self, request, view, obj):
                """动态检查对象级权限"""
                return self.permission_func(request, view, obj)

        # 使用动态权限
        class DynamicArticleViewSet(viewsets.ModelViewSet):
        """使用动态权限的文章ViewSet"""

        queryset = Article.objects.all()
        serializer_class = ArticleSerializer

        def get_permissions(self):
            """根据文章状态动态设置权限"""
            def permission_check(request, view, obj=None):
                if obj and obj.status == 'private':
                    # 私有文章只能作者查看
                    return obj.author == request.user
                elif obj and obj.status == 'premium':
                    # 高级文章需要付费或VIP
                    return (hasattr(request.user, 'profile') and
                           request.user.profile.is_vip) or obj.author == request.user
                else:
                    return request.user.is_authenticated

            permission_classes = [lambda: DynamicPermission(permission_check)]
            return [permission() for permission in permission_classes]

b.自定义权限
    a.说明
        业务规则权限
        动态权限验证
        复杂权限逻辑
    b.示例
        # apps/permissions/custom_permissions.py
        from rest_framework import permissions
        from django.contrib.auth.models import AnonymousUser
        from django.utils import timezone
        from django.db.models import Q
        import logging

        logger = logging.getLogger('permissions')

        class CategoryBasedPermission(permissions.BasePermission):
            """基于分类的权限控制"""

            def __init__(self, allowed_categories=None, restricted_categories=None):
                """
                初始化分类权限
                allowed_categories: 允许的分类列表
                restricted_categories: 限制的分类列表
                """
                self.allowed_categories = allowed_categories or []
                self.restricted_categories = restricted_categories or []

            def has_permission(self, request, view):
                """检查请求级权限"""
                # 如果没有设置分类限制,默认允许
                if not self.allowed_categories and not self.restricted_categories:
                    return True

                # 对于有分类限制的操作,需要从请求中获取分类信息
                category_id = request.data.get('category') or request.query_params.get('category')

                if not category_id:
                    # 如果没有指定分类,且有限制的分类,则拒绝
                    if self.restricted_categories:
                        return False
                    # 如果有允许的分类,则允许
                    if self.allowed_categories:
                        return True
                    return True

                return self.check_category_permission(int(category_id), request.user)

            def check_category_permission(self, category_id, user):
                """检查分类权限"""
                from apps.blog.models import Category

                try:
                    category = Category.objects.get(id=category_id)
                except Category.DoesNotExist:
                    return False

                # 检查是否为限制分类
                if category.name in self.restricted_categories:
                    # 管理员可以访问限制分类
                    return user.is_authenticated and user.is_staff

                # 检查是否为允许分类
                if self.allowed_categories:
                    return category.name in self.allowed_categories or user.is_staff

                return True

            def has_object_permission(self, request, view, obj):
                """检查对象级权限"""
                if hasattr(obj, 'category'):
                    return self.check_category_permission(obj.category.id, request.user)
                return True

        class TimeWindowPermission(permissions.BasePermission):
            """时间窗口权限控制"""

            def __init__(self, start_time=None, end_time=None, timezone_offset=0, weekdays_only=False):
                """
                初始化时间窗口权限
                start_time: 开始时间 (HH:MM)
                end_time: 结束时间 (HH:MM)
                timezone_offset: 时区偏移(小时)
                weekdays_only: 是否只在工作日生效
                """
                self.start_time = start_time
                self.end_time = end_time
                self.timezone_offset = timezone_offset
                self.weekdays_only = weekdays_only

            def has_permission(self, request, view):
                """检查当前时间是否在允许的时间窗口内"""
                from datetime import datetime, timezone as dt_timezone, timedelta

                now = datetime.now(dt_timezone.utc) + timedelta(hours=self.timezone_offset)

                # 检查工作日限制
                if self.weekdays_only and now.weekday() >= 5:  # 周六、周日
                    return False

                # 如果没有设置时间限制,默认允许
                if not self.start_time or not self.end_time:
                    return True

                # 解析时间
                start_hour, start_minute = map(int, self.start_time.split(':'))
                end_hour, end_minute = map(int, self.end_time.split(':'))

                current_time = now.time()
                start_time_obj = datetime(now.year, now.month, now.day, start_hour, start_minute).time()
                end_time_obj = datetime(now.year, now.month, now.day, end_hour, end_minute).time()

                # 检查时间是否在范围内
                is_time_allowed = start_time_obj <= current_time <= end_time_obj

                if not is_time_allowed:
                    logger.warning(f"用户 {request.user} 在非允许时间段 {current_time} 尝试访问接口 {view.__class__.__name__}")

                return is_time_allowed

        class QuotaBasedPermission(permissions.BasePermission):
            """基于配额的权限控制"""

            def __init__(self, quota_type='default', quota_limit=100, quota_period='daily'):
                """
                初始化配额权限
                quota_type: 配额类型
                quota_limit: 配额限制数量
                quota_period: 配额周期 ('daily', 'weekly', 'monthly')
                """
                self.quota_type = quota_type
                self.quota_limit = quota_limit
                self.quota_period = quota_period

            def has_permission(self, request, view):
                """检查用户是否超出配额限制"""
                if not request.user.is_authenticated:
                    return False

                # 管理员不受配额限制
                if request.user.is_staff:
                    return True

                # 检查用户配额
                current_usage = self.get_user_quota_usage(request.user)
                is_quota_available = current_usage < self.quota_limit

                if not is_quota_available:
                    logger.warning(f"用户 {request.user} 配额已用完 ({current_usage}/{self.quota_limit})")

                return is_quota_available

            def get_user_quota_usage(self, user):
                """获取用户当前配额使用量"""
                from datetime import timedelta
                from django.utils import timezone
                from .models import UserQuota

                # 确定时间范围
                now = timezone.now()
                if self.quota_period == 'daily':
                    start_time = now.replace(hour=0, minute=0, second=0, microsecond=0)
                elif self.quota_period == 'weekly':
                    start_time = now - timedelta(days=now.weekday())
                    start_time = start_time.replace(hour=0, minute=0, second=0, microsecond=0)
                elif self.quota_period == 'monthly':
                    start_time = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
                else:
                    start_time = now - timedelta(hours=1)

                # 获取使用量
                try:
                    quota_obj, created = UserQuota.objects.get_or_create(
                        user=user,
                        quota_type=self.quota_type,
                        period=self.quota_period,
                        defaults={'usage': 0, 'start_time': start_time}
                    )

                    # 如果是新创建的,重置使用量
                    if created or quota_obj.start_time != start_time:
                        quota_obj.usage = 0
                        quota_obj.start_time = start_time
                        quota_obj.save()

                    return quota_obj.usage

                except Exception as e:
                    logger.error(f"获取用户配额失败: {str(e)}")
                    return 0

            def increment_quota_usage(self, user):
                """增加配额使用量"""
                from .models import UserQuota

                try:
                    quota_obj = UserQuota.objects.get(
                        user=user,
                        quota_type=self.quota_type,
                        period=self.quota_period
                    )
                    quota_obj.usage += 1
                    quota_obj.save()
                    return True
                except UserQuota.DoesNotExist:
                    return False

        class SubscriptionBasedPermission(permissions.BasePermission):
            """基于订阅的权限控制"""

            def __init__(self, required_subscription='premium'):
                """
                初始化订阅权限
                required_subscription: 需要的订阅类型 ('basic', 'premium', 'enterprise')
                """
                self.required_subscription = required_subscription

            def has_permission(self, request, view):
                """检查用户订阅状态"""
                if not request.user.is_authenticated:
                    return False

                # 管理员不受订阅限制
                if request.user.is_staff:
                    return True

                # 检查用户订阅
                user_subscription = self.get_user_subscription(request.user)

                if not user_subscription:
                    return False

                # 检查订阅是否有效
                if not user_subscription.is_active:
                    return False

                # 检查订阅是否过期
                if user_subscription.expires_at and user_subscription.expires_at < timezone.now():
                    return False

                # 检查订阅级别
                subscription_levels = {
                    'basic': 1,
                    'premium': 2,
                    'enterprise': 3
                }

                required_level = subscription_levels.get(self.required_subscription, 1)
                user_level = subscription_levels.get(user_subscription.plan, 0)

                return user_level >= required_level

            def get_user_subscription(self, user):
                """获取用户订阅信息"""
                try:
                    if hasattr(user, 'subscription'):
                        return user.subscription
                    else:
                        from .models import UserSubscription
                        return UserSubscription.objects.filter(user=user, is_active=True).first()
                except Exception as e:
                    logger.error(f"获取用户订阅失败: {str(e)}")
                    return None

        class ContentModerationPermission(permissions.BasePermission):
            """内容审核权限控制"""

            def __init__(self, content_types=None, moderation_level='auto'):
                """
                初始化内容审核权限
                content_types: 内容类型列表
                moderation_level: 审核级别 ('auto', 'manual', 'strict')
                """
                self.content_types = content_types or []
                self.moderation_level = moderation_level

            def has_permission(self, request, view):
                """检查内容审核权限"""
                if not request.user.is_authenticated:
                    return False

                # 管理员和审核员可以管理所有内容
                if request.user.is_staff or self.is_moderator(request.user):
                    return True

                # 检查内容类型限制
                if self.content_types:
                    content_type = request.data.get('content_type') or request.query_params.get('content_type')
                    if content_type not in self.content_types:
                        return False

                # 检查用户内容创建权限
                if self.moderation_level == 'strict':
                    # 严格模式:只有审核过的用户可以创建内容
                    return self.is_approved_creator(request.user)
                elif self.moderation_level == 'manual':
                    # 手动模式:需要审核通过
                    return request.user.profile.content_approved if hasattr(request.user, 'profile') else False
                else:
                    # 自动模式:所有认证用户都可以创建,但内容需要审核
                    return True

            def has_object_permission(self, request, view, obj):
                """检查对象级内容审核权限"""
                # 作者可以修改自己的内容
                if hasattr(obj, 'author') and obj.author == request.user:
                    return True

                # 审核员可以管理所有内容
                if self.is_moderator(request.user):
                    return True

                # 管理员可以管理所有内容
                if request.user.is_staff:
                    return True

                return False

            def is_moderator(self, user):
                """检查用户是否为审核员"""
                try:
                    if hasattr(user, 'profile'):
                        return user.profile.role == 'moderator' or user.profile.is_moderator
                    else:
                        from .models import UserRole
                        return UserRole.objects.filter(
                            user=user,
                            role__name='moderator'
                        ).exists()
                except Exception:
                    return False

            def is_approved_creator(self, user):
                """检查用户是否为认证创建者"""
                try:
                    if hasattr(user, 'profile'):
                        return user.profile.content_approved and user.profile.is_verified
                    return False
                except Exception:
                    return False

        class LocationBasedPermission(permissions.BasePermission):
            """基于地理位置的权限控制"""

            def __init__(self, allowed_countries=None, restricted_regions=None):
                """
                初始化地理位置权限
                allowed_countries: 允许的国家列表
                restricted_regions: 限制的地区列表
                """
                self.allowed_countries = allowed_countries or []
                self.restricted_regions = restricted_regions or []

            def has_permission(self, request, view):
                """检查用户地理位置权限"""
                if not request.user.is_authenticated:
                    return False

                # 管理员不受地理位置限制
                if request.user.is_staff:
                    return True

                # 获取用户地理位置
                user_location = self.get_user_location(request.user)

                if not user_location:
                    return False

                # 检查国家限制
                if self.allowed_countries and user_location['country'] not in self.allowed_countries:
                    return False

                # 检查地区限制
                if self.restricted_regions and user_location['region'] in self.restricted_regions:
                    return False

                return True

            def get_user_location(self, user):
                """获取用户地理位置"""
                try:
                    # 从用户资料中获取地理位置
                    if hasattr(user, 'profile') and hasattr(user.profile, 'location'):
                        return {
                            'country': user.profile.location.country,
                            'region': user.profile.location.region,
                            'city': user.profile.location.city
                        }

                    # 从最近的登录记录中获取地理位置
                    from .models import UserLoginLog
                    login_log = UserLoginLog.objects.filter(user=user).order_by('-login_time').first()
                    if login_log and login_log.location:
                        return login_log.location

                    return None

                except Exception as e:
                    logger.error(f"获取用户地理位置失败: {str(e)}")
                    return None

        class ApiKeyPermission(permissions.BasePermission):
            """API密钥权限控制"""

            def __init__(self, required_permissions=None):
                """
                初始化API密钥权限
                required_permissions: 需要的权限列表
                """
                self.required_permissions = required_permissions or []

            def has_permission(self, request, view):
                """检查API密钥权限"""
                # 从请求头中获取API密钥
                api_key = request.META.get('HTTP_X_API_KEY')
                if not api_key:
                    api_key = request.GET.get('api_key')

                if not api_key:
                    return False

                # 验证API密钥
                try:
                    from .models import ApiKey
                    key_obj = ApiKey.objects.get(key=api_key, is_active=True)

                    # 检查密钥是否过期
                    if key_obj.expires_at and key_obj.expires_at < timezone.now():
                        return False

                    # 检查权限
                    if self.required_permissions:
                        key_permissions = set(key_obj.permissions.values_list('codename', flat=True))
                        required_permissions_set = set(self.required_permissions)

                        if not required_permissions_set.issubset(key_permissions):
                            return False

                    # 设置API密钥用户
                    request.api_user = key_obj.user
                    request.api_key = key_obj

                    return True

                except ApiKey.DoesNotExist:
                    return False

        # 复合权限类
        class CompositePermission(permissions.BasePermission):
            """复合权限类 - 组合多个权限条件"""

            def __init__(self, permission_classes, operator='AND'):
                """
                初始化复合权限
                permission_classes: 权限类列表
                operator: 逻辑操作符 ('AND', 'OR')
                """
                self.permission_classes = permission_classes
                self.operator = operator.upper()

            def has_permission(self, request, view):
                """检查复合权限"""
                results = []

                for permission_class in self.permission_classes:
                    permission = permission_class()
                    result = permission.has_permission(request, view)
                    results.append(result)

                    # 短路优化
                    if self.operator == 'AND' and not result:
                        return False
                    elif self.operator == 'OR' and result:
                        return True

                # 根据操作符返回结果
                if self.operator == 'AND':
                    return all(results)
                elif self.operator == 'OR':
                    return any(results)
                else:
                    return False

            def has_object_permission(self, request, view, obj):
                """检查复合对象级权限"""
                results = []

                for permission_class in self.permission_classes:
                    permission = permission_class()
                    result = permission.has_object_permission(request, view, obj)
                    results.append(result)

                    # 短路优化
                    if self.operator == 'AND' and not result:
                        return False
                    elif self.operator == 'OR' and result:
                        return True

                # 根据操作符返回结果
                if self.operator == 'AND':
                    return all(results)
                elif self.operator == 'OR':
                    return any(results)
                else:
                    return False

        # 权限使用示例
        from rest_framework import viewsets
        from .models import Article, Product

        class ArticleViewSet(viewsets.ModelViewSet):
            """使用自定义权限的文章ViewSet"""

            queryset = Article.objects.all()
            serializer_class = ArticleSerializer

            def get_permissions(self):
                """根据操作选择权限类"""
                if self.action == 'list':
                    # 基础列表权限:任何时间都可以查看
                    permission_classes = [permissions.AllowAny]
                elif self.action == 'retrieve':
                    # 详情权限:需要登录 + 邮箱验证 + 时间窗口限制
                    permission_classes = [
                        IsAuthenticatedCustom,
                        IsEmailVerified,
                        TimeWindowPermission(start_time='09:00', end_time='21:00')
                    ]
                elif self.action == 'create':
                    # 创建权限:需要登录 + 邮箱验证 + 配额限制 + 订阅检查
                    permission_classes = [
                        IsAuthenticatedCustom,
                        IsEmailVerified,
                        QuotaBasedPermission(quota_type='article_creation', quota_limit=10, quota_period='daily'),
                        SubscriptionBasedPermission(required_subscription='basic')
                    ]
                elif self.action == 'update':
                    # 更新权限:所有者或内容审核员
                    permission_classes = [
                        CompositePermission([
                            IsOwnerOrReadOnly,
                            ContentModerationPermission(content_types=['article'])
                        ], operator='OR')
                    ]
                elif self.action == 'destroy':
                    # 删除权限:所有者或管理员 + 严格内容审核
                    permission_classes = [
                        IsAuthenticatedCustom,
                        CompositePermission([
                            IsOwnerOrAdmin,
                            ContentModerationPermission(moderation_level='strict')
                        ], operator='OR')
                    ]
                elif self.action == 'publish':
                    # 发布权限:员工 + 地理位置限制
                    permission_classes = [
                        IsStaffOrReadOnly,
                        LocationBasedPermission(allowed_countries=['CN', 'US', 'UK'])
                    ]
                else:
                    permission_classes = [IsAuthenticatedCustom]

                return [permission() for permission in permission_classes]

            def perform_create(self, serializer):
                """创建文章时增加配额使用量"""
                super().perform_create(serializer)

                # 增加用户配额使用量
                quota_permission = QuotaBasedPermission(
                    quota_type='article_creation',
                    quota_limit=10,
                    quota_period='daily'
                )
                quota_permission.increment_quota_usage(self.request.user)

        class ProductViewSet(viewsets.ModelViewSet):
        """使用API密钥权限的产品ViewSet"""

        queryset = Product.objects.all()
        serializer_class = ProductSerializer

        def get_permissions(self):
            """根据操作选择权限类"""
            if self.action in ['list', 'retrieve']:
                # 公开接口
                permission_classes = [permissions.AllowAny]
            elif self.action in ['create', 'update', 'partial_update', 'destroy']:
                # 需要API密钥的接口
                permission_classes = [
                    ApiKeyPermission(required_permissions=['product_management'])
                ]
            else:
                permission_classes = [permissions.AllowAny]

            return [permission() for permission in permission_classes]

c.对象级权限
    a.说明
        所有权验证
        级别权限控制
        动态对象权限
    b.示例
        # apps/permissions/object_permissions.py
        from rest_framework import permissions
        from django.contrib.contenttypes.models import ContentType
        from django.contrib.auth.models import Permission
        import logging

        logger = logging.getLogger('permissions')

        class ObjectOwnershipPermission(permissions.BasePermission):
            """对象所有权权限"""

            def __init__(self, owner_field='author', allow_staff=True, allow_superuser=True):
                """
                初始化对象所有权权限
                owner_field: 所有者字段名
                allow_staff: 是否允许员工访问
                allow_superuser: 是否允许超级用户访问
                """
                self.owner_field = owner_field
                self.allow_staff = allow_staff
                self.allow_superuser = allow_superuser

            def has_object_permission(self, request, view, obj):
                """检查对象所有权"""
                if not request.user.is_authenticated:
                    return False

                # 获取对象所有者
                owner = getattr(obj, self.owner_field, None)
                if not owner:
                    return False

                # 检查是否为所有者
                if owner == request.user:
                    return True

                # 检查员工权限
                if self.allow_staff and request.user.is_staff:
                    return True

                # 检查超级用户权限
                if self.allow_superuser and request.user.is_superuser:
                    return True

                return False

        class HierarchicalPermission(permissions.BasePermission):
            """分层权限控制"""

            def __init__(self, hierarchy_field='level', min_level=None, max_level=None):
                """
                初始化分层权限
                hierarchy_field: 分级字段名
                min_level: 最小层级
                max_level: 最大层级
                """
                self.hierarchy_field = hierarchy_field
                self.min_level = min_level
                self.max_level = max_level

            def has_permission(self, request, view):
                """检查请求级分层权限"""
                if not request.user.is_authenticated:
                    return False

                # 管理员不受层级限制
                if request.user.is_staff:
                    return True

                # 获取用户层级
                user_level = self.get_user_level(request.user)

                # 检查最小层级要求
                if self.min_level is not None and user_level < self.min_level:
                    return False

                # 检查最大层级限制
                if self.max_level is not None and user_level > self.max_level:
                    return False

                return True

            def has_object_permission(self, request, view, obj):
                """检查对象级分层权限"""
                if not request.user.is_authenticated:
                    return False

                # 管理员不受层级限制
                if request.user.is_staff:
                    return True

                # 获取对象层级
                obj_level = getattr(obj, self.hierarchy_field, 0)

                # 获取用户层级
                user_level = self.get_user_level(request.user)

                # 用户只能访问等于或低于自己层级的对象
                return user_level >= obj_level

            def get_user_level(self, user):
                """获取用户层级"""
                try:
                    if hasattr(user, 'profile'):
                        return getattr(user.profile, 'level', 0)
                    else:
                        # 根据用户角色确定层级
                        if user.is_superuser:
                            return 10
                        elif user.is_staff:
                            return 5
                        else:
                            return 1
                except Exception:
                    return 0

        class TeamBasedPermission(permissions.BasePermission):
            """基于团队的权限控制"""

            def __init__(self, action_type='read'):
                """
                初始化团队权限
                action_type: 操作类型 ('read', 'write', 'manage', 'admin')
                """
                self.action_type = action_type

            def has_permission(self, request, view):
                """检查请求级团队权限"""
                if not request.user.is_authenticated:
                    return False

                # 管理员不受团队限制
                if request.user.is_staff:
                    return True

                return self.check_team_permission(request.user, self.action_type)

            def has_object_permission(self, request, view, obj):
                """检查对象级团队权限"""
                if not request.user.is_authenticated:
                    return False

                # 管理员不受团队限制
                if request.user.is_staff:
                    return True

                # 获取对象所属团队
                team = getattr(obj, 'team', None)
                if not team:
                    return False

                # 检查用户是否在团队中
                if not self.is_user_in_team(request.user, team):
                    return False

                # 检查用户在团队中的权限
                user_role = self.get_user_team_role(request.user, team)
                return self.check_role_permission(user_role, self.action_type)

            def check_team_permission(self, user, action_type):
                """检查用户的团队权限"""
                try:
                    # 获取用户所有团队角色
                    from .models import TeamMembership
                    memberships = TeamMembership.objects.filter(user=user, is_active=True)

                    for membership in memberships:
                        role = membership.role
                        if self.check_role_permission(role, action_type):
                            return True

                    return False

                except Exception as e:
                    logger.error(f"检查团队权限失败: {str(e)}")
                    return False

            def is_user_in_team(self, user, team):
                """检查用户是否在团队中"""
                try:
                    from .models import TeamMembership
                    return TeamMembership.objects.filter(
                        user=user,
                        team=team,
                        is_active=True
                    ).exists()
                except Exception:
                    return False

            def get_user_team_role(self, user, team):
                """获取用户在团队中的角色"""
                try:
                    from .models import TeamMembership
                    membership = TeamMembership.objects.get(
                        user=user,
                        team=team,
                        is_active=True
                    )
                    return membership.role
                except Exception:
                    return None

            def check_role_permission(self, role, action_type):
                """检查角色权限"""
                if not role:
                    return False

                role_permissions = {
                    'owner': ['read', 'write', 'manage', 'admin'],
                    'admin': ['read', 'write', 'manage'],
                    'manager': ['read', 'write'],
                    'member': ['read'],
                    'viewer': ['read'],
                }

                allowed_actions = role_permissions.get(role.name, [])
                return action_type in allowed_actions

        class CustomObjectPermission(permissions.BasePermission):
            """自定义对象权限"""

            def __init__(self, permission_checker=None):
                """
                初始化自定义对象权限
                permission_checker: 权限检查函数
                """
                self.permission_checker = permission_checker

            def has_permission(self, request, view):
                """检查请求级权限"""
                # 对于对象级权限,请求级通常只检查认证状态
                return request.user and request.user.is_authenticated

            def has_object_permission(self, request, view, obj):
                """检查对象级权限"""
                if not request.user.is_authenticated:
                    return False

                if self.permission_checker:
                    return self.permission_checker(request.user, obj, request)

                return True

        class ConditionalObjectPermission(permissions.BasePermission):
            """条件对象权限"""

            def __init__(self, condition_func, permission_func):
                """
                初始化条件对象权限
                condition_func: 条件检查函数
                permission_func: 权限检查函数
                """
                self.condition_func = condition_func
                self.permission_func = permission_func

            def has_permission(self, request, view):
                """检查请求级权限"""
                return request.user and request.user.is_authenticated

            def has_object_permission(self, request, view, obj):
                """检查条件对象权限"""
                if not request.user.is_authenticated:
                    return False

                # 检查条件
                if self.condition_func(request, obj):
                    return self.permission_func(request.user, obj, request)

                return True

        # 权限检查函数
        def can_edit_article(user, article, request=None):
            """检查用户是否可以编辑文章"""
            # 作者可以编辑自己的文章
            if article.author == user:
                return True

            # 管理员可以编辑所有文章
            if user.is_staff:
                return True

            # 检查文章状态和编辑权限
            if article.status == 'published':
                # 已发布的文章需要特殊权限
                return user.has_perm('blog.edit_published_article')
            elif article.status == 'draft':
                # 草稿文章作者和编辑可以编辑
                return user.has_perm('blog.edit_draft_article')

            return False

        def can_delete_comment(user, comment, request=None):
            """检查用户是否可以删除评论"""
            # 评论作者可以删除自己的评论
            if comment.author == user:
                return True

            # 文章作者可以删除自己文章下的评论
            if comment.article.author == user:
                return True

            # 管理员可以删除所有评论
            if user.is_staff:
                return True

            # 检查评论审核权限
            if user.has_perm('blog.moderate_comment'):
                return True

            return False

        def can_access_private_article(user, article, request=None):
            """检查用户是否可以访问私有文章"""
            # 作者总是可以访问自己的文章
            if article.author == user:
                return True

            # 管理员可以访问所有文章
            if user.is_staff:
                return True

            # 检查用户是否在文章的允许访问列表中
            if hasattr(article, 'allowed_users'):
                return user in article.allowed_users.all()

            # 检查用户是否有私有文章访问权限
            if user.has_perm('blog.access_private_article'):
                return True

            # 检查用户是否通过分享链接访问
            if request and 'share_token' in request.GET:
                return article.validate_share_token(request.GET['share_token'])

            return False

        def can_manage_team_project(user, project, request=None):
            """检查用户是否可以管理团队项目"""
            # 获取项目团队
            team = project.team if hasattr(project, 'team') else None
            if not team:
                return False

            # 检查用户团队权限
            from .models import TeamMembership
            try:
                membership = TeamMembership.objects.get(
                    user=user,
                    team=team,
                    is_active=True
                )

                # 项目创建者可以管理
                if hasattr(project, 'creator') and project.creator == user:
                    return True

                # 团队管理员可以管理
                if membership.role.name in ['owner', 'admin']:
                    return True

                # 项目管理员可以管理
                if hasattr(project, 'managers') and user in project.managers.all():
                    return True

                return False

            except TeamMembership.DoesNotExist:
                return False

        # 条件检查函数
        def is_article_published(request, obj):
            """检查文章是否已发布"""
            return getattr(obj, 'status', None) == 'published'

        def is_user_article_author(request, obj):
            """检查用户是否为文章作者"""
            return hasattr(request, 'user') and request.user.is_authenticated and getattr(obj, 'author', None) == request.user

        def is_weekday_time(request, obj):
            """检查是否为工作时间"""
            from datetime import datetime
            return datetime.now().weekday() < 5  # 周一到周五

        # 权限使用示例
        from rest_framework import viewsets
        from .models import Article, Comment, Project

        class ArticleViewSet(viewsets.ModelViewSet):
            """使用对象级权限的文章ViewSet"""

            queryset = Article.objects.all()
            serializer_class = ArticleSerializer

            def get_permissions(self):
                """根据操作选择权限类"""
                if self.action in ['list', 'retrieve']:
                    permission_classes = [permissions.AllowAny]
                elif self.action == 'create':
                    permission_classes = [permissions.IsAuthenticated]
                elif self.action in ['update', 'partial_update']:
                    permission_classes = [
                        permissions.IsAuthenticated,
                        CustomObjectPermission(permission_checker=can_edit_article)
                    ]
                elif self.action == 'destroy':
                    permission_classes = [
                        permissions.IsAuthenticated,
                        CustomObjectPermission(permission_checker=lambda u, o, r: o.author == u or u.is_staff)
                    ]
                else:
                    permission_classes = [permissions.IsAuthenticated]

                return [permission() for permission in permission_classes]

        class CommentViewSet(viewsets.ModelViewSet):
            """使用对象级权限的评论ViewSet"""

            queryset = Comment.objects.all()
            serializer_class = CommentSerializer

            def get_permissions(self):
                """根据操作选择权限类"""
                if self.action in ['list', 'retrieve']:
                    permission_classes = [permissions.AllowAny]
                elif self.action == 'create':
                    permission_classes = [permissions.IsAuthenticated]
                elif self.action in ['update', 'partial_update', 'destroy']:
                    permission_classes = [
                        permissions.IsAuthenticated,
                        CustomObjectPermission(permission_checker=can_delete_comment)
                    ]
                else:
                    permission_classes = [permissions.IsAuthenticated]

                return [permission() for permission in permission_classes]

        class PrivateArticleViewSet(viewsets.ModelViewSet):
            """私有文章ViewSet - 使用条件对象权限"""

            queryset = Article.objects.filter(status='private')
            serializer_class = ArticleSerializer

            def get_permissions(self):
                """根据操作选择权限类"""
                permission_classes = [
                    permissions.IsAuthenticated,
                    ConditionalObjectPermission(
                        condition_func=is_article_published,
                        permission_func=can_access_private_article
                    )
                ]

                return [permission() for permission in permission_classes]

        class ProjectViewSet(viewsets.ModelViewSet):
            """项目ViewSet - 使用团队权限"""

            queryset = Project.objects.all()
            serializer_class = ProjectSerializer

            def get_permissions(self):
                """根据操作选择权限类"""
                if self.action in ['list', 'retrieve']:
                    permission_classes = [permissions.AllowAny]
                elif self.action == 'create':
                    permission_classes = [permissions.IsAuthenticated]
                elif self.action in ['update', 'partial_update', 'destroy']:
                    permission_classes = [
                        permissions.IsAuthenticated,
                        CustomObjectPermission(permission_checker=can_manage_team_project)
                    ]
                else:
                    permission_classes = [permissions.IsAuthenticated]

                return [permission() for permission in permission_classes]

        # 分层权限示例
        class ConfidentialDocumentViewSet(viewsets.ModelViewSet):
            """机密文档ViewSet - 使用分层权限"""

            queryset = Document.objects.all()
            serializer_class = DocumentSerializer

            def get_permissions(self):
                """根据操作选择权限类"""
                if self.action in ['list', 'retrieve']:
                    permission_classes = [
                        permissions.IsAuthenticated,
                        HierarchicalPermission(min_level=3)  # 至少3级才能查看
                    ]
                elif self.action == 'create':
                    permission_classes = [
                        permissions.IsAuthenticated,
                        HierarchicalPermission(min_level=2)  # 至少2级才能创建
                    ]
                elif self.action in ['update', 'partial_update']:
                    permission_classes = [
                        permissions.IsAuthenticated,
                        HierarchicalPermission(min_level=4)  # 至少4级才能修改
                    ]
                elif self.action == 'destroy':
                    permission_classes = [
                        permissions.IsAuthenticated,
                        HierarchicalPermission(min_level=5)  # 至少5级才能删除
                    ]
                else:
                    permission_classes = [permissions.IsAuthenticated]

                return [permission() for permission in permission_classes]

            def get_queryset(self):
                """根据用户层级过滤查询集"""
                user_level = self.get_user_level(self.request.user)

                # 用户只能查看等于或低于自己层级的文档
                return Document.objects.filter(level__lte=user_level)

            def get_user_level(self, user):
                """获取用户层级"""
                if hasattr(user, 'profile'):
                    return getattr(user.profile, 'level', 0)
                elif user.is_superuser:
                    return 10
                elif user.is_staff:
                    return 5
                else:
                    return 1

        # 动态权限验证
        class DynamicPermissionViewSet(viewsets.ModelViewSet):
        """动态权限验证ViewSet"""

        queryset = Article.objects.all()
        serializer_class = ArticleSerializer

        def get_permissions(self):
            """动态选择权限类"""
            permission_classes = []

            # 基础认证要求
            permission_classes.append(permissions.IsAuthenticated)

            # 根据请求动态添加权限
            if self.action == 'create':
                # 检查创建权限
                if not self.check_create_permission():
                    permission_classes.append(permissions.IsAdminUser)

            elif self.action == 'update':
                # 检查更新权限
                if not self.check_update_permission():
                    permission_classes.append(permissions.IsAdminUser)

            return [permission() for permission in permission_classes]

        def check_create_permission(self):
            """动态检查创建权限"""
            user = self.request.user

            # 检查用户创建配额
            from django.utils import timezone
            from datetime import timedelta
            today = timezone.now().date()

            created_count = Article.objects.filter(
                author=user,
                created_at__date=today
            ).count()

            max_articles_per_day = getattr(user.profile, 'max_articles_per_day', 5) if hasattr(user, 'profile') else 5

            return created_count < max_articles_per_day

        def check_update_permission(self):
            """动态检查更新权限"""
            user = self.request.user

            # 检查用户最后更新时间
            from django.utils import timezone
            from datetime import timedelta

            last_update = Article.objects.filter(
                author=user
            ).order_by('-updated_at').first()

            if last_update:
                time_since_last_update = timezone.now() - last_update.updated_at
                min_interval = timedelta(minutes=5)  # 5分钟内不能连续更新

                if time_since_last_update < min_interval:
                    return False

            return True

5.3 用户管理

a.多步骤注册流程
    a.说明
        验证码发送和验证
        密码强度检查
        用户资料完善
    b.示例
        # apps/user_management/registration.py
        from rest_framework import status, permissions
        from rest_framework.decorators import api_view, permission_classes
        from rest_framework.response import Response
        from rest_framework.views import APIView
        from django.contrib.auth import get_user_model
        from django.core.exceptions import ValidationError
        from django.core.mail import send_mail
        from django.conf import settings
        from django.utils import timezone
        from django.shortcuts import get_object_or_404
        from django.utils.crypto import get_random_string
        from django.template.loader import render_to_string
        import logging
        import re
        import secrets
        import hashlib

        User = get_user_model()
        logger = logging.getLogger('registration')

        class UserRegistrationView(APIView):
            """用户注册视图"""

            permission_classes = [permissions.AllowAny]

            def post(self, request):
                """用户注册"""
                data = request.data

                # 验证必需字段
                required_fields = ['username', 'email', 'password', 'password_confirm']
                for field in required_fields:
                    if not data.get(field):
                        return Response({
                            'success': False,
                            'message': f'{field}不能为空',
                            'error_code': 'MISSING_FIELD',
                            'field': field
                        }, status=status.HTTP_400_BAD_REQUEST)

                try:
                    # 验证用户名
                    self.validate_username(data['username'])

                    # 验证邮箱
                    self.validate_email(data['email'])

                    # 验证密码
                    self.validate_passwords(data['password'], data['password_confirm'])

                    # 验证手机号(可选)
                    if data.get('phone'):
                        self.validate_phone(data['phone'])

                    # 检查验证码(如果启用)
                    if self.is_verification_required():
                        verification_result = self.verify_registration_code(request, data)
                        if not verification_result['success']:
                            return Response(verification_result, status=status.HTTP_400_BAD_REQUEST)

                    # 创建用户
                    user = self.create_user(data)

                    # 发送欢迎邮件
                    self.send_welcome_email(user)

                    # 记录注册日志
                    logger.info(f"用户注册成功: {user.username}")

                    return Response({
                        'success': True,
                        'message': '注册成功',
                        'data': {
                            'user_id': user.id,
                            'username': user.username,
                            'email': user.email,
                            'is_active': user.is_active,
                            'requires_verification': self.is_verification_required() and not user.is_active
                        }
                    }, status=status.HTTP_201_CREATED)

                except ValidationError as e:
                    return Response({
                        'success': False,
                        'message': str(e),
                        'error_code': 'VALIDATION_ERROR'
                    }, status=status.HTTP_400_BAD_REQUEST)

                except Exception as e:
                    logger.error(f"用户注册失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '注册失败,请稍后重试',
                        'error_code': 'REGISTRATION_ERROR'
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            def validate_username(self, username):
                """验证用户名"""
                # 长度检查
                if len(username) < 3:
                    raise ValidationError('用户名至少3个字符')
                if len(username) > 30:
                    raise ValidationError('用户名不能超过30个字符')

                # 格式检查
                if not re.match(r'^[a-zA-Z0-9_]+$', username):
                    raise ValidationError('用户名只能包含字母、数字和下划线')

                # 唯一性检查
                if User.objects.filter(username__iexact=username).exists():
                    raise ValidationError('该用户名已被使用')

                # 敏感词检查
                sensitive_words = ['admin', 'root', 'system', 'test', 'api']
                if username.lower() in sensitive_words:
                    raise ValidationError('用户名包含敏感词,请更换')

            def validate_email(self, email):
                """验证邮箱"""
                # 格式检查
                email_regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
                if not re.match(email_regex, email):
                    raise ValidationError('邮箱格式不正确')

                # 域名检查
                forbidden_domains = ['tempmail.com', '10minutemail.com']
                domain = email.split('@')[1].lower()
                if domain in forbidden_domains:
                    raise ValidationError('不支持临时邮箱')

                # 唯一性检查
                if User.objects.filter(email__iexact=email).exists():
                    raise ValidationError('该邮箱已被注册')

            def validate_passwords(self, password, password_confirm):
                """验证密码"""
                # 确认密码
                if password != password_confirm:
                    raise ValidationError('两次输入的密码不一致')

                # 长度检查
                if len(password) < 8:
                    raise ValidationError('密码至少8个字符')
                if len(password) > 128:
                    raise ValidationError('密码不能超过128个字符')

                # 强度检查
                if not re.search(r'[a-z]', password):
                    raise ValidationError('密码必须包含小写字母')
                if not re.search(r'[A-Z]', password):
                    raise ValidationError('密码必须包含大写字母')
                if not re.search(r'\d', password):
                    raise ValidationError('密码必须包含数字')
                if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
                    raise ValidationError('密码必须包含特殊字符')

                # 弱密码检查
                weak_passwords = [
                    'password', '123456', '12345678', 'qwerty', 'abc123',
                    'password123', 'admin123', 'root123', 'test123'
                ]
                if password.lower() in weak_passwords:
                    raise ValidationError('不能使用常见弱密码')

                # 与用户名的相似性检查
                username = self.request.data.get('username', '')
                if username and username.lower() in password.lower():
                    raise ValidationError('密码不能包含用户名')

            def validate_phone(self, phone):
                """验证手机号"""
                phone_regex = r'^1[3-9]\d{9}$'
                if not re.match(phone_regex, phone.replace('-', '').replace(' ', '')):
                    raise ValidationError('手机号格式不正确')

            def is_verification_required(self):
                """检查是否需要验证"""
                return getattr(settings, 'REGISTRATION_VERIFICATION_REQUIRED', True)

            def verify_registration_code(self, request, data):
                """验证注册验证码"""
                verification_type = data.get('verification_type', 'email')
                code = data.get('verification_code')

                if not code:
                    return {
                        'success': False,
                        'message': '验证码不能为空',
                        'error_code': 'MISSING_VERIFICATION_CODE'
                    }

                try:
                    from .models import VerificationCode
                    verification_obj = VerificationCode.objects.filter(
                        contact=data['email'] if verification_type == 'email' else data['phone'],
                        code=code,
                        verification_type='registration',
                        is_used=False,
                        expires_at__gt=timezone.now()
                    ).latest('created_at')

                    if not verification_obj:
                        return {
                            'success': False,
                            'message': '验证码无效或已过期',
                            'error_code': 'INVALID_VERIFICATION_CODE'
                        }

                    # 标记验证码为已使用
                    verification_obj.is_used = True
                    verification_obj.used_at = timezone.now()
                    verification_obj.save()

                    return {'success': True}

                except Exception as e:
                    logger.error(f"验证注册码失败: {str(e)}")
                    return {
                        'success': False,
                        'message': '验证码验证失败',
                        'error_code': 'VERIFICATION_ERROR'
                    }

            def create_user(self, data):
                """创建用户"""
                user = User.objects.create_user(
                    username=data['username'],
                    email=data['email'],
                    password=data['password'],
                    is_active=not self.is_verification_required()
                )

                # 创建用户资料
                self.create_user_profile(user, data)

                return user

            def create_user_profile(self, user, data):
                """创建用户资料"""
                from .models import UserProfile

                profile = UserProfile.objects.create(
                    user=user,
                    first_name=data.get('first_name', ''),
                    last_name=data.get('last_name', ''),
                    phone=data.get('phone', ''),
                    birth_date=data.get('birth_date'),
                    gender=data.get('gender', ''),
                )

                # 设置头像(如果有)
                if data.get('avatar'):
                    profile.avatar = data['avatar']
                    profile.save()

            def send_welcome_email(self, user):
                """发送欢迎邮件"""
                try:
                    subject = '欢迎注册我们的平台'
                    html_message = render_to_string('user_management/welcome_email.html', {
                        'user': user,
                        'site_name': getattr(settings, 'SITE_NAME', 'Our Platform'),
                        'support_email': getattr(settings, 'SUPPORT_EMAIL', '[email protected]')
                    })

                    send_mail(
                        subject=subject,
                        message='',  # 纯文本版本为空
                        from_email=getattr(settings, 'DEFAULT_FROM_EMAIL'),
                        recipient_list=[user.email],
                        html_message=html_message,
                        fail_silently=False,
                    )

                    logger.info(f"欢迎邮件发送成功: {user.email}")

                except Exception as e:
                    logger.error(f"发送欢迎邮件失败: {str(e)}")

        @api_view(['POST'])
        @permission_classes([permissions.AllowAny])
        def send_verification_code(request):
            """发送验证码"""
            contact = request.data.get('contact')  # 邮箱或手机号
            verification_type = request.data.get('verification_type', 'email')  # email 或 sms
            purpose = request.data.get('purpose', 'registration')  # registration 或 reset_password

            if not contact:
                return Response({
                    'success': False,
                    'message': '邮箱或手机号不能为空',
                    'error_code': 'MISSING_CONTACT'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                # 验证联系方式
                if verification_type == 'email':
                    email_regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
                    if not re.match(email_regex, contact):
                        return Response({
                            'success': False,
                            'message': '邮箱格式不正确',
                            'error_code': 'INVALID_EMAIL_FORMAT'
                        }, status=status.HTTP_400_BAD_REQUEST)
                elif verification_type == 'sms':
                    phone_regex = r'^1[3-9]\d{9}$'
                    if not re.match(phone_regex, contact.replace('-', '').replace(' ', '')):
                        return Response({
                            'success': False,
                            'message': '手机号格式不正确',
                            'error_code': 'INVALID_PHONE_FORMAT'
                        }, status=status.HTTP_400_BAD_REQUEST)

                # 检查发送频率限制
                if not self.check_send_limit(contact, verification_type):
                    return Response({
                        'success': False,
                        'message': '发送频率过高,请稍后再试',
                        'error_code': 'SEND_LIMIT_EXCEEDED'
                    }, status=status.HTTP_429_TOO_MANY_REQUEST)

                # 生成验证码
                code = self.generate_verification_code()

                # 保存验证码记录
                from .models import VerificationCode
                verification_obj = VerificationCode.objects.create(
                    contact=contact,
                    code=code,
                    verification_type=verification_type,
                    purpose=purpose,
                    expires_at=timezone.now() + timezone.timedelta(minutes=10)  # 10分钟过期
                )

                # 发送验证码
                if verification_type == 'email':
                    self.send_email_verification(contact, code, purpose)
                else:
                    self.send_sms_verification(contact, code, purpose)

                return Response({
                    'success': True,
                    'message': '验证码发送成功',
                    'data': {
                        'expires_in': 600,  # 10分钟
                    }
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"发送验证码失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '验证码发送失败',
                    'error_code': 'SEND_VERIFICATION_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        def check_send_limit(self, contact, verification_type):
            """检查发送频率限制"""
            from .models import VerificationCode
            from datetime import timedelta

            # 5分钟内最多发送3次
            recent_count = VerificationCode.objects.filter(
                contact=contact,
                verification_type=verification_type,
                created_at__gte=timezone.now() - timedelta(minutes=5)
            ).count()

            return recent_count < 3

        def generate_verification_code(self):
            """生成验证码"""
            return str(secrets.randbelow(1000000)).zfill(6)

        def send_email_verification(self, email, code, purpose):
            """发送邮件验证码"""
            try:
                subject = '验证码' if purpose == 'registration' else '重置密码验证码'
                html_message = render_to_string('user_management/email_verification.html', {
                    'code': code,
                    'purpose': purpose,
                    'site_name': getattr(settings, 'SITE_NAME', 'Our Platform'),
                    'expires_in': 10
                })

                send_mail(
                    subject=subject,
                    message='',
                    from_email=getattr(settings, 'DEFAULT_FROM_EMAIL'),
                    recipient_list=[email],
                    html_message=html_message,
                    fail_silently=False,
                )

            except Exception as e:
                logger.error(f"发送邮件验证码失败: {str(e)}")
                raise

        def send_sms_verification(self, phone, code, purpose):
            """发送短信验证码"""
            try:
                # 这里应该集成短信服务提供商的API
                # 示例中使用阿里云短信服务
                from aliyunsdkcore.client import AcsClient
                from aliyunsdkcore.acs_exception.exceptions import ServerException
                from aliyunsdkdysmsapi.request.v20170525 import SendSmsRequest

                client = AcsClient(
                    getattr(settings, 'ALIYUN_ACCESS_KEY_ID'),
                    getattr(settings, 'ALIYUN_ACCESS_KEY_SECRET'),
                    'cn-hangzhou'
                )

                request = SendSmsRequest()
                request.set_PhoneNumbers(phone)
                request.set_SignName(getattr(settings, 'SMS_SIGN_NAME', '签名'))
                request.set_TemplateCode(getattr(settings, 'SMS_TEMPLATE_CODE', 'SMS_123456789'))
                request.set_TemplateParam('{"code": "' + code + '", "purpose": "' + purpose + '"}')

                response = client.do_action_with_exception(request)
                logger.info(f"短信验证码发送成功: {phone}")

            except Exception as e:
                logger.error(f"发送短信验证码失败: {str(e)}")
                raise

        @api_view(['POST'])
        @permission_classes([permissions.AllowAny])
        def verify_code(request):
            """验证验证码"""
            contact = request.data.get('contact')
            code = request.data.get('code')
            verification_type = request.data.get('verification_type', 'email')
            purpose = request.data.get('purpose', 'registration')

            if not contact or not code:
                return Response({
                    'success': False,
                    'message': '联系方式和验证码不能为空',
                    'error_code': 'MISSING_PARAMS'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                from .models import VerificationCode

                verification_obj = VerificationCode.objects.filter(
                    contact=contact,
                    code=code,
                    verification_type=verification_type,
                    purpose=purpose,
                    is_used=False,
                    expires_at__gt=timezone.now()
                ).latest('created_at')

                if not verification_obj:
                    return Response({
                        'success': False,
                        'message': '验证码无效或已过期',
                        'error_code': 'INVALID_VERIFICATION_CODE'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 标记验证码为已使用
                verification_obj.is_used = True
                verification_obj.used_at = timezone.now()
                verification_obj.save()

                return Response({
                    'success': True,
                    'message': '验证码验证成功',
                    'data': {
                        'verified_at': verification_obj.used_at.isoformat()
                    }
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"验证码验证失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '验证码验证失败',
                    'error_code': 'VERIFICATION_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 分步注册流程
        class StepByStepRegistrationView(APIView):
        """分步注册视图"""

        permission_classes = [permissions.AllowAny]

        def post(self, request):
            """处理分步注册"""
            step = request.data.get('step', 1)

            try:
                if step == 1:
                    return self.handle_step1(request)
                elif step == 2:
                    return self.handle_step2(request)
                elif step == 3:
                    return self.handle_step3(request)
                else:
                    return Response({
                        'success': False,
                        'message': '无效的注册步骤',
                        'error_code': 'INVALID_STEP'
                    }, status=status.HTTP_400_BAD_REQUEST)

            except Exception as e:
                logger.error(f"分步注册失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '注册失败',
                    'error_code': 'REGISTRATION_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        def handle_step1(self, request):
            """处理第一步:基本信息验证"""
            data = request.data

            # 验证基本信息
            self.validate_basic_info(data)

            # 存储到session
            request.session['registration_step1'] = {
                'username': data['username'],
                'email': data['email'],
                'password': data['password'],
                'password_confirm': data['password_confirm'],
            }

            return Response({
                'success': True,
                'message': '基本信息验证成功',
                'data': {
                    'next_step': 2,
                    'next_step_description': '验证邮箱'
                }
            })

        def handle_step2(self, request):
            """处理第二步:邮箱验证"""
            data = request.data

            # 检查第一步数据
            if 'registration_step1' not in request.session:
                return Response({
                    'success': False,
                    'message': '请先完成第一步注册',
                    'error_code': 'MISSING_STEP1'
                }, status=status.HTTP_400_BAD_REQUEST)

            # 发送验证码
            if not data.get('send_code'):
                # 发送验证码
                email = request.session['registration_step1']['email']
                code = generate_verification_code()

                from .models import VerificationCode
                VerificationCode.objects.create(
                    contact=email,
                    code=code,
                    verification_type='email',
                    purpose='registration_step2',
                    expires_at=timezone.now() + timezone.timedelta(minutes=10)
                )

                send_email_verification(email, code, 'registration')

                return Response({
                    'success': True,
                    'message': '验证码已发送',
                    'data': {
                        'expires_in': 600
                    }
                })
            else:
                # 验证验证码
                verification_result = self.verify_registration_code(request, {
                    'contact': request.session['registration_step1']['email'],
                    'verification_code': data['verification_code'],
                    'verification_type': 'email',
                    'purpose': 'registration_step2'
                })

                if not verification_result['success']:
                    return Response(verification_result, status=status.HTTP_400_BAD_REQUEST)

                # 存储到session
                request.session['registration_step2'] = {
                    'email_verified': True,
                    'verification_code': data['verification_code']
                }

                return Response({
                    'success': True,
                    'message': '邮箱验证成功',
                    'data': {
                        'next_step': 3,
                        'next_step_description': '完善个人资料'
                    }
                })

        def handle_step3(self, request):
            """处理第三步:完善资料并创建用户"""
            data = request.data

            # 检查前面步骤的数据
            if 'registration_step1' not in request.session or 'registration_step2' not in request.session:
                return Response({
                    'success': False,
                    'message': '请先完成前面的注册步骤',
                    'error_code': 'MISSING_STEPS'
                }, status=status.HTTP_400_BAD_REQUEST)

            # 合并数据
            user_data = {
                **request.session['registration_step1'],
                **data
            }

            # 创建用户
            user = self.create_user(user_data)

            # 清除session数据
            del request.session['registration_step1']
            del request.session['registration_step2']

            return Response({
                'success': True,
                'message': '注册成功',
                'data': {
                    'user_id': user.id,
                    'username': user.username,
                    'email': user.email
                }
            }, status=status.HTTP_201_CREATED)

        def validate_basic_info(self, data):
            """验证基本信息"""
            # 这里复用之前的验证逻辑
            pass

02.用户资料管理
    a.说明
        个人信息更新
        头像上传处理
        密码修改
    b.示例
        # apps/user_management/profile.py
        from rest_framework import status, permissions
        from rest_framework.decorators import api_view, permission_classes
        from rest_framework.response import Response
        from rest_framework.views import APIView
        from rest_framework.parsers import MultiPartParser, FormParser
        from rest_framework import serializers
        from django.contrib.auth import get_user_model
        from django.core.files.storage import default_storage
        from django.utils import timezone
        from PIL import Image
        import io
        import logging

        User = get_user_model()
        logger = logging.getLogger('profile')

        class UserProfileSerializer(serializers.ModelSerializer):
            """用户资料序列化器"""

            class Meta:
                model = User
                fields = [
                    'username', 'email', 'first_name', 'last_name',
                    'date_joined', 'last_login'
                ]
                read_only_fields = ['username', 'date_joined', 'last_login']

            def validate_email(self, value):
                """验证邮箱"""
                user = self.context['request'].user
                if User.objects.filter(email=value).exclude(pk=user.pk).exists():
                    raise serializers.ValidationError('该邮箱已被使用')
                return value

        class UserProfileDetailSerializer(serializers.ModelSerializer):
            """用户详细资料序列化器"""

            avatar_url = serializers.SerializerMethodField()
            full_name = serializers.SerializerMethodField()
            roles = serializers.SerializerMethodField()
            permissions = serializers.SerializerMethodField()

            class Meta:
                model = User
                fields = [
                    'id', 'username', 'email', 'first_name', 'last_name',
                    'full_name', 'avatar_url', 'is_active', 'is_staff',
                    'date_joined', 'last_login', 'roles', 'permissions'
                ]

            def get_avatar_url(self, obj):
                """获取头像URL"""
                if hasattr(obj, 'profile') and obj.profile.avatar:
                    request = self.context.get('request')
                    if request:
                        return request.build_absolute_uri(obj.profile.avatar.url)
                    return obj.profile.avatar.url
                return None

            def get_full_name(self, obj):
                """获取全名"""
                return obj.get_full_name() or obj.username

            def get_roles(self, obj):
                """获取用户角色"""
                roles = []
                if obj.is_staff:
                    roles.append('staff')
                if obj.is_superuser:
                    roles.append('superuser')

                if hasattr(obj, 'profile'):
                    profile_roles = [role.name for role in obj.profile.roles.all()]
                    roles.extend(profile_roles)

                return roles

            def get_permissions(self, obj):
                """获取用户权限"""
                permissions = []
                for perm in obj.get_all_permissions():
                    app_label, codename = perm.split('.')
                    permissions.append(codename)
                return permissions

        class UserProfileView(APIView):
            """用户资料视图"""

            permission_classes = [permissions.IsAuthenticated]
            parser_classes = [MultiPartParser, FormParser]

            def get(self, request):
                """获取用户资料"""
                try:
                    serializer = UserProfileDetailSerializer(
                        request.user,
                        context={'request': request}
                    )
                    return Response({
                        'success': True,
                        'message': '获取用户资料成功',
                        'data': serializer.data
                    }, status=status.HTTP_200_OK)

                except Exception as e:
                    logger.error(f"获取用户资料失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '获取用户资料失败',
                        'error_code': 'PROFILE_RETRIEVAL_ERROR'
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            def put(self, request):
                """更新用户资料"""
                try:
                    # 更新基础用户信息
                    user = request.user
                    serializer = UserProfileSerializer(
                        user,
                        data=request.data,
                        partial=True,
                        context={'request': request}
                    )

                    if serializer.is_valid():
                        user = serializer.save()

                        # 更新用户资料
                        if hasattr(user, 'profile'):
                            self.update_user_profile(user.profile, request.data)

                        return Response({
                            'success': True,
                            'message': '用户资料更新成功',
                            'data': UserProfileDetailSerializer(
                                user,
                                context={'request': request}
                            ).data
                        }, status=status.HTTP_200_OK)
                    else:
                        return Response({
                            'success': False,
                            'message': '用户资料更新失败',
                            'error_code': 'VALIDATION_ERROR',
                            'errors': serializer.errors
                        }, status=status.HTTP_400_BAD_REQUEST)

                except Exception as e:
                    logger.error(f"更新用户资料失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '用户资料更新失败',
                        'error_code': 'PROFILE_UPDATE_ERROR'
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            def update_user_profile(self, profile, data):
                """更新用户资料"""
                # 更新基本信息
                if 'first_name' in data:
                    profile.first_name = data['first_name']
                if 'last_name' in data:
                    profile.last_name = data['last_name']
                if 'phone' in data:
                    profile.phone = data['phone']
                if 'birth_date' in data:
                    profile.birth_date = data['birth_date']
                if 'gender' in data:
                    profile.gender = data['gender']
                if 'bio' in data:
                    profile.bio = data['bio']

                profile.save()

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def upload_avatar(request):
            """上传用户头像"""
            if 'avatar' not in request.FILES:
                return Response({
                    'success': False,
                    'message': '请选择头像文件',
                    'error_code': 'MISSING_AVATAR'
                }, status=status.HTTP_400_BAD_REQUEST)

            avatar_file = request.FILES['avatar']

            # 验证文件类型
            allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
            if avatar_file.content_type not in allowed_types:
                return Response({
                    'success': False,
                    'message': '只支持 JPEG、PNG、GIF、WebP 格式的图片',
                    'error_code': 'INVALID_FILE_TYPE'
                }, status=status.HTTP_400_BAD_REQUEST)

            # 验证文件大小(5MB限制)
            if avatar_file.size > 5 * 1024 * 1024:
                return Response({
                    'success': False,
                    'message': '头像文件大小不能超过5MB',
                    'error_code': 'FILE_TOO_LARGE'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                user = request.user

                # 获取或创建用户资料
                if not hasattr(user, 'profile'):
                    from .models import UserProfile
                    profile = UserProfile.objects.create(user=user)
                else:
                    profile = user.profile

                # 处理图片
                processed_image = self.process_avatar_image(avatar_file)

                # 保存头像
                if profile.avatar:
                    # 删除旧头像
                    profile.avatar.delete(save=False)

                # 生成唯一文件名
                file_extension = avatar_file.name.split('.')[-1]
                filename = f"avatars/user_{user.id}_{timezone.now().strftime('%Y%m%d_%H%M%S')}.{file_extension}"

                # 保存文件
                path = default_storage.save(filename, processed_image)
                profile.avatar = path
                profile.save()

                # 获取头像URL
                avatar_url = request.build_absolute_uri(profile.avatar.url)

                logger.info(f"用户 {user.username} 头像上传成功: {path}")

                return Response({
                    'success': True,
                    'message': '头像上传成功',
                    'data': {
                        'avatar_url': avatar_url,
                        'file_size': processed_image.size
                    }
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"头像上传失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '头像上传失败',
                    'error_code': 'AVATAR_UPLOAD_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        def process_avatar_image(self, image_file):
            """处理头像图片"""
            # 打开图片
            img = Image.open(image_file)

            # 转换为RGB(处理RGBA图片)
            if img.mode != 'RGB':
                img = img.convert('RGB')

            # 调整尺寸(正方形,最大200x200)
            size = 200
            img.thumbnail((size, size), Image.Resampling.LANCZOS)

            # 创建新的图片文件
            output = io.BytesIO()
            img.save(output, format='JPEG', quality=85, optimize=True)
            output.seek(0)

            # 创建Django文件对象
            from django.core.files.base import ContentFile
            processed_file = ContentFile(output.read(), name=image_file.name)

            return processed_file

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def change_password(request):
            """修改密码"""
            old_password = request.data.get('old_password')
            new_password = request.data.get('new_password')
            new_password_confirm = request.data.get('new_password_confirm')

            if not old_password or not new_password or not new_password_confirm:
                return Response({
                    'success': False,
                    'message': '所有密码字段都不能为空',
                    'error_code': 'MISSING_PASSWORD_FIELDS'
                }, status=status.HTTP_400_BAD_REQUEST)

            if new_password != new_password_confirm:
                return Response({
                    'success': False,
                    'message': '两次输入的新密码不一致',
                    'error_code': 'PASSWORD_MISMATCH'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                user = request.user

                # 验证旧密码
                if not user.check_password(old_password):
                    return Response({
                        'success': False,
                        'message': '旧密码不正确',
                        'error_code': 'INVALID_OLD_PASSWORD'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 验证新密码强度
                self.validate_password_strength(new_password)

                # 检查新密码是否与旧密码相同
                if user.check_password(new_password):
                    return Response({
                        'success': False,
                        'message': '新密码不能与旧密码相同',
                        'error_code': 'SAME_PASSWORD'
                    }, status=status.HTTP_400_BAD_REQUEST)

                # 更新密码
                user.set_password(new_password)
                user.save()

                # 记录密码修改日志
                logger.info(f"用户 {user.username} 修改密码成功")

                # 发送密码修改通知邮件
                self.send_password_change_notification(user)

                return Response({
                    'success': True,
                    'message': '密码修改成功'
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"修改密码失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '密码修改失败',
                    'error_code': 'PASSWORD_CHANGE_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        def validate_password_strength(self, password):
            """验证密码强度"""
            # 长度检查
            if len(password) < 8:
                raise ValueError('密码至少8个字符')

            # 强度检查
            has_lower = any(c.islower() for c in password)
            has_upper = any(c.isupper() for c in password)
            has_digit = any(c.isdigit() for c in password)
            has_special = any(c in '!@#$%^&*(),.?":{}|<>' for c in password)

            if not (has_lower and has_upper and has_digit and has_special):
                raise ValueError('密码必须包含大小写字母、数字和特殊字符')

            # 弱密码检查
            weak_passwords = [
                'password', '123456', '12345678', 'qwerty', 'abc123',
                'password123', 'admin123', 'root123', 'test123'
            ]
            if password.lower() in weak_passwords:
                raise ValueError('不能使用常见弱密码')

        def send_password_change_notification(self, user):
            """发送密码修改通知邮件"""
            try:
                subject = '密码修改通知'
                html_message = render_to_string('user_management/password_change_notification.html', {
                    'user': user,
                    'change_time': timezone.now(),
                    'site_name': getattr(settings, 'SITE_NAME', 'Our Platform')
                })

                send_mail(
                    subject=subject,
                    message='',
                    from_email=getattr(settings, 'DEFAULT_FROM_EMAIL'),
                    recipient_list=[user.email],
                    html_message=html_message,
                    fail_silently=False,
                )

                logger.info(f"密码修改通知邮件发送成功: {user.email}")

            except Exception as e:
                logger.error(f"发送密码修改通知失败: {str(e)}")

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def delete_avatar(request):
            """删除用户头像"""
            try:
                user = request.user

                if hasattr(user, 'profile') and user.profile.avatar:
                    # 删除头像文件
                    user.profile.avatar.delete()
                    user.profile.avatar = None
                    user.profile.save()

                    logger.info(f"用户 {user.username} 头像删除成功")

                return Response({
                    'success': True,
                    'message': '头像删除成功'
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"删除头像失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '头像删除失败',
                    'error_code': 'AVATAR_DELETE_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['GET'])
        @permission_classes([permissions.IsAuthenticated])
        def user_settings(request):
            """获取用户设置"""
            try:
                user = request.user
                settings_data = {
                    'email_notifications': getattr(user.profile, 'email_notifications', True),
                    'sms_notifications': getattr(user.profile, 'sms_notifications', False),
                    'push_notifications': getattr(user.profile, 'push_notifications', True),
                    'newsletter': getattr(user.profile, 'newsletter', False),
                    'language': getattr(user.profile, 'language', 'zh-CN'),
                    'timezone': getattr(user.profile, 'timezone', 'Asia/Shanghai'),
                    'theme': getattr(user.profile, 'theme', 'light'),
                }

                return Response({
                    'success': True,
                    'message': '获取用户设置成功',
                    'data': settings_data
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"获取用户设置失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '获取用户设置失败',
                    'error_code': 'SETTINGS_RETRIEVAL_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def update_settings(request):
            """更新用户设置"""
            try:
                user = request.user

                if not hasattr(user, 'profile'):
                    from .models import UserProfile
                    profile = UserProfile.objects.create(user=user)
                else:
                    profile = user.profile

                # 更新设置
                settings_data = request.data
                for key, value in settings_data.items():
                    if hasattr(profile, key):
                        setattr(profile, key, value)

                profile.save()

                logger.info(f"用户 {user.username} 设置更新成功")

                return Response({
                    'success': True,
                    'message': '用户设置更新成功'
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"更新用户设置失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '用户设置更新失败',
                    'error_code': 'SETTINGS_UPDATE_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 批量操作
        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def batch_update_profile(request):
            """批量更新用户资料"""
            updates = request.data.get('updates', [])

            if not updates:
                return Response({
                    'success': False,
                    'message': '更新数据不能为空',
                    'error_code': 'MISSING_UPDATES'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                user = request.user
                updated_fields = []

                for update in updates:
                    field_name = update.get('field')
                    field_value = update.get('value')

                    if not field_name or field_value is None:
                        continue

                    # 验证字段名
                    allowed_fields = ['first_name', 'last_name', 'phone', 'bio', 'gender']
                    if field_name not in allowed_fields:
                        continue

                    # 更新用户模型字段
                    if hasattr(user, field_name):
                        setattr(user, field_name, field_value)
                        updated_fields.append(field_name)
                    else:
                        # 更新用户资料字段
                        if hasattr(user, 'profile'):
                            if hasattr(user.profile, field_name):
                                setattr(user.profile, field_name, field_value)
                                updated_fields.append(field_name)

                # 保存更改
                user.save()
                if hasattr(user, 'profile'):
                    user.profile.save()

                logger.info(f"用户 {user.username} 批量更新资料成功: {updated_fields}")

                return Response({
                    'success': True,
                    'message': '批量更新成功',
                    'data': {
                        'updated_fields': updated_fields
                    }
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"批量更新用户资料失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '批量更新失败',
                    'error_code': 'BATCH_UPDATE_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

5.4 安全机制

01.CORS配置
    a.说明
        跨域请求处理
        安全头设置
        预检请求支持
    b.示例
        # apps/security/cors.py
        from corsheaders.middleware import CorsMiddleware
        from corsheaders.signals import check_request_enabled
        from django.dispatch import receiver
        from django.utils import timezone
        import logging

        logger = logging.getLogger('cors')

        class CustomCorsMiddleware(CorsMiddleware):
            """自定义CORS中间件"""

            def process_response(self, request, response):
                """处理响应,添加CORS头"""
                # 调用父类方法
                response = super().process_response(request, response)

                # 添加自定义CORS头
                if getattr(settings, 'CUSTOM_CORS_ENABLED', False):
                    self.add_custom_cors_headers(request, response)

                return response

            def add_custom_cors_headers(self, request, response):
                """添加自定义CORS头"""
                # 根据请求路径设置不同的CORS策略
                path = request.path

                if path.startswith('/api/public/'):
                    # 公开API:允许所有来源
                    response['Access-Control-Allow-Origin'] = '*'
                    response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
                    response['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-Requested-With'
                    response['Access-Control-Max-Age'] = '86400'  # 24小时

                elif path.startswith('/api/auth/'):
                    # 认证API:允许特定来源
                    allowed_origins = getattr(settings, 'CORS_ALLOWED_ORIGINS', [])
                    origin = request.META.get('HTTP_ORIGIN')

                    if origin in allowed_origins:
                        response['Access-Control-Allow-Origin'] = origin
                        response['Access-Control-Allow-Credentials'] = 'true'
                        response['Access-Control-Allow-Methods'] = 'POST, OPTIONS'
                        response['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-CSRFToken'
                        response['Access-Control-Max-Age'] = '3600'  # 1小时

                elif path.startswith('/api/v1/') or path.startswith('/api/v2/'):
                    # API v1/v2:严格的CORS策略
                    allowed_origins = getattr(settings, 'CORS_ALLOWED_ORIGINS', [])
                    origin = request.META.get('HTTP_ORIGIN')

                    if origin in allowed_origins:
                        response['Access-Control-Allow-Origin'] = origin
                        response['Access-Control-Allow-Credentials'] = 'true'
                        response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, PATCH, DELETE, OPTIONS'
                        response['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-Requested-With, X-API-Version'
                        response['Access-Control-Expose-Headers'] = 'X-Total-Count, X-Page-Count'
                        response['Access-Control-Max-Age'] = '7200'  # 2小时

                return response

        # CORS信号处理
        @receiver(check_request_enabled)
        def cors_allow_origin_to_all(sender, request, **kwargs):
            """动态CORS允许逻辑"""
            if request.path.startswith('/api/webhook/'):
                # Webhook端点需要动态CORS验证
                origin = request.META.get('HTTP_ORIGIN')
                if origin and self.is_valid_webhook_origin(origin, request):
                    return True
                return False
            return None

        def is_valid_webhook_origin(origin, request):
            """验证Webhook来源"""
            try:
                from .models import WebhookConfig
                return WebhookConfig.objects.filter(
                    origin_url=origin,
                    is_active=True
                ).exists()
            except Exception as e:
                logger.error(f"验证Webhook来源失败: {str(e)}")
                return False

        # CORS配置
        CORS_ALLOW_ALL_ORIGINS = False  # 生产环境中应设为False
        CORS_ALLOWED_ORIGINS = [
            "https://yourdomain.com",
            "https://admin.yourdomain.com",
            "https://app.yourdomain.com",
        ]
        CORS_ALLOW_CREDENTIALS = True
        CORS_ALLOWED_METHODS = [
            "GET",
            "POST",
            "PUT",
            "PATCH",
            "DELETE",
            "OPTIONS",
        ]
        CORS_ALLOWED_HEADERS = [
            "accept",
            "accept-encoding",
            "authorization",
            "content-type",
            "dnt",
            "origin",
            "user-agent",
            "x-csrftoken",
            "x-requested-with",
        ]
        CORS_EXPOSE_HEADERS = [
            "X-Total-Count",
            "X-Page-Count",
            "X-Current-Page",
            "X-Next-Page",
            "X-Previous-Page",
        ]
        CORS_PREFLIGHT_MAX_AGE = 86400  # 24小时

        # 开发环境配置
        if DEBUG:
            CORS_ALLOW_ALL_ORIGINS = True
            CORS_ALLOW_CREDENTIALS = True

02.HTTPS强制
    a.说明
        SSL重定向配置
        安全传输协议
        HSTS头设置
    b.示例
        # apps/security/https.py
        from django.middleware.security import SecurityMiddleware
        from django.http import HttpResponseRedirect
        from django.conf import settings
        from django.utils.deprecation import MiddlewareMixin
        import logging

        logger = logging.getLogger('https')

        class HTTPSRedirectMiddleware(MiddlewareMixin):
            """HTTPS重定向中间件"""

            def process_request(self, request):
                """处理请求,重定向到HTTPS"""
                # 只在生产环境中启用
                if not getattr(settings, 'FORCE_HTTPS', False):
                    return None

                # 检查是否已经是HTTPS
                if request.is_secure():
                    return None

                # 排除开发环境和本地请求
                if getattr(settings, 'DEBUG', False) or '127.0.0.1' in request.get_host():
                    return None

                # 排除健康检查等内部API
                if request.path in ['/health/', '/ping/', '/metrics/']:
                    return None

                # 重定向到HTTPS
                url = request.build_absolute_uri(request.get_full_path())
                https_url = url.replace('http://', 'https://', 1)

                logger.info(f"HTTPS重定向: {url} -> {https_url}")

                return HttpResponseRedirect(https_url)

        class StrictTransportSecurityMiddleware(MiddlewareMixin):
            """严格传输安全中间件"""

            def process_response(self, request, response):
                """添加HSTS头"""
                # 只在HTTPS响应中添加HSTS头
                if not request.is_secure():
                    return response

                # 获取HSTS配置
                max_age = getattr(settings, 'HSTS_MAX_AGE', 31536000)  # 1年
                include_subdomains = getattr(settings, 'HSTS_INCLUDE_SUBDOMAINS', True)
                preload = getattr(settings, 'HSTS_PRELOAD', True)

                # 构建HSTS头
                hsts_parts = [f"max-age={max_age}"]
                if include_subdomains:
                    hsts_parts.append("includeSubDomains")
                if preload:
                    hsts_parts.append("preload")

                hsts_value = "; ".join(hsts_parts)
                response['Strict-Transport-Security'] = hsts_value

                # 其他安全头
                response['X-Content-Type-Options'] = 'nosniff'
                response['X-Frame-Options'] = 'DENY'
                response['X-XSS-Protection'] = '1; mode=block'
                response['Referrer-Policy'] = 'strict-origin-when-cross-origin'

                # CSP头
                csp_policy = getattr(settings, 'CSP_POLICY', None)
                if csp_policy:
                    response['Content-Security-Policy'] = csp_policy

                return response

        # 安全头中间件
        class SecurityHeadersMiddleware(MiddlewareMixin):
            """安全头中间件"""

            def process_response(self, request, response):
                """添加安全头"""
                # 基础安全头
                response['X-Content-Type-Options'] = 'nosniff'
                response['X-Frame-Options'] = 'DENY'
                response['X-XSS-Protection'] = '1; mode=block'
                response['Referrer-Policy'] = 'strict-origin-when-cross-origin'

                # API特有头
                if request.path.startswith('/api/'):
                    response['X-API-Version'] = getattr(settings, 'API_VERSION', 'v1')
                    response['X-Rate-Limit-Remaining'] = str(get_rate_limit_remaining(request))
                    response['X-Rate-Limit-Reset'] = str(get_rate_limit_reset())

                # 移除敏感信息头
                response.pop('Server', None)
                response.pop('X-Powered-By', None)

                return response

        def get_rate_limit_remaining(request):
            """获取剩余请求限制"""
            try:
                from django.core.cache import cache
                user_id = request.user.id if request.user.is_authenticated else 'anonymous'
                cache_key = f"rate_limit_{user_id}_{request.path}"
                limit_data = cache.get(cache_key, {'limit': 100, 'used': 0})
                return max(0, limit_data['limit'] - limit_data['used'])
            except:
                return 0

        def get_rate_limit_reset():
            """获取限制重置时间"""
            from datetime import datetime, timedelta
            reset_time = datetime.now().replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
            return int(reset_time.timestamp())

        # SSL配置检查
        class SSLCheckMiddleware(MiddlewareMixin):
            """SSL检查中间件"""

            def process_request(self, request):
                """检查SSL配置"""
                if getattr(settings, 'DEBUG', False):
                    return None  # 开发环境跳过检查

                # 检查是否使用HTTPS
                if not request.is_secure():
                    logger.error(f"未使用HTTPS: {request.get_full_path()}")
                    # 可以选择返回错误响应或记录日志

                # 检查SSL证书
                if hasattr(request, 'ssl_info'):
                    ssl_info = request.ssl_info
                    if ssl_info.get('protocol') != 'TLS':
                        logger.error(f"SSL协议不安全: {ssl_info.get('protocol')}")

                    # 检查证书有效期
                    cert_info = ssl_info.get('cert')
                    if cert_info:
                        import ssl
                        cert = ssl.DER_cert_to_PEM_cert(cert_info['der'])
                        x509 = ssl.load_certificate(ssl.FILETYPE_PEM, cert)
                        expiry_date = x509.get_notAfter()

                        days_remaining = (expiry_date - timezone.now().date()).days
                        if days_remaining < 30:
                            logger.warning(f"SSL证书即将过期: {expiry_date} (剩余{days_remaining}天)")

                return None

        # 中间件配置
        MIDDLEWARE = [
            'django.middleware.security.SecurityMiddleware',
            'apps.security.https.StrictTransportSecurityMiddleware',
            'apps.security.https.HTTPSRedirectMiddleware',
            'apps.security.cors.CustomCorsMiddleware',
            'apps.security.security.SecurityHeadersMiddleware',
            # ... 其他中间件
        ]

        # 安全配置
        SECURE_SSL_REDIRECT = True
        SECURE_SSL_HOST = None
        SECURE_SSL_PROXY = True
        SECURE_BROWSER_XSS_FILTER = True
        SECURE_CONTENT_TYPE_NOSNIFF = True
        SECURE_HSTS_SECONDS = 31536000
        SECURE_HSTS_INCLUDE_SUBDOMAINS = True
        SECURE_HSTS_PRELOAD = True
        SESSION_COOKIE_SECURE = True
        CSRF_COOKIE_SECURE = True
        X_FRAME_OPTIONS = 'DENY'

        # CSP配置
        CSP_DEFAULT_SRC = "'self'"
        CSP_SCRIPT_SRC = "'self' 'unsafe-inline' https://cdn.jsdelivr.net"
        CSP_STYLE_SRC = "'self' 'unsafe-inline' https://fonts.googleapis.com"
        CSP_FONT_SRC = "'self' https://fonts.gstatic.com"
        CSP_IMG_SRC = "'self' data: https:"
        CSP_CONNECT_SRC = "'self' https://api.example.com"

03.API密钥管理
    a.说明
        密钥生成算法
        访问权限控制
        使用量限制
    b.示例
        # apps/security/api_keys.py
        from rest_framework import status, permissions
        from rest_framework.decorators import api_view, permission_classes
        from rest_framework.response import Response
        from rest_framework.views import APIView
        from django.contrib.auth import get_user_model
        from django.utils import timezone
        from django.db.models import Q
        import secrets
        import hashlib
        import logging

        User = get_user_model()
        logger = logging.getLogger('api_keys')

        class APIKeyManager:
            """API密钥管理器"""

            @staticmethod
            def generate_api_key():
                """生成API密钥"""
                # 生成32字节的随机字符串
                return secrets.token_urlsafe(32)

            @staticmethod
            def generate_api_key_hash(api_key):
                """生成API密钥哈希"""
                return hashlib.sha256(api_key.encode()).hexdigest()

            @staticmethod
            def create_api_key(user, name=None, permissions=None, expires_at=None):
                """创建API密钥"""
                api_key = APIKeyManager.generate_api_key()
                key_hash = APIKeyManager.generate_api_key_hash(api_key)

                from .models import APIKey
                return APIKey.objects.create(
                    user=user,
                    name=name or f"API Key {timezone.now().strftime('%Y%m%d_%H%M%S')}",
                    key_hash=key_hash,
                    permissions=permissions or [],
                    expires_at=expires_at,
                    is_active=True
                )

            @staticmethod
            def validate_api_key(api_key):
                """验证API密钥"""
                if not api_key:
                    return None, False, "API密钥不能为空"

                key_hash = APIKeyManager.generate_api_key_hash(api_key)

                try:
                    from .models import APIKey
                    key_obj = APIKey.objects.select_related('user').get(
                        key_hash=key_hash,
                        is_active=True
                    )

                    # 检查是否过期
                    if key_obj.expires_at and key_obj.expires_at < timezone.now():
                        return None, False, "API密钥已过期"

                    # 检查使用次数限制
                    if key_obj.max_uses and key_obj.usage_count >= key_obj.max_uses:
                        return None, False, "API密钥使用次数已达上限"

                    return key_obj, True, "验证成功"

                except Exception as e:
                    logger.error(f"API密钥验证失败: {str(e)}")
                    return None, False, "API密钥验证失败"

        class APIKeyView(APIView):
            """API密钥管理视图"""

            permission_classes = [permissions.IsAuthenticated]

            def get(self, request):
                """获取用户的API密钥列表"""
                try:
                    from .models import APIKey

                    api_keys = APIKey.objects.filter(
                        user=request.user,
                        is_active=True
                    ).order_by('-created_at')

                    serializer = APIKeySerializer(api_keys, many=True, context={'request': request})

                    return Response({
                        'success': True,
                        'message': '获取API密钥列表成功',
                        'data': serializer.data
                    }, status=status.HTTP_200_OK)

                except Exception as e:
                    logger.error(f"获取API密钥列表失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '获取API密钥列表失败',
                        'error_code': 'API_KEYS_RETRIEVAL_ERROR'
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            def post(self, request):
                """创建新的API密钥"""
                try:
                    data = request.data
                    name = data.get('name')
                    permissions = data.get('permissions', [])
                    max_uses = data.get('max_uses')
                    expires_days = data.get('expires_days')

                    # 检查用户密钥数量限制
                    from .models import APIKey
                    user_key_count = APIKey.objects.filter(user=request.user, is_active=True).count()
                    max_keys_per_user = getattr(settings, 'MAX_API_KEYS_PER_USER', 5)

                    if user_key_count >= max_keys_per_user:
                        return Response({
                            'success': False,
                            'message': f'API密钥数量已达上限(最多{max_keys_per_user}个)',
                            'error_code': 'MAX_KEYS_EXCEEDED'
                        }, status=status.HTTP_400_BAD_REQUEST)

                    # 设置过期时间
                    expires_at = None
                    if expires_days:
                        expires_at = timezone.now() + timezone.timedelta(days=expires_days)

                    # 创建API密钥
                    api_key_obj = APIKeyManager.create_api_key(
                        user=request.user,
                        name=name,
                        permissions=permissions,
                        expires_at=expires_at
                    )

                    # 设置最大使用次数
                    if max_uses:
                        api_key_obj.max_uses = max_uses
                        api_key_obj.save()

                    # 返回密钥(只在创建时返回一次)
                    api_key = APIKeyManager.generate_api_key()
                    api_key_hash = APIKeyManager.generate_api_key_hash(api_key)

                    # 临时存储明文密钥用于返回
                    api_key_obj.temp_key = api_key
                    serializer = APIKeySerializer(api_key_obj, context={'request': request, 'show_key': True})

                    logger.info(f"用户 {request.user.username} 创建API密钥: {name}")

                    return Response({
                        'success': True,
                        'message': 'API密钥创建成功',
                        'data': serializer.data
                    }, status=status.HTTP_201_CREATED)

                except Exception as e:
                    logger.error(f"创建API密钥失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': 'API密钥创建失败',
                        'error_code': 'API_KEY_CREATE_ERROR'
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['GET'])
        @permission_classes([permissions.AllowAny])
        def validate_api_key(request):
            """验证API密钥"""
            api_key = request.GET.get('api_key') or request.META.get('HTTP_X_API_KEY')

            if not api_key:
                return Response({
                    'success': False,
                    'message': 'API密钥不能为空',
                    'error_code': 'MISSING_API_KEY'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                key_obj, is_valid, message = APIKeyManager.validate_api_key(api_key)

                if not is_valid:
                    return Response({
                        'success': False,
                        'message': message,
                        'error_code': 'INVALID_API_KEY'
                    }, status=status.HTTP_401_UNAUTHORIZED)

                # 增加使用次数
                if key_obj.max_uses:
                    key_obj.usage_count += 1
                    key_obj.save(update_fields=['usage_count'])

                # 更新最后使用时间
                key_obj.last_used_at = timezone.now()
                key_obj.save(update_fields=['last_used_at'])

                return Response({
                    'success': True,
                    'message': 'API密钥验证成功',
                    'data': {
                        'key_id': key_obj.id,
                        'key_name': key_obj.name,
                        'permissions': key_obj.permissions,
                        'usage_count': key_obj.usage_count,
                        'max_uses': key_obj.max_uses,
                        'expires_at': key_obj.expires_at.isoformat() if key_obj.expires_at else None,
                        'last_used_at': key_obj.last_used_at.isoformat(),
                    }
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"验证API密钥失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': 'API密钥验证失败',
                    'error_code': 'API_KEY_VALIDATION_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        class APIKeyDetailView(APIView):
            """API密钥详情视图"""

            permission_classes = [permissions.IsAuthenticated]

            def get_object(self, key_id):
                """获取API密钥对象"""
                from .models import APIKey
                try:
                    return APIKey.objects.get(
                        id=key_id,
                        user=self.request.user
                    )
                except APIKey.DoesNotExist:
                    return None

            def get(self, request, key_id):
                """获取API密钥详情"""
                try:
                    api_key = self.get_object(key_id)
                    if not api_key:
                        return Response({
                            'success': False,
                            'message': 'API密钥不存在',
                            'error_code': 'API_KEY_NOT_FOUND'
                        }, status=status.HTTP_404_NOT_FOUND)

                    serializer = APIKeyDetailSerializer(api_key, context={'request': request})
                    return Response({
                        'success': True,
                        'message': '获取API密钥详情成功',
                        'data': serializer.data
                    }, status=status.HTTP_200_OK)

                except Exception as e:
                    logger.error(f"获取API密钥详情失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': '获取API密钥详情失败',
                        'error_code': 'API_KEY_DETAIL_ERROR'
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            def put(self, request, key_id):
                """更新API密钥"""
                try:
                    api_key = self.get_object(key_id)
                    if not api_key:
                        return Response({
                            'success': False,
                            'message': 'API密钥不存在',
                            'error_code': 'API_KEY_NOT_FOUND'
                        }, status=status.HTTP_404_NOT_FOUND)

                    data = request.data
                    serializer = APIKeyUpdateSerializer(
                        api_key,
                        data=data,
                        partial=True,
                        context={'request': request}
                    )

                    if serializer.is_valid():
                        api_key = serializer.save()
                        return Response({
                            'success': True,
                            'message': 'API密钥更新成功',
                            'data': APIKeySerializer(api_key, context={'request': request}).data
                        }, status=status.HTTP_200_OK)
                    else:
                        return Response({
                            'success': False,
                            'message': 'API密钥更新失败',
                            'error_code': 'VALIDATION_ERROR',
                            'errors': serializer.errors
                        }, status=status.HTTP_400_BAD_REQUEST)

                except Exception as e:
                    logger.error(f"更新API密钥失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': 'API密钥更新失败',
                        'error_code': 'API_KEY_UPDATE_ERROR'
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            def delete(self, request, key_id):
                """删除API密钥"""
                try:
                    api_key = self.get_object(key_id)
                    if not api_key:
                        return Response({
                            'success': False,
                            'message': 'API密钥不存在',
                            'error_code': 'API_KEY_NOT_FOUND'
                        }, status=status.HTTP_404_NOT_FOUND)

                    # 软删除(标记为非活跃)
                    api_key.is_active = False
                    api_key.deactivated_at = timezone.now()
                    api_key.save(update_fields=['is_active', 'deactivated_at'])

                    logger.info(f"用户 {request.user.username} 删除API密钥: {api_key.name}")

                    return Response({
                        'success': True,
                        'message': 'API密钥删除成功'
                    }, status=status.HTTP_200_OK)

                except Exception as e:
                    logger.error(f"删除API密钥失败: {str(e)}", exc_info=True)
                    return Response({
                        'success': False,
                        'message': 'API密钥删除失败',
                        'error_code': 'API_KEY_DELETE_ERROR'
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['POST'])
        @permission_classes([permissions.IsAuthenticated])
        def regenerate_api_key(request):
            """重新生成API密钥"""
            key_id = request.data.get('key_id')

            if not key_id:
                return Response({
                    'success': False,
                    'message': '密钥ID不能为空',
                    'error_code': 'MISSING_KEY_ID'
                }, status=status.HTTP_400_BAD_REQUEST)

            try:
                from .models import APIKey
                api_key = APIKey.objects.get(
                    id=key_id,
                    user=request.user
                )

                # 生成新的API密钥
                new_key = APIKeyManager.generate_api_key()
                new_key_hash = APIKeyManager.generate_api_key(new_key)

                # 更新密钥
                api_key.key_hash = new_key_hash
                api_key.last_regenerated_at = timezone.now()
                api_key.save(update_fields=['key_hash', 'last_regenerated_at'])

                # 临时存储明文密钥用于返回
                api_key.temp_key = new_key
                serializer = APIKeySerializer(api_key, context={'request': request, 'show_key': True})

                logger.info(f"用户 {request.user.username} 重新生成API密钥: {api_key.name}")

                return Response({
                    'success': True,
                    'message': 'API密钥重新生成成功',
                    'data': serializer.data
                }, status=status.HTTP_200_OK)

            except APIKey.DoesNotExist:
                return Response({
                    'success': False,
                    'message': 'API密钥不存在',
                    'error_code': 'API_KEY_NOT_FOUND'
                }, status=status.HTTP_404_NOT_FOUND)

            except Exception as e:
                logger.error(f"重新生成API密钥失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': 'API密钥重新生成失败',
                    'error_code': 'API_KEY_REGENERATE_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        @api_view(['GET'])
        @permission_classes([permissions.IsAuthenticated])
        def api_key_stats(request):
            """获取API密钥统计信息"""
            try:
                from .models import APIKey
                from django.db.models import Count, Sum
                from django.utils import timezone
                from datetime import timedelta

                # 基础统计
                user_api_keys = APIKey.objects.filter(user=request.user)

                # 总数、活跃数、过期数
                stats = {
                    'total_keys': user_api_keys.count(),
                    'active_keys': user_api_keys.filter(is_active=True).count(),
                    'inactive_keys': user_api_keys.filter(is_active=False).count(),
                    'expired_keys': user_api_keys.filter(
                        expires_at__lt=timezone.now()
                    ).count(),
                }

                # 最近30天的使用统计
                thirty_days_ago = timezone.now() - timedelta(days=30)
                usage_stats = user_api_keys.aggregate(
                    total_usage=Sum('usage_count'),
                    recent_usage=Count('usage_count', filter=Q(
                        last_used_at__gte=thirty_days_ago
                    ))
                )

                stats.update({
                    'total_usage': usage_stats['total_usage'] or 0,
                    'recent_usage': usage_stats['recent_usage'] or 0,
                })

                # 按密钥统计
                key_stats = user_api_keys.filter(is_active=True).aggregate(
                    keys_with_limit=Count('id', filter=Q(max_uses__gt=0)),
                    unlimited_keys=Count('id', filter=Q(max_uses__isnull=True))
                )

                stats.update({
                    'keys_with_limit': key_stats['keys_with_limit'] or 0,
                    'unlimited_keys': key_stats['unlimited_keys'] or 0,
                })

                return Response({
                    'success': True,
                    'message': '获取API密钥统计成功',
                    'data': stats
                }, status=status.HTTP_200_OK)

            except Exception as e:
                logger.error(f"获取API密钥统计失败: {str(e)}", exc_info=True)
                return Response({
                    'success': False,
                    'message': '获取API密钥统计失败',
                    'error_code': 'API_KEY_STATS_ERROR'
                }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 序列化器
        # apps/security/serializers.py
        from rest_framework import serializers
        from .models import APIKey

        class APIKeySerializer(serializers.ModelSerializer):
            """API密钥序列化器"""

            class Meta:
                model = APIKey
                fields = [
                    'id', 'name', 'permissions', 'is_active',
                    'max_uses', 'usage_count',
                    'created_at', 'last_used_at',
                    'expires_at'
                ]
                read_only_fields = [
                    'id', 'key_hash', 'usage_count',
                    'created_at', 'last_used_at', 'expires_at'
                ]

            def to_representation(self, instance):
                """自定义序列化输出"""
                data = super().to_representation(instance)

                # 只在创建时返回明文密钥
                if hasattr(self, 'context') and self.context.get('show_key') and hasattr(instance, 'temp_key'):
                    data['api_key'] = instance.temp_key
                    del instance.temp_key  # 确保密钥安全

                # 格式化时间
                if instance.expires_at:
                    data['expires_in_days'] = (instance.expires_at - timezone.now()).days
                    if data['expires_in_days'] < 0:
                        data['expires_in_days'] = 0

                return data

        class APIKeyDetailSerializer(APIKeySerializer):
            """API密钥详情序列化器"""

            class Meta(APIKeySerializer.Meta):
                fields = APIKeySerializer.Meta.fields + [
                    'last_regenerated_at', 'deactivated_at',
                    'request_count'
                ]

        class APIKeyUpdateSerializer(serializers.ModelSerializer):
            """API密钥更新序列化器"""

            class Meta:
                model = APIKey
                fields = ['name', 'permissions', 'max_uses', 'expires_at', 'is_active']

            def validate_permissions(self, value):
                """验证权限"""
                if not isinstance(value, list):
                    raise serializers.ValidationError("权限必须是列表格式")

                available_permissions = [
                    'read', 'write', 'delete', 'admin', 'api_manage'
                ]
                invalid_perms = set(value) - set(available_permissions)
                if invalid_perms:
                    raise serializers.ValidationError(f"无效的权限: {', '.join(invalid_perms)}")

                return value

        # API密钥权限检查装饰器
        def require_api_key_permission(required_permissions=None):
            """API密钥权限检查装饰器"""
            def decorator(view_func):
                def wrapper(request, *args, **kwargs):
                    # 检查是否有API密钥
                    api_key = request.GET.get('api_key') or request.META.get('HTTP_X_API_KEY')
                    if not api_key:
                        return Response({
                            'success': False,
                            'message': '需要API密钥',
                            'error_code': 'API_KEY_REQUIRED'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    # 验证API密钥
                    key_obj, is_valid, message = APIKeyManager.validate_api_key(api_key)
                    if not is_valid:
                        return Response({
                            'success': False,
                            'message': message,
                            'error_code': 'INVALID_API_KEY'
                        }, status=status.HTTP_401_UNAUTHORIZED)

                    # 检查权限
                    if required_permissions:
                        key_permissions = set(key_obj.permissions)
                        required_perms_set = set(required_permissions)

                        if not required_perms_set.issubset(key_permissions):
                            return Response({
                                'success': False,
                                'message': f'API密钥缺少所需权限: {", ".join(required_perms_set - key_permissions)}',
                                'error_code': 'INSUFFICIENT_PERMISSIONS'
                            }, status=status.HTTP_403_FORBIDDEN)

                    # 将API密钥信息添加到请求中
                    request.api_key = key_obj

                    return view_func(request, *args, **kwargs)
                return wrapper
            return decorator

        # 模型
        # apps/security/models.py
        from django.db import models
        from django.contrib.auth import get_user_model

        User = get_user_model()

        class APIKey(models.Model):
            """API密钥模型"""

            user = models.ForeignKey(
                User,
                on_delete=models.CASCADE,
                related_name='api_keys',
                verbose_name='用户'
            )
            name = models.CharField('密钥名称', max_length=100)
            key_hash = models.CharField('密钥哈希', max_length=64, unique=True)
            permissions = models.JSONField('权限', default=list)
            is_active = models.BooleanField('是否激活', default=True)
            max_uses = models.PositiveIntegerField('最大使用次数', null=True, blank=True)
            usage_count = models.PositiveIntegerField('使用次数', default=0)
            created_at = models.DateTimeField('创建时间', auto_now_add=True)
            last_used_at = models.DateTimeField('最后使用时间', null=True, blank=True)
            expires_at = models.DateTimeField('过期时间', null=True, blank=True)
            last_regenerated_at = models.DateTimeField('最后重新生成时间', null=True, blank=True)
            deactivated_at = models.DateTimeField('停用时间', null=True, blank=True)

            class Meta:
                verbose_name = 'API密钥'
                verbose_name_plural = 'API密钥'
                ordering = ['-created_at']
                indexes = [
                    models.Index(fields=['user', 'is_active']),
                    models.Index(fields=['key_hash']),
                    models.Index(fields=['expires_at']),
                ]

            def __str__(self):
                return f"{self.user.username} - {self.name}"

            @property
            def is_expired(self):
                """检查密钥是否过期"""
                return self.expires_at and self.expires_at < timezone.now()

            @property
            def is_usage_limit_reached(self):
                """检查是否达到使用次数限制"""
                return self.max_uses and self.usage_count >= self.max_uses

            def increment_usage(self):
                """增加使用次数"""
                self.usage_count += 1
                self.last_used_at = timezone.now()
                self.save(update_fields=['usage_count', 'last_used_at'])

        class APIKeyUsageLog(models.Model):
            """API密钥使用日志"""

            api_key = models.ForeignKey(APIKey, on_delete=models.CASCADE, related_name='usage_logs')
            user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='api_key_logs')
            request_method = models.CharField('请求方法', max_length=10)
            request_path = models.CharField('请求路径', max_length=255)
            request_ip = models.GenericIPAddressField('请求IP')
            response_status = models.IntegerField('响应状态码')
            response_time = models.IntegerField('响应时间(ms)', null=True, blank=True)
            user_agent = models.TextField('用户代理', blank=True)
            created_at = models.DateTimeField('创建时间', auto_now_add=True)

            class Meta:
                verbose_name = 'API密钥使用日志'
                verbose_name_plural = 'API密钥使用日志'
                ordering = ['-created_at']
                indexes = [
                    models.Index(fields=['api_key', 'created_at']),
                    models.Index(fields=['user', 'created_at']),
                    models.Index(fields=['created_at']),
                ]

            def __str__(self):
                return f"{self.user.username} - {self.request_method} {self.request_path}"

04.审计日志
    a.说明
        操作记录跟踪
        安全事件监控
        日志分析报告
    b.示例
        # apps/security/audit.py
        from django.db import models
        from django.contrib.auth import get_user_model
        from django.utils import timezone
        from django.contrib.admin.models import LogEntry
        import json
        import logging

        User = get_user_model()
        logger = logging.getLogger('audit')

        class AuditLog(models.Model):
            """审计日志模型"""

            ACTION_CHOICES = [
                ('CREATE', '创建'),
                ('UPDATE', '更新'),
                ('DELETE', '删除'),
                ('LOGIN', '登录'),
                ('LOGOUT', '登出'),
                ('ACCESS', '访问'),
                ('PERMISSION_DENIED', '权限拒绝'),
                ('SECURITY_VIOLATION', '安全违规'),
                ('SYSTEM_ERROR', '系统错误'),
            ]

            RESULT_CHOICES = [
                ('SUCCESS', '成功'),
                ('FAILURE', '失败'),
                ('ERROR', '错误'),
            ]

            user = models.ForeignKey(
                User,
                on_delete=models.SET_NULL,
                null=True,
                blank=True,
                related_name='audit_logs',
                verbose_name='用户'
            )
            action = models.CharField('操作类型', max_length=20, choices=ACTION_CHOICES)
            resource_type = models.CharField('资源类型', max_length=50)
            resource_id = models.CharField('资源ID', max_length=50, blank=True)
            result = models.CharField('结果', max_length=20, choices=RESULT_CHOICES)
            message = models.TextField('消息')
            details = models.JSONField('详情', default=dict, blank=True)
            ip_address = models.GenericIPAddressField('IP地址')
            user_agent = models.TextField('用户代理', blank=True)
            request_id = models.CharField('请求ID', max_length=100, blank=True)
            session_id = models.CharField('会话ID', max_length=100, blank=True)
            timestamp = models.DateTimeField('时间戳', auto_now_add=True)

            class Meta:
                verbose_name = '审计日志'
                verbose_name_plural = '审计日志'
                ordering = ['-timestamp']
                indexes = [
                    models.Index(fields=['user', 'timestamp']),
                    models.Index(fields=['action', 'timestamp']),
                    models.Index(fields=['resource_type', 'timestamp']),
                    models.Index(fields=['result', 'timestamp']),
                    models.Index(fields=['timestamp']),
                ]

            def __str__(self):
                return f"{self.user} - {self.action} - {self.resource_type} - {self.result}"

        class SecurityEvent(models.Model):
            """安全事件模型"""

            EVENT_TYPES = [
                ('BRUTE_FORCE_ATTACK', '暴力破解攻击'),
                ('SQL_INJECTION', 'SQL注入攻击'),
                ('XSS_ATTACK', '跨站脚本攻击'),
                ('CSRF_ATTACK', '跨站请求伪造'),
                ('AUTHENTICATION_BYPASS', '认证绕过'),
                ('PRIVILEGE_ESCALATION', '权限提升'),
                ('DATA_BREACH', '数据泄露'),
                ('MALICIOUS_REQUEST', '恶意请求'),
                ('RATE_LIMIT_EXCEEDED', '请求频率超限'),
            ]

            SEVERITY_LEVELS = [
                ('LOW', '低'),
                ('MEDIUM', '中'),
                ('HIGH', '高'),
                ('CRITICAL', '严重'),
            ]

            user = models.ForeignKey(
                User,
                on_delete=models.SET_NULL,
                null=True,
                blank=True,
                related_name='security_events',
                verbose_name='用户'
            )
            event_type = models.CharField('事件类型', max_length=50, choices=EVENT_TYPES)
            severity = models.CharField('严重程度', max_length=20, choices=SEVERITY_LEVELS)
            description = models.TextField('事件描述')
            details = models.JSONField('详情', default=dict)
            ip_address = models.GenericIPAddressField('IP地址')
            user_agent = models.TextField('用户代理', blank=True)
            blocked = models.BooleanField('是否阻止', default=False)
            created_at = models.DateTimeField('创建时间', auto_now_add=True)

            class Meta:
                verbose_name = '安全事件'
                verbose_name_plural = '安全事件'
                ordering = ['-created_at']
                indexes = [
                    models.Index(fields=['user', 'created_at']),
                    models.Index(fields=['event_type', 'created_at']),
                    models.Index(fields=['severity', 'created_at']),
                    models.Index(fields=['created_at']),
                ]

            def __str__(self):
                return f"{self.event_type} - {self.severity} - {self.ip_address}"

        class UserActivityLog(models.Model):
            """用户活动日志"""

            user = models.ForeignKey(
                User,
                on_delete=models.CASCADE,
                related_name='activity_logs',
                verbose_name='用户'
            )
            activity_type = models.CharField('活动类型', max_length=50)
            description = models.TextField('描述')
            resource = models.CharField('资源', max_length=255, blank=True)
            resource_id = models.CharField('资源ID', max_length=50, blank=True)
            ip_address = models.GenericIPAddressField('IP地址')
            user_agent = models.TextField('用户代理', blank=True)
            duration = models.IntegerField('持续时间(ms)', null=True, blank=True)
            created_at = models.DateTimeField('创建时间', auto_now_add=True)

            class Meta:
                verbose_name = '用户活动日志'
                verbose_name_plural = '用户活动日志'
                logging = ['user', 'activity_type']
                ordering = ['-created_at']
                indexes = [
                    models.Index(fields=['user', 'created_at']),
                    models.Index(fields=['activity_type', 'created_at']),
                    models.Index(fields=['created_at']),
                ]

            def __str__(self):
                return f"{self.user.username} - {self.activity_type}"

        class SystemAuditLog(models.Model):
            """系统审计日志"""

            component = models.CharField('组件', max_length=50)
            level = models.CharField('级别', max_length=20)  # INFO, WARNING, ERROR, CRITICAL
            message = models.TextField('消息')
            details = models.JSONField('详情', default=dict)
            stack_trace = models.TextField('堆栈跟踪', blank=True)
            user = models.ForeignKey(
                User,
                on_delete=models.SET_NULL,
                null=True,
                blank=True,
                related_name='system_logs',
                verbose_name='用户'
            )
            ip_address = models.GenericIPAddressField('IP地址', null=True, blank=True)
            created_at = models.DateTimeField('创建时间', auto_now_add=True)

            class Meta:
                verbose_name = '系统审计日志'
                verbose_name_plural = '系统审计日志'
                ordering = ['-created_at']
                indexes = [
                    models.Index(fields=['component', 'created_at']),
                    models.Index(fields=['level', 'created_at']),
                    models.Index(fields=['created_at']),
                ]

            def __str__(self):
                return f"{self.component} - {self.level} - {self.message[:50]}"

        # 审计日志记录器
        class AuditLogger:
            """审计日志记录器"""

            @staticmethod
            def log_action(user, action, resource_type, resource_id=None, result='SUCCESS', message='', details=None, request=None):
                """记录操作日志"""
                try:
                    ip_address = request.META.get('REMOTE_ADDR') if request else None
                    user_agent = request.META.get('HTTP_USER_AGENT', '') if request else ''
                    session_id = getattr(request, 'session_key', '') if request else ''
                    request_id = getattr(request, 'id', '') if hasattr(request, 'id') else ''

                    AuditLog.objects.create(
                        user=user,
                        action=action,
                        resource_type=resource_type,
                        resource_id=resource_id or '',
                        result=result,
                        message=message,
                        details=details or {},
                        ip_address=ip_address,
                        user_agent=user_agent,
                        request_id=request_id,
                        session_id=session_id,
                    )

                    # 记录到标准日志
                    logger.info(
                        f"Audit: {action} {resource_type}:{resource_id} by {user} - {result} - {message}",
                        extra={
                            'user_id': user.id if user else None,
                            'action': action,
                            'resource_type': resource_type,
                            'resource_id': resource_id,
                            'result': result,
                            'ip_address': ip_address,
                        }
                    )

                except Exception as e:
                    logger.error(f"审计日志记录失败: {str(e)}", exc_info=True)

            @staticmethod
            def log_security_event(user, event_type, severity, description, details=None, blocked=False, request=None):
                """记录安全事件"""
                try:
                    ip_address = request.META.get('REMOTE_ADDR') if request else None
                    user_agent = request.META.get('HTTP_USER_AGENT', '') if request else ''

                    SecurityEvent.objects.create(
                        user=user,
                        event_type=event_type,
                        severity=severity,
                        description=description,
                        details=details or {},
                        ip_address=ip_address,
                        user_agent=user_agent,
                        blocked=blocked,
                        created_at=timezone.now(),
                    )

                    # 记录到标准日志
                    log_level = logging.WARNING if severity in ['MEDIUM', 'HIGH', 'CRITICAL'] else logging.INFO
                    getattr(logger, log_level)(
                        f"Security: {event_type} - {severity} - {description}",
                        extra={
                            'user_id': user.id if user else None,
                            'event_type': event_type,
                            'severity': severity,
                            'blocked': blocked,
                            'ip_address': ip_address,
                        }
                    )

                    # 如果是严重事件,发送通知
                    if severity in ['HIGH', 'CRITICAL']:
                        AuditLogger.send_security_notification(event_type, description, ip_address)

                except Exception as e:
                    logger.error(f"安全事件记录失败: {str(e)}", exc_info=True)

            @staticmethod
            def log_user_activity(user, activity_type, description, resource=None, resource_id=None, duration=None, request=None):
                """记录用户活动"""
                try:
                    ip_address = request.META.get('REMOTE_ADDR') if request else None
                    user_agent = request.META.get('HTTP_USER_AGENT', '') if request else ''

                    UserActivityLog.objects.create(
                        user=user,
                        activity_type=activity_type,
                        description=description,
                        resource=resource or '',
                        resource_id=resource_id or '',
                        ip_address=ip_address,
                        user_agent=user_agent,
                        duration=duration,
                        created_at=timezone.now(),
                    )

                except Exception as e:
                    logger.error(f"用户活动日志记录失败: {str(e)}", exc_info=True)

            @staticmethod
            def log_system_error(component, level, message, details=None, stack_trace=None, user=None, request=None):
                """记录系统错误"""
                try:
                    ip_address = request.META.get('REMOTE_ADDR') if request else None

                    SystemAuditLog.objects.create(
                        component=component,
                        level=level,
                        message=message,
                        details=details or {},
                        stack_trace=stack_trace,
                        user=user,
                        ip_address=ip_address,
                        created_at=timezone.now(),
                    )

                    # 记录到标准日志
                    log_method = getattr(logger, level.lower(), logger.error)
                    log_method(
                        f"System Error [{component}]: {message}",
                        exc_info=True if stack_trace else None,
                        extra={
                            'component': component,
                            'level': level,
                            'user_id': user.id if user else None,
                            'ip_address': ip_address,
                        }
                    )

                    # 如果是严重错误,发送通知
                    if level in ['ERROR', 'CRITICAL']:
                        AuditLogger.send_system_notification(component, message)

                except Exception as e:
                    logger.error(f"系统审计日志记录失败: {str(e)}", exc_info=True)

            @staticmethod
            def send_security_notification(event_type, description, ip_address=None):
                """发送安全通知"""
                try:
                    from django.core.mail import send_mail
                    from django.conf import settings

                    subject = f"安全警报: {event_type}"
                    message = f"检测到安全事件: {description}\nIP地址: {ip_address}\n时间: {timezone.now().isoformat()}"

                    recipients = getattr(settings, 'SECURITY_NOTIFICATION_EMAILS', [])
                    if recipients:
                        send_mail(
                            subject=subject,
                            message=message,
                            from_email=getattr(settings, 'DEFAULT_FROM_EMAIL'),
                            recipient_list=recipients,
                            fail_silently=False,
                        )

                    logger.info(f"安全通知已发送: {event_type}")

                except Exception as e:
                    logger.error(f"发送安全通知失败: {str(e)}")

            @staticmethod
            def send_system_notification(component, message):
                """发送系统通知"""
                try:
                    from django.core.mail import send_mail
                    from django.conf import settings

                    subject = f"系统警报: {component}"
                    full_message = f"组件: {component}\n错误: {message}\n时间: {timezone.now().isoformat()}"

                    recipients = getattr(settings, 'SYSTEM_NOTIFICATION_EMAILS', [])
                    if recipients:
                        send_mail(
                            subject=subject,
                            message=full_message,
                            from_email=getattr(settings, 'DEFAULT_FROM_EMAIL'),
                            recipient_list=recipients,
                            fail_silently=False,
                        )

                    logger.info(f"系统通知已发送: {component}")

                except Exception as e:
                    logger.error(f"发送系统通知失败: {str(e)}")

        # 中间件自动记录
        class AuditMiddleware:
            """审计中间件"""

            def __init__(self, get_response):
                self.get_response = get_response

            def __call__(self, request):
                """处理请求"""
                start_time = timezone.now()

                response = self.get_response(request)

                # 记录请求日志
                duration = int((timezone.now() - start_time).total_seconds() * 1000)
                self.log_request(request, response, duration)

                return response

            def log_request(self, request, response, duration):
                """记录请求日志"""
                try:
                    # 只记录API请求
                    if not request.path.startswith('/api/'):
                        return

                    user = getattr(request, 'user', None)
                    if not user or not user.is_authenticated:
                        return

                    # 确定请求类型
                    if request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
                        action = self.get_action_from_method(request.method)
                        resource_type = self.get_resource_type(request)
                        resource_id = self.get_resource_id(request)

                        # 确定结果
                        if 200 <= response.status_code < 300:
                            result = 'SUCCESS'
                        elif 400 <= response.status_code < 500:
                            result = 'FAILURE'
                        else:
                            result = 'ERROR'

                        # 获取响应内容
                        try:
                            if hasattr(response, 'data'):
                                response_data = response.data
                            details = {
                                'status_code': response.status_code,
                                'content_type': response.get('Content-Type'),
                            }
                            if isinstance(response_data, dict):
                                details['response_keys'] = list(response_data.keys())
                        except:
                            details = {}

                        message = self.get_message_from_response(response)

                        # 记录审计日志
                        AuditLogger.log_action(
                            user=user,
                            action=action,
                            resource_type=resource_type,
                            resource_id=resource_id,
                            result=result,
                            message=message,
                            details=details,
                            request=request
                        )

                        # 检查安全事件
                        self.check_security_events(request, response)

                except Exception as e:
                    logger.error(f"审计中间件记录失败: {str(e)}", exc_info=True)

            def get_action_from_method(self, method):
                """从HTTP方法获取操作类型"""
                method_map = {
                    'GET': 'ACCESS',
                    'POST': 'CREATE',
                    'PUT': 'UPDATE',
                    'PATCH': 'UPDATE',
                    'DELETE': 'DELETE',
                    'OPTIONS': 'ACCESS',
                }
                return method_map.get(method, 'UNKNOWN')

            def get_resource_type(self, request):
                """从请求获取资源类型"""
                path_parts = request.path.strip('/').split('/')
                if len(path_parts) >= 2:
                    return path_parts[1].upper()
                return 'UNKNOWN'

            def get_resource_id(self, request):
                """从请求获取资源ID"""
                # 从URL路径提取ID
                path_parts = request.path.strip('/').split('/')
                if len(path_parts) >= 3:
                    return path_parts[2]
                # 从查询参数提取ID
                elif 'id' in request.GET:
                    return str(request.GET['id'])
                # 从请求体提取ID(仅限POST/PUT)
                elif request.method in ['POST', 'PUT'] and hasattr(request, 'data'):
                    try:
                        import json
                        data = json.loads(request.body) if request.body else {}
                        return data.get('id') or data.get('pk')
                    except:
                        pass
                return ''

            def get_message_from_response(self, response):
                """从响应获取消息"""
                if hasattr(response, 'data') and isinstance(response.data, dict):
                    return response.data.get('message', response.data.get('detail', ''))
                return ''

            def check_security_events(self, request, response):
                """检查安全事件"""
                # 检查权限拒绝
                if response.status_code == 403:
                    AuditLogger.log_security_event(
                        user=getattr(request, 'user', None),
                        event_type='PERMISSION_DENIED',
                        severity='MEDIUM',
                        description=f"权限被拒绝: {request.method} {request.path}",
                        details={
                            'path': request.path,
                            'method': request.method,
                            'status_code': response.status_code,
                        },
                        blocked=True,
                        request=request
                    )

                # 检查认证失败
                elif response.status_code == 401:
                    AuditLogger.log_security_event(
                        user=getattr(request, 'user', None),
                        event_type='AUTHENTICATION_BYPASS',
                        severity='MEDIUM',
                        description=f"认证失败: {request.method} {request.path}",
                        details={
                            'path': request.path,
                            'method': request.method,
                            'status_code': response.status_code,
                        },
                        blocked=False,
                        request=request
                    )

                # 检查速率限制
                elif response.status_code == 429:
                    AuditLogger.log_security_event(
                        user=getattr(request, 'user', None),
                        event_type='RATE_LIMIT_EXCEEDED',
                        severity='MEDIUM',
                        description=f"请求频率超限: {request.method} {request.path}",
                        details={
                            'path': request.path,
                            'method': request.method,
                            'status_code': response.status_code,
                        },
                        blocked=True,
                        request=request
                    )

                # 检查服务器错误
                elif response.status_code >= 500:
                    AuditLogger.log_system_error(
                        component='API',
                        level='ERROR',
                        message=f"服务器错误: {request.method} {request.path} - {response.status_code}",
                        details={
                            'path': request.path,
                            'method': request.method,
                            'status_code': response.status_code,
                            'response_data': str(response.data)[:500] if hasattr(response, 'data') else None,
                        },
                        user=getattr(request, 'user', None),
                        request=request
                    )

        # 生成审计报告
        def generate_audit_report(start_date=None, end_date=None):
            """生成审计报告"""
            try:
                from django.utils import timezone
                from django.db.models import Count
                from datetime import timedelta

                # 设置默认时间范围
                if not end_date:
                    end_date = timezone.now()
                if not start_date:
                    start_date = end_date - timedelta(days=30)

                # 生成报告数据
                report_data = {
                    'period': {
                        'start_date': start_date.isoformat(),
                        'end_date': end_date.isoformat(),
                        'days': (end_date - start_date).days
                    },
                    'overview': {
                        'total_logs': AuditLog.objects.filter(
                            timestamp__gte=start_date,
                            timestamp__lte=end_date
                        ).count(),
                        'successful_operations': AuditLog.objects.filter(
                            timestamp__gte=start_date,
                            timestamp__lte=end_date,
                            result='SUCCESS'
                        ).count(),
                        'failed_operations': AuditLog.objects.filter(
                            timestamp__ge=start_date,
                            timestamp__lte=end_date,
                            result='FAILURE'
                        ).count(),
                        'errors': AuditLog.objects.filter(
                            timestamp__gte=start_date,
                            timestamp__lte=end_date,
                            result='ERROR'
                        ).count(),
                    },
                    'user_activities': {
                        'active_users': UserActivityLog.objects.filter(
                            created_at__gte=start_date,
                            created_at__lte=end_date
                       ).values('user').distinct().count(),
                        'total_activities': UserActivityLog.objects.filter(
                            created_at__gte=start_date,
                            created_at__lte=end_date
                        ).count(),
                    },
                    'security_events': {
                        'total_events': SecurityEvent.objects.filter(
                            created_at__gte=start_date,
                            created_at__lte=end_date
                        ).count(),
                        'blocked_events': SecurityEvent.objects.filter(
                            created_at__gte=start_date,
                            created_at__lte=end_date,
                            blocked=True
                        ).count(),
                        'critical_events': SecurityEvent.objects.filter(
                            created_at__gte=start_date,
                            created_at__lte=end_date,
                            severity='CRITICAL'
                        ).count(),
                        'high_severity_events': SecurityEvent.objects.filter(
                            created_at__gte=start_date,
                            created_at__lte=end_date,
                            severity='HIGH'
                        ).count(),
                        'medium_severity_events': SecurityEvent.objects.filter(
                            created_at__gte=start_date,
                            created_at__lte=end_date,
                            severity='MEDIUM'
                        ).count(),
                    },
                    'top_resources': {
                        'most_accessed_resources': AuditLog.objects.filter(
                            timestamp__gte=start_date,
                            timestamp__lte=end_date
                        ).values('resource_type').annotate(
                            count=Count('id')
                        ).order_by('-count')[:10],
                        'most_active_users': AuditLog.objects.filter(
                            timestamp__gte=start_date,
                            timestamp__lte=end_date,
                            user__isnull=False
                        ).values('user__username').annotate(
                            count=Count('id')
                        ).order_by('-count')[:10],
                    }
                }

                # 保存报告
                from .models import AuditReport
                report = AuditReport.objects.create(
                    start_date=start_date,
                    end_date=end_date,
                    report_data=report_data,
                    created_at=timezone.now()
                )

                logger.info(f"审计报告生成成功: {start_date} 到 {end_date}")

                return report

            except Exception as e:
                logger.error(f"生成审计报告失败: {str(e)}", exc_info=True)
                return None

6 数据验证与过滤

6.1 字段级验证

01.内置验证器
    a.必填字段验证
        DRF提供required参数控制字段是否必填,默认为True。当字段缺失时会返回"This field is required."错误。
        from rest_framework import serializers

        class UserSerializer(serializers.Serializer):
            username = serializers.CharField(required=True, max_length=100)
            email = serializers.EmailField(required=True)
            age = serializers.IntegerField(required=False)  # 可选字段
            bio = serializers.CharField(required=False, allow_blank=True)  # 允许空字符串

        # 测试验证
        serializer = UserSerializer(data={'username': 'john'})
        if not serializer.is_valid():
            print(serializer.errors)
            # 输出: {'email': [ErrorDetail(string='This field is required.', code='required')]}

        # 完整数据验证通过
        serializer = UserSerializer(data={
            'username': 'john',
            'email': '[email protected]',
            'age': 25
        })
        print(serializer.is_valid())  # True
    b.空值处理验证
        allow_null参数控制是否接受None值,allow_blank控制字符串字段是否接受空字符串。
        class ProductSerializer(serializers.Serializer):
            name = serializers.CharField(max_length=200)
            description = serializers.CharField(allow_blank=True)  # 允许空字符串
            price = serializers.DecimalField(max_digits=10, decimal_places=2, allow_null=True)  # 允许None
            stock = serializers.IntegerField(min_value=0)

        # 测试空值处理
        data1 = {'name': 'Product A', 'description': '', 'price': None, 'stock': 10}
        serializer1 = ProductSerializer(data=data1)
        print(serializer1.is_valid())  # True

        # 不允许空值的情况
        data2 = {'name': '', 'description': 'desc', 'price': 100, 'stock': 5}
        serializer2 = ProductSerializer(data=data2)
        if not serializer2.is_valid():
            print(serializer2.errors)
            # 输出: {'name': [ErrorDetail(string='This field may not be blank.', code='blank')]}
    c.长度和范围验证
        使用max_length、min_length、max_value、min_value等参数限制字段值的范围。
        class ArticleSerializer(serializers.Serializer):
            title = serializers.CharField(min_length=5, max_length=200)
            content = serializers.CharField(min_length=100)
            views = serializers.IntegerField(min_value=0, max_value=1000000)
            rating = serializers.FloatField(min_value=0.0, max_value=5.0)

        # 测试长度验证
        data = {
            'title': 'Hi',  # 太短
            'content': 'Short content',  # 太短
            'views': -10,  # 小于最小值
            'rating': 6.0  # 大于最大值
        }
        serializer = ArticleSerializer(data=data)
        if not serializer.is_valid():
            print(serializer.errors)
            # 输出多个验证错误
            # {'title': ['Ensure this field has at least 5 characters.'],
            #  'content': ['Ensure this field has at least 100 characters.'],
            #  'views': ['Ensure this value is greater than or equal to 0.'],
            #  'rating': ['Ensure this value is less than or equal to 5.0.']}
    d.正则表达式验证
        使用RegexValidator或直接在CharField中使用正则表达式验证字段格式。
        from rest_framework.validators import RegexValidator

        class ContactSerializer(serializers.Serializer):
            phone = serializers.CharField(
                validators=[RegexValidator(
                    regex=r'^\+?1?\d{9,15}$',
                    message='Phone number must be entered in the format: +999999999. Up to 15 digits allowed.'
                )]
            )
            zip_code = serializers.CharField(
                validators=[RegexValidator(
                    regex=r'^\d{5}(-\d{4})?$',
                    message='Enter a valid ZIP code in format: 12345 or 12345-6789'
                )]
            )
            username = serializers.CharField(
                validators=[RegexValidator(
                    regex=r'^[a-zA-Z0-9_]{3,20}$',
                    message='Username must be 3-20 characters, alphanumeric and underscore only'
                )]
            )

        # 测试正则验证
        data = {'phone': '123', 'zip_code': 'abc', 'username': 'ab'}
        serializer = ContactSerializer(data=data)
        if not serializer.is_valid():
            print(serializer.errors)

02.自定义字段验证方法
    a.validate_<field_name>方法
        在Serializer类中定义validate_<field_name>方法可以对单个字段进行自定义验证,方法接收字段值作为参数。
        from rest_framework import serializers
        from django.contrib.auth.models import User

        class UserRegistrationSerializer(serializers.Serializer):
            username = serializers.CharField(max_length=150)
            email = serializers.EmailField()
            password = serializers.CharField(min_length=8, write_only=True)
            age = serializers.IntegerField()

            def validate_username(self, value):
                # 检查用户名是否已存在
                if User.objects.filter(username=value).exists():
                    raise serializers.ValidationError("用户名已被使用")
                # 检查用户名是否包含特殊字符
                if not value.isalnum():
                    raise serializers.ValidationError("用户名只能包含字母和数字")
                return value

            def validate_email(self, value):
                # 检查邮箱是否已注册
                if User.objects.filter(email=value).exists():
                    raise serializers.ValidationError("该邮箱已被注册")
                # 检查邮箱域名
                if not value.endswith(('@gmail.com', '@yahoo.com', '@outlook.com')):
                    raise serializers.ValidationError("仅支持Gmail、Yahoo或Outlook邮箱")
                return value

            def validate_age(self, value):
                # 年龄范围验证
                if value < 18:
                    raise serializers.ValidationError("用户必须年满18岁")
                if value > 120:
                    raise serializers.ValidationError("请输入有效的年龄")
                return value
    b.返回验证后的值
        validate_<field_name>方法必须返回验证后的值,可以在验证过程中对值进行清理或转换。
        class ProductSerializer(serializers.Serializer):
            name = serializers.CharField(max_length=200)
            price = serializers.DecimalField(max_digits=10, decimal_places=2)
            discount = serializers.FloatField()

            def validate_name(self, value):
                # 清理产品名称:去除首尾空格,转换为标题格式
                cleaned_value = value.strip().title()
                if len(cleaned_value) < 3:
                    raise serializers.ValidationError("产品名称至少需要3个字符")
                return cleaned_value

            def validate_price(self, value):
                # 价格必须大于0
                if value <= 0:
                    raise serializers.ValidationError("价格必须大于0")
                # 价格不能超过100000
                if value > 100000:
                    raise serializers.ValidationError("价格不能超过100000")
                return value

            def validate_discount(self, value):
                # 折扣范围:0-1之间
                if not 0 <= value <= 1:
                    raise serializers.ValidationError("折扣必须在0到1之间")
                return value

        # 测试
        data = {'name': '  laptop  ', 'price': 999.99, 'discount': 0.15}
        serializer = ProductSerializer(data=data)
        if serializer.is_valid():
            print(serializer.validated_data)
            # 输出: {'name': 'Laptop', 'price': Decimal('999.99'), 'discount': 0.15}
    c.抛出验证错误
        使用serializers.ValidationError抛出验证错误,可以传递字符串或字典作为错误消息。
        class OrderSerializer(serializers.Serializer):
            quantity = serializers.IntegerField()
            unit_price = serializers.DecimalField(max_digits=10, decimal_places=2)
            coupon_code = serializers.CharField(required=False, allow_blank=True)

            def validate_quantity(self, value):
                if value <= 0:
                    raise serializers.ValidationError("数量必须大于0")
                if value > 1000:
                    raise serializers.ValidationError("单次订单数量不能超过1000")
                return value

            def validate_coupon_code(self, value):
                if value:
                    # 假设验证优惠券代码
                    valid_coupons = ['SAVE10', 'SAVE20', 'WELCOME']
                    if value.upper() not in valid_coupons:
                        raise serializers.ValidationError(
                            f"无效的优惠券代码。有效代码:{', '.join(valid_coupons)}"
                        )
                    return value.upper()
                return value

        # 测试验证错误
        data = {'quantity': 1500, 'unit_price': 50.00, 'coupon_code': 'INVALID'}
        serializer = OrderSerializer(data=data)
        if not serializer.is_valid():
            print(serializer.errors)
            # 输出: {'quantity': ['单次订单数量不能超过1000'],
            #       'coupon_code': ['无效的优惠券代码。有效代码:SAVE10, SAVE20, WELCOME']}

03.多字段联合验证
    a.validate方法实现跨字段验证
        validate方法接收所有字段的数据字典,可以实现多个字段之间的联合验证逻辑。
        from datetime import date
        from rest_framework import serializers

        class EventSerializer(serializers.Serializer):
            title = serializers.CharField(max_length=200)
            start_date = serializers.DateField()
            end_date = serializers.DateField()
            max_participants = serializers.IntegerField()
            min_participants = serializers.IntegerField()

            def validate(self, data):
                # 验证结束日期必须晚于开始日期
                if data['end_date'] < data['start_date']:
                    raise serializers.ValidationError({
                        'end_date': '结束日期必须晚于开始日期'
                    })

                # 验证开始日期不能是过去的日期
                if data['start_date'] < date.today():
                    raise serializers.ValidationError({
                        'start_date': '开始日期不能早于今天'
                    })

                # 验证最大参与人数必须大于最小参与人数
                if data['max_participants'] <= data['min_participants']:
                    raise serializers.ValidationError({
                        'max_participants': '最大参与人数必须大于最小参与人数'
                    })

                return data

        # 测试跨字段验证
        from datetime import timedelta
        data = {
            'title': 'Tech Conference',
            'start_date': date.today() + timedelta(days=10),
            'end_date': date.today() + timedelta(days=5),  # 错误:早于开始日期
            'max_participants': 50,
            'min_participants': 100  # 错误:大于最大值
        }
        serializer = EventSerializer(data=data)
        if not serializer.is_valid():
            print(serializer.errors)
    b.条件验证逻辑
        根据某个字段的值决定其他字段的验证规则,实现动态验证逻辑。
        class PaymentSerializer(serializers.Serializer):
            payment_method = serializers.ChoiceField(choices=['credit_card', 'paypal', 'bank_transfer'])
            card_number = serializers.CharField(required=False, allow_blank=True)
            card_cvv = serializers.CharField(required=False, allow_blank=True)
            paypal_email = serializers.EmailField(required=False, allow_blank=True)
            bank_account = serializers.CharField(required=False, allow_blank=True)
            amount = serializers.DecimalField(max_digits=10, decimal_places=2)

            def validate(self, data):
                payment_method = data.get('payment_method')

                # 根据支付方式验证相应字段
                if payment_method == 'credit_card':
                    if not data.get('card_number'):
                        raise serializers.ValidationError({
                            'card_number': '使用信用卡支付时必须提供卡号'
                        })
                    if not data.get('card_cvv'):
                        raise serializers.ValidationError({
                            'card_cvv': '使用信用卡支付时必须提供CVV'
                        })
                    # 验证卡号格式(简化版)
                    if len(data['card_number'].replace(' ', '')) != 16:
                        raise serializers.ValidationError({
                            'card_number': '信用卡号必须是16位数字'
                        })

                elif payment_method == 'paypal':
                    if not data.get('paypal_email'):
                        raise serializers.ValidationError({
                            'paypal_email': '使用PayPal支付时必须提供邮箱'
                        })

                elif payment_method == 'bank_transfer':
                    if not data.get('bank_account'):
                        raise serializers.ValidationError({
                            'bank_account': '使用银行转账时必须提供账号'
                        })
                    # 银行转账金额限制
                    if data['amount'] < 100:
                        raise serializers.ValidationError({
                            'amount': '银行转账最低金额为100'
                        })

                return data
    c.复杂业务逻辑验证
        实现包含多个条件和业务规则的复杂验证逻辑。
        class BookingSerializer(serializers.Serializer):
            room_type = serializers.ChoiceField(choices=['single', 'double', 'suite'])
            check_in = serializers.DateField()
            check_out = serializers.DateField()
            adults = serializers.IntegerField(min_value=1)
            children = serializers.IntegerField(min_value=0)
            special_requests = serializers.CharField(required=False, allow_blank=True)

            def validate(self, data):
                # 验证入住和退房日期
                if data['check_out'] <= data['check_in']:
                    raise serializers.ValidationError({
                        'check_out': '退房日期必须晚于入住日期'
                    })

                # 计算住宿天数
                stay_duration = (data['check_out'] - data['check_in']).days
                if stay_duration > 30:
                    raise serializers.ValidationError('住宿时间不能超过30天')

                # 根据房间类型验证人数
                total_guests = data['adults'] + data['children']
                room_capacity = {
                    'single': 1,
                    'double': 2,
                    'suite': 4
                }

                max_capacity = room_capacity[data['room_type']]
                if total_guests > max_capacity:
                    raise serializers.ValidationError({
                        'room_type': f'{data["room_type"]}房间最多容纳{max_capacity}人'
                    })

                # 验证预订时间(至少提前1天)
                if data['check_in'] <= date.today():
                    raise serializers.ValidationError({
                        'check_in': '入住日期必须至少提前1天预订'
                    })

                # 特殊要求长度限制
                if data.get('special_requests') and len(data['special_requests']) > 500:
                    raise serializers.ValidationError({
                        'special_requests': '特殊要求不能超过500字符'
                    })

                return data

04.验证器类
    a.自定义验证器类
        创建可复用的验证器类,可以在多个序列化器中使用相同的验证逻辑。
        from rest_framework.validators import UniqueValidator

        class MultipleOfValidator:
            """验证数值是否为指定数的倍数"""
            def __init__(self, multiple):
                self.multiple = multiple

            def __call__(self, value):
                if value % self.multiple != 0:
                    raise serializers.ValidationError(
                        f'值必须是{self.multiple}的倍数'
                    )

        class RangeValidator:
            """验证数值是否在指定范围内"""
            def __init__(self, min_value, max_value):
                self.min_value = min_value
                self.max_value = max_value

            def __call__(self, value):
                if not self.min_value <= value <= self.max_value:
                    raise serializers.ValidationError(
                        f'值必须在{self.min_value}到{self.max_value}之间'
                    )

        class ProductSerializer(serializers.Serializer):
            name = serializers.CharField(max_length=200)
            quantity = serializers.IntegerField(
                validators=[MultipleOfValidator(10)]  # 数量必须是10的倍数
            )
            price = serializers.DecimalField(
                max_digits=10,
                decimal_places=2,
                validators=[RangeValidator(1, 10000)]  # 价格范围1-10000
            )

        # 测试自定义验证器
        data = {'name': 'Product', 'quantity': 25, 'price': 15000}
        serializer = ProductSerializer(data=data)
        if not serializer.is_valid():
            print(serializer.errors)
            # 输出: {'quantity': ['值必须是10的倍数'], 'price': ['值必须在1到10000之间']}
    b.组合多个验证器
        在一个字段上应用多个验证器,按顺序执行所有验证逻辑。
        from rest_framework.validators import UniqueValidator, RegexValidator
        from django.contrib.auth.models import User

        class UsernameValidator:
            """自定义用户名验证器"""
            def __call__(self, value):
                # 不能以数字开头
                if value[0].isdigit():
                    raise serializers.ValidationError('用户名不能以数字开头')
                # 不能包含连续的下划线
                if '__' in value:
                    raise serializers.ValidationError('用户名不能包含连续的下划线')

        class UserSerializer(serializers.ModelSerializer):
            username = serializers.CharField(
                max_length=150,
                validators=[
                    UniqueValidator(queryset=User.objects.all(), message='用户名已存在'),
                    RegexValidator(
                        regex=r'^[a-zA-Z][a-zA-Z0-9_]{2,19}$',
                        message='用户名必须以字母开头,3-20个字符,只能包含字母、数字和下划线'
                    ),
                    UsernameValidator()
                ]
            )
            email = serializers.EmailField(
                validators=[
                    UniqueValidator(queryset=User.objects.all(), message='邮箱已被注册')
                ]
            )

            class Meta:
                model = User
                fields = ['username', 'email', 'password']
                extra_kwargs = {'password': {'write_only': True}}
    c.验证器的配置和参数
        验证器可以接收配置参数,实现灵活的验证逻辑。
        class FileExtensionValidator:
            """文件扩展名验证器"""
            def __init__(self, allowed_extensions):
                self.allowed_extensions = [ext.lower() for ext in allowed_extensions]

            def __call__(self, value):
                ext = value.name.split('.')[-1].lower()
                if ext not in self.allowed_extensions:
                    raise serializers.ValidationError(
                        f'不支持的文件格式。允许的格式:{", ".join(self.allowed_extensions)}'
                    )

        class FileSizeValidator:
            """文件大小验证器"""
            def __init__(self, max_size_mb):
                self.max_size = max_size_mb * 1024 * 1024  # 转换为字节

            def __call__(self, value):
                if value.size > self.max_size:
                    raise serializers.ValidationError(
                        f'文件大小不能超过{self.max_size / (1024 * 1024):.1f}MB'
                    )

        class DocumentSerializer(serializers.Serializer):
            title = serializers.CharField(max_length=200)
            file = serializers.FileField(
                validators=[
                    FileExtensionValidator(['pdf', 'doc', 'docx']),
                    FileSizeValidator(max_size_mb=5)
                ]
            )
            image = serializers.ImageField(
                required=False,
                validators=[
                    FileExtensionValidator(['jpg', 'jpeg', 'png', 'gif']),
                    FileSizeValidator(max_size_mb=2)
                ]
            )

        # 使用示例
        # data = {'title': 'Report', 'file': uploaded_file}
        # serializer = DocumentSerializer(data=data)
        # if serializer.is_valid():
        #     serializer.save()

6.2 查询过滤

01.基础过滤实现
    a.使用filter_backends配置
        DRF提供filter_backends属性来配置过滤后端,支持多种过滤方式。
        from rest_framework import generics, filters
        from django_filters.rest_framework import DjangoFilterBackend
        from .models import Product

        from .serializers import ProductSerializer

        class ProductListView(generics.ListAPIView):
            queryset = Product.objects.all()
            serializer_class = ProductSerializer
            filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
            filterset_fields = ['category', 'price', 'in_stock']
            search_fields = ['name', 'description']
            ordering_fields = ['price', 'created_at']
            ordering = ['-created_at']  # 默认排序

        # URL示例:
        # /api/products/?category=electronics&price=100
        # /api/products/?search=laptop
        # /api/products/?ordering=-price
    b.DjangoFilterBackend使用
        django-filter库提供强大的过滤功能,支持精确匹配、范围查询等。
        from django_filters import rest_framework as filters
        from .models import Article

        class ArticleFilter(filters.FilterSet):
            title = filters.CharFilter(lookup_expr='icontains')  # 标题包含
            min_views = filters.NumberFilter(field_name='views', lookup_expr='gte')
            max_views = filters.NumberFilter(field_name='views', lookup_expr='lte')
            published_after = filters.DateFilter(field_name='published_at', lookup_expr='gte')
            published_before = filters.DateFilter(field_name='published_at', lookup_expr='lte')
            author = filters.CharFilter(field_name='author__username', lookup_expr='icontains')

            class Meta:
                model = Article
                fields = ['title', 'status', 'category']

        class ArticleListView(generics.ListAPIView):
            queryset = Article.objects.all()
            serializer_class = ArticleSerializer
            filter_backends = [DjangoFilterBackend]
            filterset_class = ArticleFilter

        # URL示例:
        # /api/articles/?title=django&min_views=100&max_views=1000
        # /api/articles/?published_after=2024-01-01&author=john
    c.SearchFilter搜索功能
        SearchFilter提供全文搜索功能,支持多字段搜索和搜索词高亮。
        from rest_framework import generics, filters
        from .models import Book

        class BookListView(generics.ListAPIView):
            queryset = Book.objects.all()
            serializer_class = BookSerializer
            filter_backends = [filters.SearchFilter]
            search_fields = ['title', 'author', 'isbn', 'description']
            # 使用^表示开头匹配,=表示精确匹配,@表示全文搜索,$表示正则匹配
            # search_fields = ['^title', '=isbn', '@description']

        # URL示例:
        # /api/books/?search=python
        # /api/books/?search=978-0-13-468599-1

        # 多字段搜索示例
        class AdvancedBookListView(generics.ListAPIView):
            queryset = Book.objects.all()
            serializer_class = BookSerializer
            filter_backends = [filters.SearchFilter]
            search_fields = [
                '^title',  # 标题开头匹配(优先级高)
                'author',  # 作者包含匹配
                '=isbn',   # ISBN精确匹配
                '@description'  # 描述全文搜索
            ]

02.高级过滤技术
    a.自定义FilterSet类
        创建自定义FilterSet类实现复杂的过滤逻辑和多条件组合。
        from django_filters import rest_framework as filters
        from django.db.models import Q
        from .models import Order

        class OrderFilter(filters.FilterSet):
            # 日期范围过滤
            date_from = filters.DateFilter(field_name='created_at', lookup_expr='gte')
            date_to = filters.DateFilter(field_name='created_at', lookup_expr='lte')

            # 价格范围过滤
            min_price = filters.NumberFilter(field_name='total_amount', lookup_expr='gte')
            max_price = filters.NumberFilter(field_name='total_amount', lookup_expr='lte')

            # 状态多选过滤
            status = filters.MultipleChoiceFilter(
                choices=[('pending', 'Pending'), ('paid', 'Paid'), ('shipped', 'Shipped')],
                lookup_expr='in'
            )

            # 客户搜索(支持用户名或邮箱)
            customer = filters.CharFilter(method='filter_customer')

            # 是否包含优惠券
            has_coupon = filters.BooleanFilter(method='filter_has_coupon')

            def filter_customer(self, queryset, name, value):
                return queryset.filter(
                    Q(customer__username__icontains=value) |
                    Q(customer__email__icontains=value)
                )

            def filter_has_coupon(self, queryset, name, value):
                if value:
                    return queryset.exclude(coupon__isnull=True)
                return queryset.filter(coupon__isnull=True)

            class Meta:
                model = Order
                fields = ['status', 'payment_method']

        class OrderListView(generics.ListAPIView):
            queryset = Order.objects.select_related('customer', 'coupon')
            serializer_class = OrderSerializer
            filter_backends = [DjangoFilterBackend]
            filterset_class = OrderFilter
    b.方法过滤器实现
        使用method参数创建自定义过滤方法,实现复杂的查询逻辑。
        from django_filters import rest_framework as filters
        from django.db.models import Count, Avg
        from .models import Course

        class CourseFilter(filters.FilterSet):
            # 按评分范围过滤
            min_rating = filters.NumberFilter(method='filter_min_rating')

            # 按学生数量过滤
            min_students = filters.NumberFilter(method='filter_min_students')

            # 按价格区间过滤
            price_range = filters.ChoiceFilter(
                choices=[
                    ('free', 'Free'),
                    ('low', 'Low (0-50)'),
                    ('medium', 'Medium (50-200)'),
                    ('high', 'High (200+)')
                ],
                method='filter_price_range'
            )

            # 按难度级别过滤
            difficulty = filters.MultipleChoiceFilter(
                choices=[('beginner', 'Beginner'), ('intermediate', 'Intermediate'), ('advanced', 'Advanced')]
            )

            def filter_min_rating(self, queryset, name, value):
                return queryset.annotate(
                    avg_rating=Avg('reviews__rating')
                ).filter(avg_rating__gte=value)

            def filter_min_students(self, queryset, name, value):
                return queryset.annotate(
                    student_count=Count('enrollments')
                ).filter(student_count__gte=value)

            def filter_price_range(self, queryset, name, value):
                if value == 'free':
                    return queryset.filter(price=0)
                elif value == 'low':
                    return queryset.filter(price__gt=0, price__lte=50)
                elif value == 'medium':
                    return queryset.filter(price__gt=50, price__lte=200)
                elif value == 'high':
                    return queryset.filter(price__gt=200)
                return queryset

            class Meta:
                model = Course
                fields = ['category', 'instructor', 'difficulty']

        # URL示例:
        # /api/courses/?min_rating=4.5&min_students=100
        # /api/courses/?price_range=medium&difficulty=beginner,intermediate
    c.多条件组合过滤
        实现AND、OR、NOT等逻辑组合的复杂过滤条件。
        from django_filters import rest_framework as filters
        from django.db.models import Q
        from .models import Property

        class PropertyFilter(filters.FilterSet):
            # 位置过滤(城市或区域)
            location = filters.CharFilter(method='filter_location')

            # 价格范围
            min_price = filters.NumberFilter(field_name='price', lookup_expr='gte')
            max_price = filters.NumberFilter(field_name='price', lookup_expr='lte')

            # 房间数量范围
            min_bedrooms = filters.NumberFilter(field_name='bedrooms', lookup_expr='gte')
            max_bedrooms = filters.NumberFilter(field_name='bedrooms', lookup_expr='lte')

            # 面积范围
            min_area = filters.NumberFilter(field_name='area', lookup_expr='gte')
            max_area = filters.NumberFilter(field_name='area', lookup_expr='lte')

            # 特性过滤(支持多选)
            features = filters.CharFilter(method='filter_features')

            # 可用性过滤
            available = filters.BooleanFilter(field_name='is_available')

            def filter_location(self, queryset, name, value):
                # 搜索城市、区域或邮编
                return queryset.filter(
                    Q(city__icontains=value) |
                    Q(district__icontains=value) |
                    Q(zip_code__icontains=value)
                )

            def filter_features(self, queryset, name, value):
                # 支持逗号分隔的多个特性,如:parking,pool,gym
                features_list = [f.strip() for f in value.split(',')]
                query = Q()
                for feature in features_list:
                    query &= Q(features__name__icontains=feature)
                return queryset.filter(query).distinct()

            class Meta:
                model = Property
                fields = ['property_type', 'available']

        # URL示例:
        # /api/properties/?location=beijing&min_price=1000000&max_price=5000000
        # /api/properties/?min_bedrooms=2&max_bedrooms=4&features=parking,pool
        # /api/properties/?property_type=apartment&available=true

03.排序功能
    a.OrderingFilter配置
        使用OrderingFilter实现多字段排序和默认排序设置。
        from rest_framework import generics, filters
        from .models import Product

        class ProductListView(generics.ListAPIView):
            queryset = Product.objects.all()
            serializer_class = ProductSerializer
            filter_backends = [filters.OrderingFilter]
            ordering_fields = ['price', 'created_at', 'name', 'rating', 'sales_count']
            ordering = ['-created_at']  # 默认按创建时间降序

        # URL示例:
        # /api/products/?ordering=price  # 价格升序
        # /api/products/?ordering=-price  # 价格降序
        # /api/products/?ordering=price,-created_at  # 多字段排序

        # 限制排序字段示例
        class RestrictedProductListView(generics.ListAPIView):
            queryset = Product.objects.all()
            serializer_class = ProductSerializer
            filter_backends = [filters.OrderingFilter]
            ordering_fields = ['price', 'rating']  # 只允许按价格和评分排序
            ordering = ['price']
    b.多字段排序
        实现复杂的多字段排序逻辑,包括关联模型字段排序。
        from rest_framework import generics, filters
        from django.db.models import Count, Avg
        from .models import Article

        class ArticleListView(generics.ListAPIView):
            serializer_class = ArticleSerializer
            filter_backends = [filters.OrderingFilter]
            ordering_fields = [
                'title',
                'published_at',
                'views',
                'author__username',  # 关联字段排序
                'comment_count',  # 聚合字段排序
                'avg_rating'  # 聚合字段排序
            ]
            ordering = ['-published_at']

            def get_queryset(self):
                # 添加聚合字段用于排序
                return Article.objects.annotate(
                    comment_count=Count('comments'),
                    avg_rating=Avg('ratings__score')
                ).select_related('author')

        # URL示例:
        # /api/articles/?ordering=author__username,-published_at
        # /api/articles/?ordering=-comment_count
        # /api/articles/?ordering=-avg_rating,title
    c.自定义排序逻辑
        实现基于复杂业务规则的自定义排序逻辑。
        from rest_framework import generics, filters
        from django.db.models import Case, When, Value, IntegerField, F
        from .models import Task

        class CustomOrderingFilter(filters.OrderingFilter):
            def filter_queryset(self, request, queryset, view):
                ordering = self.get_ordering(request, queryset, view)

                if ordering:
                    # 处理自定义排序字段
                    if 'priority' in ordering or '-priority' in ordering:
                        # 自定义优先级排序:urgent > high > medium > low
                        priority_order = Case(
                            When(priority='urgent', then=Value(1)),
                            When(priority='high', then=Value(2)),
                            When(priority='medium', then=Value(3)),
                            When(priority='low', then=Value(4)),
                            default=Value(5),
                            output_field=IntegerField()
                        )

                        if '-priority' in ordering:
                            queryset = queryset.annotate(priority_order=priority_order).order_by('-priority_order')
                        else:
                            queryset = queryset.annotate(priority_order=priority_order).order_by('priority_order')

                        # 移除priority字段,处理其他排序字段
                        ordering = [o for o in ordering if o not in ['priority', '-priority']]

                if ordering:
                    return queryset.order_by(*ordering)
                return queryset

        class TaskListView(generics.ListAPIView):
            queryset = Task.objects.all()
            serializer_class = TaskSerializer
            filter_backends = [CustomOrderingFilter]
            ordering_fields = ['priority', 'due_date', 'created_at', 'status']
            ordering = ['priority', 'due_date']

        # URL示例:
        # /api/tasks/?ordering=priority,-due_date
        # /api/tasks/?ordering=-priority,created_at

04.分页与过滤结合
    a.过滤后分页处理
        在过滤结果上应用分页,确保性能和用户体验。
        from rest_framework import generics
        from rest_framework.pagination import PageNumberPagination
        from django_filters.rest_framework import DjangoFilterBackend
        from rest_framework import filters

        class StandardResultsSetPagination(PageNumberPagination):
            page_size = 20
            page_size_query_param = 'page_size'
            max_page_size = 100

        class ProductListView(generics.ListAPIView):
            queryset = Product.objects.all()
            serializer_class = ProductSerializer
            pagination_class = StandardResultsSetPagination
            filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
            filterset_fields = ['category', 'brand', 'in_stock']
            search_fields = ['name', 'description']
            ordering_fields = ['price', 'created_at', 'rating']

        # URL示例:
        # /api/products/?category=electronics&page=2&page_size=50
        # /api/products/?search=laptop&ordering=-price&page=1

        # 响应格式:
        # {
        #     "count": 150,
        #     "next": "http://api.example.com/products/?page=3",
        #     "previous": "http://api.example.com/products/?page=1",
        #     "results": [...]
        # }
    b.性能优化策略
        优化过滤和分页的数据库查询性能,减少查询次数和数据传输量。
        from rest_framework import generics
        from django.db.models import Prefetch, Count, Avg
        from django_filters.rest_framework import DjangoFilterBackend

        class OptimizedProductListView(generics.ListAPIView):
            serializer_class = ProductSerializer
            filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
            filterset_fields = ['category', 'brand']
            ordering_fields = ['price', 'rating']
            pagination_class = StandardResultsSetPagination

            def get_queryset(self):
                # 使用select_related减少关联查询
                queryset = Product.objects.select_related(
                    'category',
                    'brand',
                    'manufacturer'
                )

                # 使用prefetch_related优化多对多和反向外键
                queryset = queryset.prefetch_related(
                    'images',
                    'tags',
                    Prefetch('reviews', queryset=Review.objects.filter(is_approved=True))
                )

                # 添加聚合字段
                queryset = queryset.annotate(
                    review_count=Count('reviews'),
                    avg_rating=Avg('reviews__rating')
                )

                # 只查询需要的字段
                queryset = queryset.only(
                    'id', 'name', 'price', 'category__name',
                    'brand__name', 'in_stock', 'created_at'
                )

                return queryset

        # 使用缓存优化
        from django.core.cache import cache
        from django.utils.encoding import force_str

        class CachedProductListView(generics.ListAPIView):
            queryset = Product.objects.all()
            serializer_class = ProductSerializer
            filter_backends = [DjangoFilterBackend]
            filterset_fields = ['category']

            def list(self, request, *args, **kwargs):
                # 生成缓存键
                cache_key = f"products_{force_str(request.GET.urlencode())}"

                # 尝试从缓存获取
                cached_data = cache.get(cache_key)
                if cached_data:
                    return Response(cached_data)

                # 执行查询
                response = super().list(request, *args, **kwargs)

                # 缓存结果(5分钟)
                cache.set(cache_key, response.data, 300)

                return response
    c.过滤结果统计
        提供过滤结果的统计信息,如总数、平均值、最大最小值等。
        from rest_framework import generics
        from rest_framework.response import Response
        from django.db.models import Count, Avg, Max, Min, Sum
        from django_filters.rest_framework import DjangoFilterBackend

        class ProductListWithStatsView(generics.ListAPIView):
            queryset = Product.objects.all()
            serializer_class = ProductSerializer
            filter_backends = [DjangoFilterBackend]
            filterset_fields = ['category', 'brand']
            pagination_class = StandardResultsSetPagination

            def list(self, request, *args, **kwargs):
                # 获取过滤后的queryset
                queryset = self.filter_queryset(self.get_queryset())

                # 计算统计信息
                stats = queryset.aggregate(
                    total_count=Count('id'),
                    avg_price=Avg('price'),
                    min_price=Min('price'),
                    max_price=Max('price'),
                    total_stock=Sum('stock_quantity'),
                    avg_rating=Avg('rating')
                )

                # 分页
                page = self.paginate_queryset(queryset)
                if page is not None:
                    serializer = self.get_serializer(page, many=True)
                    response = self.get_paginated_response(serializer.data)
                    response.data['stats'] = stats
                    return response

                serializer = self.get_serializer(queryset, many=True)
                return Response({
                    'results': serializer.data,
                    'stats': stats
                })

        # 响应格式:
        # {
        #     "count": 150,
        #     "next": "...",
        #     "previous": "...",
        #     "results": [...],
        #     "stats": {
        #         "total_count": 150,
        #         "avg_price": 299.99,
        #         "min_price": 49.99,
        #         "max_price": 1999.99,
        #         "total_stock": 5000,
        #         "avg_rating": 4.3
        #     }
        # }

7 关系处理

7.1 序列化器关系字段

01.关系字段类型概述
    a.核心关系字段
        DRF提供多种关系字段类型用于处理模型间的关联关系。每种字段类型适用于不同的序列化场景,选择合适的字段类型可以优化API性能和数据表示。
    b.PrimaryKeyRelatedField
        使用主键表示关联对象,这是最轻量级的关系表示方式,适合客户端已知对象ID的场景。
        ---
        from rest_framework import serializers
        from django.contrib.auth.models import User
        from .models import Post, Comment, Category

        # 模型定义
        # models.py
        from django.db import models

        class Category(models.Model):
            name = models.CharField(max_length=100)
            slug = models.SlugField(unique=True)

            def __str__(self):
                return self.name

        class Post(models.Model):
            title = models.CharField(max_length=200)
            content = models.TextField()
            author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
            category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='posts')
            tags = models.ManyToManyField('Tag', related_name='posts', blank=True)
            created_at = models.DateTimeField(auto_now_add=True)

            class Meta:
                ordering = ['-created_at']

        class Tag(models.Model):
            name = models.CharField(max_length=50, unique=True)

            def __str__(self):
                return self.name

        class Comment(models.Model):
            post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
            author = models.ForeignKey(User, on_delete=models.CASCADE)
            content = models.TextField()
            created_at = models.DateTimeField(auto_now_add=True)

        # 使用PrimaryKeyRelatedField的序列化器
        class PostSerializer(serializers.ModelSerializer):
            # 使用主键表示作者,返回用户ID
            author = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
            # 使用主键表示分类
            category = serializers.PrimaryKeyRelatedField(
                queryset=Category.objects.all(),
                allow_null=True  # 允许为空
            )
            # 多对多关系也可以使用主键列表
            tags = serializers.PrimaryKeyRelatedField(
                many=True,  # 表示多对多关系
                queryset=Tag.objects.all(),
                required=False  # 可选字段
            )

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'author', 'category', 'tags', 'created_at']

        # 输出示例:
        # {
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author": 5,  # 用户ID
        #     "category": 2,  # 分类ID
        #     "tags": [1, 3, 5],  # 标签ID列表
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---
    c.StringRelatedField
        使用模型的__str__方法返回值表示关联对象,适合只读展示场景,提供更友好的数据表示。
        ---
        class PostListSerializer(serializers.ModelSerializer):
            # 使用__str__方法返回值,只读字段
            author = serializers.StringRelatedField()
            category = serializers.StringRelatedField()
            tags = serializers.StringRelatedField(many=True)

            class Meta:
                model = Post
                fields = ['id', 'title', 'author', 'category', 'tags', 'created_at']
                read_only_fields = ['author', 'category', 'tags']

        # 输出示例:
        # {
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "author": "admin",  # User的__str__返回值
        #     "category": "Python开发",  # Category的__str__返回值
        #     "tags": ["Django", "REST", "API"],  # Tag的__str__返回值列表
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---
    d.SlugRelatedField
        使用模型的指定字段表示关联对象,常用于使用slug、username等唯一标识符的场景。
        ---
        class PostDetailSerializer(serializers.ModelSerializer):
            # 使用username字段表示作者
            author = serializers.SlugRelatedField(
                slug_field='username',  # 使用User模型的username字段
                queryset=User.objects.all()
            )
            # 使用slug字段表示分类
            category = serializers.SlugRelatedField(
                slug_field='slug',
                queryset=Category.objects.all(),
                allow_null=True
            )
            # 使用name字段表示标签
            tags = serializers.SlugRelatedField(
                many=True,
                slug_field='name',
                queryset=Tag.objects.all(),
                required=False
            )

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'author', 'category', 'tags', 'created_at']

        # 输出示例:
        # {
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author": "admin",  # username
        #     "category": "python-dev",  # slug
        #     "tags": ["django", "rest", "api"],  # name列表
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---

02.嵌套序列化器的使用
    a.基本嵌套序列化
        嵌套序列化器允许在一个序列化器中完整展示关联对象的所有字段,提供最丰富的数据表示,但会增加查询复杂度。
        ---
        # 定义用户序列化器
        class UserSerializer(serializers.ModelSerializer):
            class Meta:
                model = User
                fields = ['id', 'username', 'email', 'first_name', 'last_name']

        # 定义分类序列化器
        class CategorySerializer(serializers.ModelSerializer):
            class Meta:
                model = Category
                fields = ['id', 'name', 'slug']

        # 定义标签序列化器
        class TagSerializer(serializers.ModelSerializer):
            class Meta:
                model = Tag
                fields = ['id', 'name']

        # 在Post序列化器中嵌套使用
        class PostNestedSerializer(serializers.ModelSerializer):
            # 嵌套完整的用户信息
            author = UserSerializer(read_only=True)
            # 嵌套完整的分类信息
            category = CategorySerializer(read_only=True)
            # 嵌套完整的标签列表
            tags = TagSerializer(many=True, read_only=True)
            # 添加评论数量统计
            comments_count = serializers.SerializerMethodField()

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'author', 'category', 'tags',
                         'comments_count', 'created_at']

            def get_comments_count(self, obj):
                return obj.comments.count()

        # 输出示例:
        # {
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author": {
        #         "id": 5,
        #         "username": "admin",
        #         "email": "[email protected]",
        #         "first_name": "Admin",
        #         "last_name": "User"
        #     },
        #     "category": {
        #         "id": 2,
        #         "name": "Python开发",
        #         "slug": "python-dev"
        #     },
        #     "tags": [
        #         {"id": 1, "name": "Django"},
        #         {"id": 3, "name": "REST"},
        #         {"id": 5, "name": "API"}
        #     ],
        #     "comments_count": 12,
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---
    b.深度嵌套控制
        使用depth参数可以自动嵌套指定层级的关联对象,简化序列化器定义,但需注意性能影响。
        ---
        class PostAutoNestedSerializer(serializers.ModelSerializer):
            class Meta:
                model = Post
                fields = '__all__'
                # depth=1 自动嵌套一层关联对象
                # 会自动展开author、category等外键字段
                depth = 1

        # 使用depth的优缺点
        # 优点:无需手动定义嵌套序列化器,代码简洁
        # 缺点:无法精确控制嵌套字段,可能暴露不必要的数据

        # 更精确的控制方式:混合使用
        class PostMixedSerializer(serializers.ModelSerializer):
            # 对author使用嵌套序列化器,精确控制字段
            author = UserSerializer(read_only=True)
            # 对category使用简单的字符串表示
            category_name = serializers.CharField(source='category.name', read_only=True)
            # 对tags只返回名称列表
            tag_names = serializers.SerializerMethodField()

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'author', 'category_name',
                         'tag_names', 'created_at']

            def get_tag_names(self, obj):
                return [tag.name for tag in obj.tags.all()]

        # 输出示例:
        # {
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author": {
        #         "id": 5,
        #         "username": "admin",
        #         "email": "[email protected]",
        #         "first_name": "Admin",
        #         "last_name": "User"
        #     },
        #     "category_name": "Python开发",
        #     "tag_names": ["Django", "REST", "API"],
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---

03.关系字段的read_only和write_only配置
    a.read_only字段配置
        read_only字段只在序列化输出时使用,不参与反序列化验证,常用于自动生成或计算的关联字段。
        ---
        class CommentSerializer(serializers.ModelSerializer):
            # post字段在创建时通过URL参数传入,不在请求体中
            post = serializers.PrimaryKeyRelatedField(read_only=True)
            # author从request.user获取,不允许客户端指定
            author = UserSerializer(read_only=True)
            # 添加作者用户名的快捷访问
            author_username = serializers.CharField(source='author.username', read_only=True)

            class Meta:
                model = Comment
                fields = ['id', 'post', 'author', 'author_username', 'content', 'created_at']
                read_only_fields = ['created_at']

        # 在视图中设置post和author
        from rest_framework import generics
        from rest_framework.permissions import IsAuthenticated

        class CommentCreateView(generics.CreateAPIView):
            serializer_class = CommentSerializer
            permission_classes = [IsAuthenticated]

            def perform_create(self, serializer):
                # 从URL参数获取post_id
                post_id = self.kwargs.get('post_id')
                # 从认证用户获取author
                serializer.save(
                    post_id=post_id,
                    author=self.request.user
                )

        # 请求示例(POST /api/posts/1/comments/):
        # {
        #     "content": "这是一条评论"
        # }

        # 响应示例:
        # {
        #     "id": 10,
        #     "post": 1,
        #     "author": {
        #         "id": 5,
        #         "username": "admin",
        #         "email": "[email protected]",
        #         "first_name": "Admin",
        #         "last_name": "User"
        #     },
        #     "author_username": "admin",
        #     "content": "这是一条评论",
        #     "created_at": "2024-01-15T14:20:00Z"
        # }
        ---
    b.write_only字段配置
        write_only字段只在反序列化时使用,不在序列化输出中显示,常用于敏感数据或临时处理字段。
        ---
        class PostCreateSerializer(serializers.ModelSerializer):
            # author_id只用于创建,不在响应中返回
            author_id = serializers.PrimaryKeyRelatedField(
                queryset=User.objects.all(),
                source='author',
                write_only=True
            )
            # category_slug用于创建,使用slug查找分类
            category_slug = serializers.SlugRelatedField(
                slug_field='slug',
                queryset=Category.objects.all(),
                source='category',
                write_only=True,
                allow_null=True,
                required=False
            )
            # tag_names用于创建,使用名称列表
            tag_names = serializers.ListField(
                child=serializers.CharField(),
                write_only=True,
                required=False
            )

            # 响应时使用嵌套序列化器
            author = UserSerializer(read_only=True)
            category = CategorySerializer(read_only=True)
            tags = TagSerializer(many=True, read_only=True)

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'author_id', 'author',
                         'category_slug', 'category', 'tag_names', 'tags', 'created_at']

            def create(self, validated_data):
                # 处理tag_names
                tag_names = validated_data.pop('tag_names', [])
                post = Post.objects.create(**validated_data)

                # 根据名称获取或创建标签
                for tag_name in tag_names:
                    tag, created = Tag.objects.get_or_create(name=tag_name)
                    post.tags.add(tag)

                return post

        # 请求示例(POST /api/posts/):
        # {
        #     "title": "新文章",
        #     "content": "文章内容...",
        #     "author_id": 5,
        #     "category_slug": "python-dev",
        #     "tag_names": ["Django", "Python", "Web"]
        # }

        # 响应示例(不包含write_only字段):
        # {
        #     "id": 2,
        #     "title": "新文章",
        #     "content": "文章内容...",
        #     "author": {
        #         "id": 5,
        #         "username": "admin",
        #         "email": "[email protected]",
        #         "first_name": "Admin",
        #         "last_name": "User"
        #     },
        #     "category": {
        #         "id": 2,
        #         "name": "Python开发",
        #         "slug": "python-dev"
        #     },
        #     "tags": [
        #         {"id": 1, "name": "Django"},
        #         {"id": 2, "name": "Python"},
        #         {"id": 6, "name": "Web"}
        #     ],
        #     "created_at": "2024-01-15T15:00:00Z"
        # }
        ---

04.自定义关系字段
    a.继承RelatedField创建自定义字段
        通过继承RelatedField类可以创建完全自定义的关系字段表示方式,满足特殊业务需求。
        ---
        from rest_framework import serializers

        # 自定义关系字段:返回用户的显示名称
        class UserDisplayField(serializers.RelatedField):
            """
            自定义字段,返回用户的友好显示名称
            格式:username (first_name last_name)
            """
            def to_representation(self, value):
                # value是User对象
                if value.first_name and value.last_name:
                    return f"{value.username} ({value.first_name} {value.last_name})"
                return value.username

            def to_internal_value(self, data):
                # data是客户端提交的值(username)
                try:
                    return User.objects.get(username=data)
                except User.DoesNotExist:
                    raise serializers.ValidationError(f"用户 '{data}' 不存在")

        # 自定义关系字段:返回分类的完整路径
        class CategoryPathField(serializers.RelatedField):
            """
            返回分类的完整路径,如:技术/Python/Django
            """
            def to_representation(self, value):
                # 假设Category有parent字段构成树形结构
                path_parts = [value.name]
                current = value
                while hasattr(current, 'parent') and current.parent:
                    current = current.parent
                    path_parts.insert(0, current.name)
                return ' / '.join(path_parts)

            def to_internal_value(self, data):
                # 使用slug查找分类
                try:
                    return Category.objects.get(slug=data)
                except Category.DoesNotExist:
                    raise serializers.ValidationError(f"分类 '{data}' 不存在")

        # 使用自定义字段
        class PostCustomSerializer(serializers.ModelSerializer):
            author = UserDisplayField(queryset=User.objects.all())
            category = CategoryPathField(
                queryset=Category.objects.all(),
                allow_null=True
            )

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'author', 'category', 'created_at']

        # 输出示例:
        # {
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author": "admin (Admin User)",
        #     "category": "技术 / Python / Web框架",
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---
    b.使用SerializerMethodField实现复杂关系逻辑
        SerializerMethodField提供了最大的灵活性,可以实现任意复杂的关系数据处理逻辑。
        ---
        class PostAdvancedSerializer(serializers.ModelSerializer):
            # 自定义作者信息,包含统计数据
            author_info = serializers.SerializerMethodField()
            # 相关文章推荐
            related_posts = serializers.SerializerMethodField()
            # 热门评论
            top_comments = serializers.SerializerMethodField()

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'author_info', 'related_posts',
                         'top_comments', 'created_at']

            def get_author_info(self, obj):
                """返回作者信息及其统计数据"""
                author = obj.author
                return {
                    'id': author.id,
                    'username': author.username,
                    'email': author.email,
                    'posts_count': author.posts.count(),
                    'total_comments': Comment.objects.filter(post__author=author).count(),
                    'joined_date': author.date_joined.strftime('%Y-%m-%d')
                }

            def get_related_posts(self, obj):
                """返回相同分类或标签的相关文章"""
                # 获取相同分类的文章
                related = Post.objects.filter(
                    category=obj.category
                ).exclude(
                    id=obj.id
                )[:3]

                return [{
                    'id': post.id,
                    'title': post.title,
                    'author': post.author.username,
                    'created_at': post.created_at
                } for post in related]

            def get_top_comments(self, obj):
                """返回点赞最多的评论(假设Comment有likes字段)"""
                # 这里简化为返回最新的3条评论
                comments = obj.comments.select_related('author').order_by('-created_at')[:3]

                return [{
                    'id': comment.id,
                    'author': comment.author.username,
                    'content': comment.content[:100],  # 截取前100字符
                    'created_at': comment.created_at
                } for comment in comments]

        # 输出示例:
        # {
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author_info": {
        #         "id": 5,
        #         "username": "admin",
        #         "email": "[email protected]",
        #         "posts_count": 15,
        #         "total_comments": 87,
        #         "joined_date": "2023-06-01"
        #     },
        #     "related_posts": [
        #         {
        #             "id": 3,
        #             "title": "Django视图详解",
        #             "author": "admin",
        #             "created_at": "2024-01-10T09:00:00Z"
        #         },
        #         {
        #             "id": 5,
        #             "title": "Django模型最佳实践",
        #             "author": "developer",
        #             "created_at": "2024-01-08T14:30:00Z"
        #         }
        #     ],
        #     "top_comments": [
        #         {
        #             "id": 20,
        #             "author": "user1",
        #             "content": "非常详细的教程,感谢分享!",
        #             "created_at": "2024-01-15T16:45:00Z"
        #         }
        #     ],
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---

7.2 一对多关系

01.外键关系的序列化
    a.基本外键序列化
        外键关系是最常见的一对多关系,在序列化时需要考虑正向和反向两个方向的数据表示。
        ---
        from rest_framework import serializers
        from django.contrib.auth.models import User
        from .models import Post, Comment

        # 正向关系:从Comment访问Post
        class CommentSerializer(serializers.ModelSerializer):
            # 方式1:使用主键
            post_id = serializers.PrimaryKeyRelatedField(
                source='post',
                queryset=Post.objects.all()
            )

            # 方式2:使用嵌套序列化器
            post_detail = serializers.SerializerMethodField()

            class Meta:
                model = Comment
                fields = ['id', 'post_id', 'post_detail', 'author', 'content', 'created_at']

            def get_post_detail(self, obj):
                return {
                    'id': obj.post.id,
                    'title': obj.post.title,
                    'author': obj.post.author.username
                }

        # 输出示例:
        # {
        #     "id": 10,
        #     "post_id": 1,
        #     "post_detail": {
        #         "id": 1,
        #         "title": "Django REST Framework入门",
        #         "author": "admin"
        #     },
        #     "author": 5,
        #     "content": "很好的文章",
        #     "created_at": "2024-01-15T14:20:00Z"
        # }
        ---
    b.外键字段的验证
        在处理外键关系时,需要添加适当的验证逻辑,确保数据完整性和业务规则的正确性。
        ---
        from rest_framework import serializers
        from rest_framework.exceptions import ValidationError

        class CommentCreateSerializer(serializers.ModelSerializer):
            post = serializers.PrimaryKeyRelatedField(queryset=Post.objects.all())
            author = serializers.HiddenField(default=serializers.CurrentUserDefault())

            class Meta:
                model = Comment
                fields = ['id', 'post', 'author', 'content', 'created_at']
                read_only_fields = ['created_at']

            def validate_post(self, value):
                """验证文章是否允许评论"""
                # 检查文章是否存在
                if not value:
                    raise ValidationError("文章不存在")

                # 检查文章是否允许评论(假设Post有allow_comments字段)
                if hasattr(value, 'allow_comments') and not value.allow_comments:
                    raise ValidationError("该文章不允许评论")

                # 检查文章是否已归档(假设Post有is_archived字段)
                if hasattr(value, 'is_archived') and value.is_archived:
                    raise ValidationError("已归档的文章不能添加评论")

                return value

            def validate(self, attrs):
                """跨字段验证"""
                post = attrs.get('post')
                author = attrs.get('author')

                # 检查用户是否被禁止评论该文章
                # 假设有CommentBan模型记录禁止评论的用户
                from .models import CommentBan
                if CommentBan.objects.filter(post=post, user=author).exists():
                    raise ValidationError("您已被禁止在该文章下评论")

                # 检查评论频率限制(防止刷屏)
                from django.utils import timezone
                from datetime import timedelta
                recent_time = timezone.now() - timedelta(minutes=1)
                recent_comments = Comment.objects.filter(
                    author=author,
                    created_at__gte=recent_time
                ).count()
                if recent_comments >= 5:
                    raise ValidationError("评论过于频繁,请稍后再试")

                return attrs

            def create(self, validated_data):
                """创建评论并更新文章的评论计数"""
                comment = Comment.objects.create(**validated_data)

                # 更新文章的评论计数(假设Post有comments_count字段)
                post = comment.post
                if hasattr(post, 'comments_count'):
                    post.comments_count = post.comments.count()
                    post.save(update_fields=['comments_count'])

                return comment
        ---

02.反向关系的处理
    a.使用related_name访问反向关系
        Django的related_name允许从父对象访问所有子对象,在序列化时可以展示一对多关系的"多"的一侧。
        ---
        from rest_framework import serializers

        # 简单的评论序列化器
        class SimpleCommentSerializer(serializers.ModelSerializer):
            author_username = serializers.CharField(source='author.username', read_only=True)

            class Meta:
                model = Comment
                fields = ['id', 'author_username', 'content', 'created_at']

        # 在Post序列化器中包含所有评论
        class PostWithCommentsSerializer(serializers.ModelSerializer):
            # 使用related_name='comments'访问所有评论
            comments = SimpleCommentSerializer(many=True, read_only=True)
            # 评论数量
            comments_count = serializers.IntegerField(source='comments.count', read_only=True)
            # 作者信息
            author_username = serializers.CharField(source='author.username', read_only=True)

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'author_username',
                         'comments', 'comments_count', 'created_at']

        # 输出示例:
        # {
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author_username": "admin",
        #     "comments": [
        #         {
        #             "id": 10,
        #             "author_username": "user1",
        #             "content": "很好的文章",
        #             "created_at": "2024-01-15T14:20:00Z"
        #         },
        #         {
        #             "id": 11,
        #             "author_username": "user2",
        #             "content": "学习了",
        #             "created_at": "2024-01-15T15:30:00Z"
        #         }
        #     ],
        #     "comments_count": 2,
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---
    b.分页反向关系
        当反向关系包含大量数据时,应该使用分页来避免性能问题和数据传输过大。
        ---
        from rest_framework import serializers
        from rest_framework.pagination import PageNumberPagination

        class CommentPagination(PageNumberPagination):
            page_size = 10
            page_size_query_param = 'page_size'
            max_page_size = 50

        # 使用SerializerMethodField实现分页
        class PostWithPaginatedCommentsSerializer(serializers.ModelSerializer):
            comments = serializers.SerializerMethodField()
            comments_count = serializers.IntegerField(source='comments.count', read_only=True)

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'comments', 'comments_count', 'created_at']

            def get_comments(self, obj):
                """返回分页的评论列表"""
                # 从context获取request对象
                request = self.context.get('request')

                # 获取评论查询集
                comments = obj.comments.select_related('author').order_by('-created_at')

                # 如果有request,使用分页
                if request:
                    paginator = CommentPagination()
                    paginated_comments = paginator.paginate_queryset(comments, request)
                    serializer = SimpleCommentSerializer(paginated_comments, many=True)
                    return {
                        'results': serializer.data,
                        'count': comments.count(),
                        'next': paginator.get_next_link(),
                        'previous': paginator.get_previous_link()
                    }

                # 如果没有request,返回前10条
                serializer = SimpleCommentSerializer(comments[:10], many=True)
                return serializer.data

        # 在视图中使用
        from rest_framework import generics

        class PostDetailView(generics.RetrieveAPIView):
            queryset = Post.objects.all()
            serializer_class = PostWithPaginatedCommentsSerializer

            def get_serializer_context(self):
                """传递request到序列化器"""
                context = super().get_serializer_context()
                context['request'] = self.request
                return context

        # 输出示例(GET /api/posts/1/?page=1):
        # {
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "comments": {
        #         "results": [
        #             {
        #                 "id": 20,
        #                 "author_username": "user5",
        #                 "content": "最新评论",
        #                 "created_at": "2024-01-15T18:00:00Z"
        #             }
        #         ],
        #         "count": 25,
        #         "next": "http://api.example.com/api/posts/1/?page=2",
        #         "previous": null
        #     },
        #     "comments_count": 25,
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---

03.一对多关系的创建和更新
    a.创建时关联外键
        在创建子对象时,需要正确设置外键关联,可以通过URL参数、请求体或认证用户来确定父对象。
        ---
        from rest_framework import serializers, generics
        from rest_framework.permissions import IsAuthenticated

        class CommentCreateSerializer(serializers.ModelSerializer):
            # post不在请求体中,通过URL参数传入
            author = serializers.HiddenField(default=serializers.CurrentUserDefault())

            class Meta:
                model = Comment
                fields = ['id', 'content', 'author', 'created_at']
                read_only_fields = ['created_at']

        # 嵌套路由视图
        class PostCommentCreateView(generics.CreateAPIView):
            serializer_class = CommentCreateSerializer
            permission_classes = [IsAuthenticated]

            def perform_create(self, serializer):
                # 从URL参数获取post_id
                post_id = self.kwargs.get('post_id')
                try:
                    post = Post.objects.get(id=post_id)
                except Post.DoesNotExist:
                    from rest_framework.exceptions import NotFound
                    raise NotFound("文章不存在")

                # 保存评论并关联文章
                serializer.save(post=post)

        # urls.py配置
        # path('posts/<int:post_id>/comments/', PostCommentCreateView.as_view())

        # 请求示例(POST /api/posts/1/comments/):
        # {
        #     "content": "这是一条新评论"
        # }

        # 响应示例:
        # {
        #     "id": 30,
        #     "content": "这是一条新评论",
        #     "author": 5,
        #     "created_at": "2024-01-15T19:00:00Z"
        # }
        ---
    b.批量创建子对象
        在某些场景下需要一次性创建多个关联对象,可以通过自定义序列化器方法实现。
        ---
        from rest_framework import serializers
        from django.db import transaction

        class BulkCommentSerializer(serializers.Serializer):
            """批量创建评论的序列化器"""
            post_id = serializers.IntegerField()
            comments = serializers.ListField(
                child=serializers.CharField(max_length=500),
                min_length=1,
                max_length=10  # 限制一次最多创建10条评论
            )

            def validate_post_id(self, value):
                """验证文章是否存在"""
                try:
                    post = Post.objects.get(id=value)
                    return value
                except Post.DoesNotExist:
                    raise serializers.ValidationError("文章不存在")

            @transaction.atomic
            def create(self, validated_data):
                """批量创建评论"""
                post_id = validated_data['post_id']
                comment_contents = validated_data['comments']
                author = self.context['request'].user

                # 批量创建评论对象
                comments = []
                for content in comment_contents:
                    comment = Comment(
                        post_id=post_id,
                        author=author,
                        content=content
                    )
                    comments.append(comment)

                # 使用bulk_create批量插入
                created_comments = Comment.objects.bulk_create(comments)

                # 更新文章的评论计数
                post = Post.objects.get(id=post_id)
                if hasattr(post, 'comments_count'):
                    post.comments_count = post.comments.count()
                    post.save(update_fields=['comments_count'])

                return created_comments

            def to_representation(self, instance):
                """返回创建的评论列表"""
                return SimpleCommentSerializer(instance, many=True).data

        # 视图实现
        from rest_framework.views import APIView
        from rest_framework.response import Response
        from rest_framework import status

        class BulkCommentCreateView(APIView):
            permission_classes = [IsAuthenticated]

            def post(self, request):
                serializer = BulkCommentSerializer(
                    data=request.data,
                    context={'request': request}
                )
                serializer.is_valid(raise_exception=True)
                comments = serializer.save()
                return Response(
                    serializer.to_representation(comments),
                    status=status.HTTP_201_CREATED
                )

        # 请求示例(POST /api/comments/bulk/):
        # {
        #     "post_id": 1,
        #     "comments": [
        #         "第一条评论",
        #         "第二条评论",
        #         "第三条评论"
        #     ]
        # }

        # 响应示例:
        # [
        #     {
        #         "id": 31,
        #         "author_username": "admin",
        #         "content": "第一条评论",
        #         "created_at": "2024-01-15T19:10:00Z"
        #     },
        #     {
        #         "id": 32,
        #         "author_username": "admin",
        #         "content": "第二条评论",
        #         "created_at": "2024-01-15T19:10:00Z"
        #     },
        #     {
        #         "id": 33,
        #         "author_username": "admin",
        #         "content": "第三条评论",
        #         "created_at": "2024-01-15T19:10:00Z"
        #     }
        # ]
        ---

04.性能优化
    a.使用select_related优化外键查询
        select_related通过SQL JOIN在单次查询中获取关联对象,避免N+1查询问题,适用于一对一和外键关系。
        ---
        from rest_framework import generics
        from django.db.models import Prefetch

        # 未优化的视图(会产生N+1查询)
        class CommentListViewBad(generics.ListAPIView):
            queryset = Comment.objects.all()  # 每个comment访问post和author时都会查询
            serializer_class = CommentSerializer

        # 优化后的视图
        class CommentListViewGood(generics.ListAPIView):
            # 使用select_related预加载外键关联
            queryset = Comment.objects.select_related('post', 'author').all()
            serializer_class = CommentSerializer

        # 查询分析示例
        from django.db import connection
        from django.test.utils import override_settings

        @override_settings(DEBUG=True)
        def analyze_queries():
            # 未优化的查询
            print("未优化的查询:")
            connection.queries_log.clear()
            comments = Comment.objects.all()[:10]
            for comment in comments:
                # 每次访问post和author都会触发新查询
                print(f"{comment.author.username}: {comment.post.title}")
            print(f"查询次数: {len(connection.queries)}")  # 输出: 21 (1 + 10 + 10)

            # 优化后的查询
            print("\n优化后的查询:")
            connection.queries_log.clear()
            comments = Comment.objects.select_related('post', 'author').all()[:10]
            for comment in comments:
                print(f"{comment.author.username}: {comment.post.title}")
            print(f"查询次数: {len(connection.queries)}")  # 输出: 1

        # 在序列化器中使用
        class CommentDetailSerializer(serializers.ModelSerializer):
            post_title = serializers.CharField(source='post.title', read_only=True)
            post_author = serializers.CharField(source='post.author.username', read_only=True)
            author_username = serializers.CharField(source='author.username', read_only=True)
            author_email = serializers.EmailField(source='author.email', read_only=True)

            class Meta:
                model = Comment
                fields = ['id', 'post_title', 'post_author', 'author_username',
                         'author_email', 'content', 'created_at']

        # 视图中使用多层select_related
        class CommentDetailView(generics.RetrieveAPIView):
            # 使用双下划线跨越多层关系
            queryset = Comment.objects.select_related(
                'post',           # 预加载post
                'post__author',   # 预加载post的author
                'author'          # 预加载comment的author
            )
            serializer_class = CommentDetailSerializer

        # SQL查询示例(优化后只执行一次):
        # SELECT comment.*, post.*, post_author.*, author.*
        # FROM comment
        # INNER JOIN post ON comment.post_id = post.id
        # INNER JOIN auth_user AS post_author ON post.author_id = post_author.id
        # INNER JOIN auth_user AS author ON comment.author_id = author.id
        # WHERE comment.id = 1
        ---
    b.条件性加载关联数据
        根据请求参数动态决定是否加载关联数据,在列表视图中使用简化数据,在详情视图中使用完整数据。
        ---
        from rest_framework import serializers

        class DynamicFieldsModelSerializer(serializers.ModelSerializer):
            """
            支持动态字段的序列化器基类
            通过fields参数控制返回的字段
            """
            def __init__(self, *args, **kwargs):
                # 从context获取fields参数
                fields = self.context.get('fields')

                # 调用父类初始化
                super().__init__(*args, **kwargs)

                if fields:
                    # 只保留指定的字段
                    allowed = set(fields.split(','))
                    existing = set(self.fields.keys())
                    for field_name in existing - allowed:
                        self.fields.pop(field_name)

        class PostDynamicSerializer(DynamicFieldsModelSerializer):
            author = UserSerializer(read_only=True)
            category = CategorySerializer(read_only=True)
            comments = SimpleCommentSerializer(many=True, read_only=True)
            tags = TagSerializer(many=True, read_only=True)

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'author', 'category',
                         'comments', 'tags', 'created_at']

        # 视图实现
        class PostListView(generics.ListAPIView):
            serializer_class = PostDynamicSerializer

            def get_queryset(self):
                """根据请求参数动态优化查询"""
                queryset = Post.objects.all()

                # 获取fields参数
                fields = self.request.query_params.get('fields', '')

                # 根据需要的字段优化查询
                if 'author' in fields:
                    queryset = queryset.select_related('author')
                if 'category' in fields:
                    queryset = queryset.select_related('category')
                if 'comments' in fields:
                    queryset = queryset.prefetch_related('comments__author')
                if 'tags' in fields:
                    queryset = queryset.prefetch_related('tags')

                return queryset

            def get_serializer_context(self):
                """传递fields参数到序列化器"""
                context = super().get_serializer_context()
                context['fields'] = self.request.query_params.get('fields')
                return context

        # 请求示例1(只获取基本字段):
        # GET /api/posts/?fields=id,title,created_at
        # 响应:
        # [
        #     {
        #         "id": 1,
        #         "title": "Django REST Framework入门",
        #         "created_at": "2024-01-15T10:30:00Z"
        #     }
        # ]

        # 请求示例2(获取包含作者和分类的字段):
        # GET /api/posts/?fields=id,title,author,category
        # 响应:
        # [
        #     {
        #         "id": 1,
        #         "title": "Django REST Framework入门",
        #         "author": {
        #             "id": 5,
        #             "username": "admin",
        #             "email": "[email protected]"
        #         },
        #         "category": {
        #             "id": 2,
        #             "name": "Python开发",
        #             "slug": "python-dev"
        #         }
        #     }
        # ]
        ---

7.3 多对多关系

01.ManyToManyField的序列化
    a.基本多对多序列化
        多对多关系是Django和DRF中的重要特性,在序列化时需要考虑数据的表示方式和性能优化。
        ---
        from rest_framework import serializers
        from django.contrib.auth.models import User
        from .models import Post, Tag

        # Tag模型序列化器
        class TagSerializer(serializers.ModelSerializer):
            # 添加文章数量统计
            posts_count = serializers.SerializerMethodField()

            class Meta:
                model = Tag
                fields = ['id', 'name', 'posts_count']

            def get_posts_count(self, obj):
                return obj.posts.count()

        # 在Post中使用多对多关系的不同表示方式
        class PostSerializer(serializers.ModelSerializer):
            # 方式1:使用主键列表
            tag_ids = serializers.PrimaryKeyRelatedField(
                source='tags',
                many=True,
                queryset=Tag.objects.all(),
                write_only=True  # 只用于写入
            )

            # 方式2:使用嵌套序列化器
            tags = TagSerializer(many=True, read_only=True)

            # 方式3:使用字符串列表
            tag_names = serializers.SerializerMethodField()

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'tag_ids', 'tags', 'tag_names', 'created_at']

            def get_tag_names(self, obj):
                return [tag.name for tag in obj.tags.all()]

            def create(self, validated_data):
                """处理多对多关系的创建"""
                tags_data = validated_data.pop('tags', [])
                post = Post.objects.create(**validated_data)

                # 设置多对多关系
                post.tags.set(tags_data)
                return post

            def update(self, instance, validated_data):
                """处理多对多关系的更新"""
                tags_data = validated_data.pop('tags', None)

                # 更新其他字段
                for attr, value in validated_data.items():
                    setattr(instance, attr, value)
                instance.save()

                # 更新多对多关系
                if tags_data is not None:
                    instance.tags.set(tags_data)

                return instance

        # 请求示例(创建):
        # POST /api/posts/
        # {
        #     "title": "新的文章",
        #     "content": "文章内容...",
        #     "tag_ids": [1, 3, 5]
        # }

        # 响应示例:
        # {
        #     "id": 2,
        #     "title": "新的文章",
        #     "content": "文章内容...",
        #     "tag_ids": [1, 3, 5],
        #     "tags": [
        #         {"id": 1, "name": "Django", "posts_count": 15},
        #         {"id": 3, "name": "REST", "posts_count": 8},
        #         {"id": 5, "name": "API", "posts_count": 12}
        #     ],
        #     "tag_names": ["Django", "REST", "API"],
        #     "created_at": "2024-01-15T20:00:00Z"
        # }
        ---
    b SlugRelatedField处理多对多
        使用SlugRelatedField可以通过唯一标识符处理多对多关系,提供更友好的API接口。
        ---
        from rest_framework import serializers

        class PostWithSlugTagsSerializer(serializers.ModelSerializer):
            # 使用slug字段表示标签
            tag_slugs = serializers.SlugRelatedField(
                source='tags',
                many=True,
                slug_field='slug',  # 假设Tag模型有slug字段
                queryset=Tag.objects.all(),
                write_only=True
            )

            tags = TagSerializer(many=True, read_only=True)

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'tag_slugs', 'tags', 'created_at']

        # 扩展Tag模型以支持slug
        # models.py
        class Tag(models.Model):
            name = models.CharField(max_length=50, unique=True)
            slug = models.SlugField(max_length=50, unique=True)

            def __str__(self):
                return self.name

            def save(self, *args, **kwargs):
                # 自动生成slug
                if not self.slug:
                    from django.utils.text import slugify
                    self.slug = slugify(self.name)
                super().save(*args, **kwargs)

        # 请求示例:
        # POST /api/posts/
        # {
        #     "title": "新文章",
        #     "content": "内容...",
        #     "tag_slugs": ["django", "rest", "api"]
        # }

        # 响应示例:
        # {
        #     "id": 3,
        #     "title": "新文章",
        #     "content": "内容...",
        #     "tag_slugs": ["django", "rest", "api"],
        #     "tags": [
        #         {"id": 1, "name": "Django", "posts_count": 16},
        #         {"id": 2, "name": "REST", "posts_count": 9},
        #         {"id": 3, "name": "API", "posts_count": 13}
        #     ],
        #     "created_at": "2024-01-15T20:30:00Z"
        # }
        ---

02.中间表的处理
    a.自定义中间表模型
        当需要在多对多关系中存储额外信息时,需要创建自定义中间表模型,并在序列化器中处理复杂的关系。
        ---
        # models.py - 自定义中间表示例
        from django.db import models
        from django.contrib.auth.models import User

        class Student(models.Model):
            name = models.CharField(max_length=100)
            email = models.EmailField(unique=True)

            def __str__(self):
                return self.name

        class Course(models.Model):
            name = models.CharField(max_length=200)
            description = models.TextField()
            max_students = models.IntegerField(default=30)

            def __str__(self):
                return self.name

        class Enrollment(models.Model):
            """学生选课的中间表模型"""
            student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='enrollments')
            course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='enrollments')

            # 额外的中间表字段
            enrollment_date = models.DateTimeField(auto_now_add=True)
            grade = models.CharField(max_length=2, null=True, blank=True)  # A, B, C, F
            status = models.CharField(max_length=20, choices=[
                ('active', 'Active'),
                ('completed', 'Completed'),
                ('dropped', 'Dropped')
            ], default='active')
            attendance_rate = models.FloatField(default=0.0)  # 出勤率
            final_score = models.IntegerField(null=True, blank=True)  # 最终成绩

            class Meta:
                unique_together = ['student', 'course']
                ordering = ['-enrollment_date']

        # 序列化器设计
        class EnrollmentSerializer(serializers.ModelSerializer):
            student_name = serializers.CharField(source='student.name', read_only=True)
            course_name = serializers.CharField(source='course.name', read_only=True)

            class Meta:
                model = Enrollment
                fields = ['id', 'student', 'student_name', 'course', 'course_name',
                         'enrollment_date', 'grade', 'status', 'attendance_rate',
                         'final_score']

        class StudentWithEnrollmentsSerializer(serializers.ModelSerializer):
            """学生及其选课信息序列化器"""
            enrollments = serializers.SerializerMethodField()
            total_courses = serializers.IntegerField(source='enrollments.count', read_only=True)

            class Meta:
                model = Student
                fields = ['id', 'name', 'email', 'enrollments', 'total_courses']

            def get_enrollments(self, obj):
                enrollments = obj.enrollments.select_related('course').all()
                return EnrollmentSerializer(enrollments, many=True).data

        class CourseWithEnrollmentsSerializer(serializers.ModelSerializer):
            """课程及其学生信息序列化器"""
            enrollments = serializers.SerializerMethodField()
            enrolled_students = serializers.IntegerField(source='enrollments.count', read_only=True)

            class Meta:
                model = Course
                fields = ['id', 'name', 'description', 'max_students', 'enrollments',
                         'enrolled_students']

            def get_enrollments(self, obj):
                enrollments = obj.enrollments.select_related('student').all()
                return EnrollmentSerializer(enrollments, many=True).data

        # 使用示例
        # GET /api/students/1/
        # 响应示例:
        # {
        #     "id": 1,
        #     "name": "张三",
        #     "email": "[email protected]",
        #     "enrollments": [
        #         {
        #             "id": 1,
        #             "student": 1,
        #             "student_name": "张三",
        #             "course": 1,
        #             "course_name": "Python编程",
        #             "enrollment_date": "2024-01-10T09:00:00Z",
        #             "grade": "A",
        #             "status": "active",
        #             "attendance_rate": 95.5,
        #             "final_score": 92
        #         },
        #         {
        #             "id": 2,
        #             "student": 1,
        #             "student_name": "张三",
        #             "course": 2,
        #             "course_name": "数据结构",
        #             "enrollment_date": "2024-01-12T14:00:00Z",
        #             "grade": null,
        #             "status": "active",
        #             "attendance_rate": 88.0,
        #             "final_score": null
        #         }
        #     ],
        #     "total_courses": 2
        # }
        ---
    b.创建和管理中间表数据
        需要特殊的序列化器来处理中间表的创建和更新操作,包括关联关系的建立和额外字段的设置。
        ---
        from rest_framework import serializers
        from django.db import transaction

        class EnrollmentCreateSerializer(serializers.ModelSerializer):
            """创建选课记录的序列化器"""

            class Meta:
                model = Enrollment
                fields = ['student', 'course', 'status', 'attendance_rate', 'final_score']

            def validate(self, attrs):
                """验证选课的逻辑"""
                student = attrs['student']
                course = attrs['course']

                # 检查是否已经选过这门课
                if Enrollment.objects.filter(student=student, course=course).exists():
                    raise serializers.ValidationError("该学生已经选过这门课程")

                # 检查课程是否还有名额
                current_enrollments = course.enrollments.filter(status='active').count()
                if current_enrollments >= course.max_students:
                    raise serializers.ValidationError("该课程已满员")

                # 检查学生是否已经选了太多门课(假设最多选5门)
                active_courses = student.enrollments.filter(status='active').count()
                if active_courses >= 5:
                    raise serializers.ValidationError("该学生选课数量已达上限")

                return attrs

        class EnrollmentUpdateSerializer(serializers.ModelSerializer):
            """更新选课记录的序列化器"""

            class Meta:
                model = Enrollment
                fields = ['status', 'grade', 'attendance_rate', 'final_score']

            def validate_grade(self, value):
                """验证成绩格式"""
                valid_grades = ['A', 'B', 'C', 'D', 'F']
                if value and value not in valid_grades:
                    raise serializers.ValidationError(f"成绩必须是以下之一: {', '.join(valid_grades)}")
                return value

            def validate_final_score(self, value):
                """验证最终成绩"""
                if value and (value < 0 or value > 100):
                    raise serializers.ValidationError("最终成绩必须在0-100之间")
                return value

        class BatchEnrollmentSerializer(serializers.Serializer):
            """批量选课序列化器"""
            student_id = serializers.IntegerField()
            course_ids = serializers.ListField(
                child=serializers.IntegerField(),
                min_length=1,
                max_length=3  # 一次最多选3门课
            )

            def validate_student_id(self, value):
                """验证学生是否存在"""
                try:
                    Student.objects.get(id=value)
                    return value
                except Student.DoesNotExist:
                    raise serializers.ValidationError("学生不存在")

            def validate_course_ids(self, value):
                """验证课程是否存在"""
                if not Course.objects.filter(id__in=value).count() == len(value):
                    raise serializers.ValidationError("部分课程不存在")
                return value

            @transaction.atomic
            def create(self, validated_data):
                """批量创建选课记录"""
                student_id = validated_data['student_id']
                course_ids = validated_data['course_ids']
                student = Student.objects.get(id=student_id)

                enrollments = []
                errors = []

                for course_id in course_ids:
                    try:
                        # 检查是否已选
                        if Enrollment.objects.filter(student=student, course_id=course_id).exists():
                            errors.append(f"课程 {course_id} 已选过")
                            continue

                        # 检查名额
                        course = Course.objects.get(id=course_id)
                        current_count = course.enrollments.filter(status='active').count()
                        if current_count >= course.max_students:
                            errors.append(f"课程 {course.name} 已满员")
                            continue

                        # 创建选课记录
                        enrollment = Enrollment.objects.create(
                            student=student,
                            course=course
                        )
                        enrollments.append(enrollment)

                    except Exception as e:
                        errors.append(f"课程 {course_id} 选课失败: {str(e)}")

                if errors and not enrollments:
                    raise serializers.ValidationError({"error": errors})

                return {
                    'success_count': len(enrollments),
                    'error_count': len(errors),
                    'errors': errors,
                    'enrollments': enrollments
                }

        # 视图实现
        from rest_framework import generics, status
        from rest_framework.response import Response
        from rest_framework.decorators import api_view

        class EnrollmentCreateView(generics.CreateAPIView):
            serializer_class = EnrollmentCreateSerializer

            def perform_create(self, serializer):
                serializer.save()

        @api_view(['POST'])
        def batch_enroll(request):
            """批量选课接口"""
            serializer = BatchEnrollmentSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            result = serializer.save()

            response_data = {
                'message': f"成功选课 {result['success_count']} 门",
                'errors': result['errors'] if result['errors'] else None
            }

            status_code = status.HTTP_201_CREATED if result['success_count'] > 0 else status.HTTP_400_BAD_REQUEST

            return Response(response_data, status=status_code)

        # 批量选课请求示例:
        # POST /api/enrollments/batch/
        # {
        #     "student_id": 1,
        #     "course_ids": [1, 2, 3]
        # }

        # 响应示例:
        # {
        #     "message": "成功选课 2 门",
        #     "errors": ["课程 3 已满员"]
        # }
        ---

03.多对多关系的增删改查
    a.添加和移除多对多关系
        多对多关系的更新需要特别注意,不能简单地使用set()方法,需要根据具体需求选择合适的更新策略。
        ---
        from rest_framework import serializers
        from django.db import transaction

        class PostUpdateTagsSerializer(serializers.ModelSerializer):
            """更新文章标签的专用序列化器"""

            class Meta:
                model = Post
                fields = ['id', 'title']  # 标题用于识别文章

            # 自定义标签操作字段
            add_tags = serializers.ListField(
                child=serializers.CharField(max_length=50),
                write_only=True,
                required=False
            )
            remove_tags = serializers.ListField(
                child=serializers.CharField(max_length=50),
                write_only=True,
                required=False
            )
            replace_tags = serializers.ListField(
                child=serializers.CharField(max_length=50),
                write_only=True,
                required=False
            )

            def validate_add_tags(self, value):
                """验证要添加的标签"""
                if value:
                    # 限制一次添加的标签数量
                    if len(value) > 10:
                        raise serializers.ValidationError("一次最多添加10个标签")
                return value

            def validate_remove_tags(self, value):
                """验证要移除的标签"""
                if value:
                    # 限制一次移除的标签数量
                    if len(value) > 20:
                        raise serializers.ValidationError("一次最多移除20个标签")
                return value

            @transaction.atomic
            def update(self, instance, validated_data):
                """处理标签的各种操作"""

                # 获取操作参数
                add_tags = validated_data.pop('add_tags', None)
                remove_tags = validated_data.pop('remove_tags', None)
                replace_tags = validated_data.pop('replace_tags', None)

                # 更新其他字段
                for attr, value in validated_data.items():
                    setattr(instance, attr, value)

                # 执行标签操作
                if replace_tags is not None:
                    # 完全替换所有标签
                    instance.tags.clear()
                    for tag_name in replace_tags:
                        tag, _ = Tag.objects.get_or_create(name=tag_name)
                        instance.tags.add(tag)

                else:
                    # 处理添加标签
                    if add_tags:
                        for tag_name in add_tags:
                            tag, _ = Tag.objects.get_or_create(name=tag_name)
                            instance.tags.add(tag)

                    # 处理移除标签
                    if remove_tags:
                        for tag_name in remove_tags:
                            try:
                                tag = Tag.objects.get(name=tag_name)
                                instance.tags.remove(tag)
                            except Tag.DoesNotExist:
                                # 标签不存在,忽略
                                pass

                instance.save()
                return instance

        # 视图实现
        from rest_framework import generics, status
        from rest_framework.response import Response

        class PostUpdateTagsView(generics.UpdateAPIView):
            queryset = Post.objects.all()
            serializer_class = PostUpdateTagsSerializer

            def update(self, request, *args, **kwargs):
                partial = kwargs.pop('partial', False)
                instance = self.get_object()
                serializer = self.get_serializer(instance, data=request.data, partial=partial)
                serializer.is_valid(raise_exception=True)

                # 执行更新
                self.perform_update(serializer)

                # 返回更新后的标签列表
                tags_serializer = TagSerializer(instance.tags.all(), many=True)

                return Response({
                    'message': '标签更新成功',
                    'tags': tags_serializer.data,
                    'tags_count': instance.tags.count()
                })

        # API使用示例:

        # 1. 添加标签
        # PATCH /api/posts/1/update-tags/
        # {
        #     "add_tags": ["Python", "Web开发"]
        # }
        # 响应:
        # {
        #     "message": "标签更新成功",
        #     "tags": [
        #         {"id": 1, "name": "Django", "posts_count": 15},
        #         {"id": 4, "name": "Python", "posts_count": 20},
        #         {"id": 5, "name": "Web开发", "posts_count": 8}
        #     ],
        #     "tags_count": 3
        # }

        # 2. 移除标签
        # PATCH /api/posts/1/update-tags/
        # {
        #     "remove_tags": ["Python"]
        # }

        # 3. 替换所有标签
        # PATCH /api/posts/1/update-tags/
        # {
        #     "replace_tags": ["Django", "REST", "API"]
        # }
        ---
    b.复杂多对多查询
        多对多关系经常需要进行复杂的查询操作,包括过滤、统计、排序等,需要掌握高效的查询技巧。
        ---
        from rest_framework import serializers, generics
        from rest_framework.response import Response
        from django.db.models import Count, Q, F
        from django.db.models.functions import Length

        class TagStatisticsSerializer(serializers.ModelSerializer):
            """标签统计序列化器"""
            posts_count = serializers.SerializerMethodField()
            average_post_views = serializers.SerializerMethodField()
            recent_posts = serializers.SerializerMethodField()
            trending_score = serializers.SerializerMethodField()

            class Meta:
                model = Tag
                fields = ['id', 'name', 'posts_count', 'average_post_views',
                         'recent_posts', 'trending_score']

            def get_posts_count(self, obj):
                """获取文章数量"""
                return obj.posts.count()

            def get_average_post_views(self, obj):
                """获取平均浏览量(假设Post有views字段)"""
                from django.db.models import Avg
                avg_views = obj.posts.aggregate(avg=Avg('views'))['avg']
                return round(avg_views or 0, 2)

            def get_recent_posts(self, obj):
                """获取最近的文章"""
                from datetime import timedelta
                from django.utils import timezone

                recent_time = timezone.now() - timedelta(days=7)
                recent_posts = obj.posts.filter(
                    created_at__gte=recent_time
                ).order_by('-created_at')[:3]

                return [{
                    'id': post.id,
                    'title': post.title,
                    'created_at': post.created_at
                } for post in recent_posts]

            def get_trending_score(self, obj):
                """计算趋势分数(基于最近的文章数量和浏览量)"""
                from datetime import timedelta
                from django.utils import timezone
                from django.db.models import Sum

                recent_time = timezone.now() - timedelta(days=7)
                recent_data = obj.posts.filter(
                    created_at__gte=recent_time
                ).aggregate(
                    post_count=Count('id'),
                    total_views=Sum('views')
                )

                # 趋势分数 = 最近文章数 * 10 + 最近总浏览量 / 100
                score = (recent_data['post_count'] or 0) * 10
                score += (recent_data['total_views'] or 0) / 100

                return round(score, 2)

        # 复杂查询视图
        class TagListView(generics.ListAPIView):
            serializer_class = TagStatisticsSerializer

            def get_queryset(self):
                """获取标签列表,支持多种过滤和排序"""
                queryset = Tag.objects.annotate(
                    posts_count=Count('posts')
                ).filter(
                    posts_count__gt=0  # 只返回有文章的标签
                )

                # 过滤参数
                min_posts = self.request.query_params.get('min_posts')
                max_posts = self.request.query_params.get('max_posts')
                trending_only = self.request.query_params.get('trending_only')

                if min_posts:
                    queryset = queryset.filter(posts_count__gte=min_posts)
                if max_posts:
                    queryset = queryset.filter(posts_count__lte=max_posts)
                if trending_only == 'true':
                    # 只返回趋势标签(最近7天有新文章)
                    from datetime import timedelta
                    from django.utils import timezone
                    recent_time = timezone.now() - timedelta(days=7)
                    queryset = queryset.filter(
                        posts__created_at__gte=recent_time
                    ).distinct()

                # 排序参数
                sort_by = self.request.query_params.get('sort_by', 'name')
                sort_order = self.request.query_params.get('sort_order', 'asc')

                sort_prefix = '-' if sort_order == 'desc' else ''

                if sort_by == 'posts_count':
                    queryset = queryset.order_by(f'{sort_prefix}posts_count')
                elif sort_by == 'name':
                    queryset = queryset.order_by(f'{sort_prefix}name')
                elif sort_by == 'trending':
                    # 按趋势分数排序
                    queryset = queryset.annotate(
                        recent_posts=Count('posts', filter=Q(
                            posts__created_at__gte=timezone.now() - timedelta(days=7)
                        ))
                    ).order_by(f'{sort_prefix}recent_posts')

                return queryset

        # 自定义查询接口
        @api_view(['GET'])
        def tag_analysis(request):
            """标签分析接口"""

            # 1. 热门标签(文章数量最多)
            popular_tags = Tag.objects.annotate(
                posts_count=Count('posts')
            ).order_by('-posts_count')[:10]

            # 2. 新兴标签(最近创建的标签)
            from datetime import timedelta
            from django.utils import timezone

            recent_time = timezone.now() - timedelta(days=30)
            emerging_tags = Tag.objects.filter(
                created_at__gte=recent_time
            ).annotate(
                posts_count=Count('posts')
            ).order_by('-posts_count')[:10]

            # 3. 活跃标签(最近有文章更新的标签)
            active_tags = Tag.objects.filter(
                posts__created_at__gte=recent_time
            ).annotate(
                recent_posts=Count('posts', filter=Q(
                    posts__created_at__gte=recent_time
                ))
            ).order_by('-recent_posts')[:10]

            # 4. 标签使用率分析
            total_posts = Post.objects.count()
            tag_usage_stats = Tag.objects.annotate(
                posts_count=Count('posts'),
                usage_rate=Count('posts') * 100.0 / total_posts
            ).filter(
                usage_rate__gte=1.0  # 使用率超过1%
            ).order_by('-usage_rate')[:10]

            data = {
                'popular_tags': TagSerializer(popular_tags, many=True).data,
                'emerging_tags': TagSerializer(emerging_tags, many=True).data,
                'active_tags': TagSerializer(active_tags, many=True).data,
                'usage_stats': [{
                    'name': tag.name,
                    'posts_count': tag.posts_count,
                    'usage_rate': round(tag.usage_rate, 2)
                } for tag in tag_usage_stats],
                'total_tags': Tag.objects.count(),
                'total_posts': total_posts,
                'average_tags_per_post': Post.objects.annotate(
                    tag_count=Count('tags')
                ).aggregate(avg=Avg('tag_count'))['avg'] or 0
            }

            return Response(data)

        # API使用示例:

        # GET /api/tags/
        # 基础标签列表

        # GET /api/tags/?min_posts=5&sort_by=posts_count&sort_order=desc
        # 获取至少5篇文章的标签,按文章数量降序排列

        # GET /api/tags/?trending_only=true&sort_by=trending
        # 获取趋势标签,按热度排序

        # GET /api/tags/analysis/
        # 获取完整的标签分析报告
        ---

04.through模型的使用
    a.定义和使用through模型
        通过自定义through模型,可以在多对多关系中存储额外的元数据,实现更复杂的业务逻辑。
        ---
        # models.py - 复杂的多对多关系示例
        from django.db import models
        from django.contrib.auth.models import User

        class Book(models.Model):
            title = models.CharField(max_length=200)
            author = models.CharField(max_length=100)
            isbn = models.CharField(max_length=13, unique=True)
            publication_date = models.DateField()
            page_count = models.IntegerField()
            description = models.TextField()

            def __str__(self):
                return self.title

        class Reader(models.Model):
            user = models.OneToOneField(User, on_delete=models.CASCADE)
            reading_preference = models.CharField(max_length=100)
            max_books_per_month = models.IntegerField(default=5)

            def __str__(self):
                return f"{self.user.username} (Reader)"

        class ReadingSession(models.Model):
            """阅读会话 - Book和Reader的through模型"""
            book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='reading_sessions')
            reader = models.ForeignKey(Reader, on_delete=models.CASCADE, related_name='reading_sessions')

            # 额外的会话数据
            start_date = models.DateTimeField(auto_now_add=True)
            end_date = models.DateTimeField(null=True, blank=True)
            pages_read = models.IntegerField(default=0)
            reading_time_minutes = models.IntegerField(default=0)
            status = models.CharField(max_length=20, choices=[
                ('reading', 'Reading'),
                ('completed', 'Completed'),
                ('paused', 'Paused'),
                ('abandoned', 'Abandoned')
            ], default='reading')

            # 评分和笔记
            rating = models.IntegerField(null=True, blank=True, choices=[(i, i) for i in range(1, 6)])
            notes = models.TextField(blank=True)

            # 阅读进度跟踪
            last_page_read = models.IntegerField(default=0)
            reading_speed_pages_per_hour = models.FloatField(default=0.0)

            class Meta:
                unique_together = ['book', 'reader']
                ordering = ['-start_date']

        # 序列化器设计
        class BookSerializer(serializers.ModelSerializer):
            class Meta:
                model = Book
                fields = ['id', 'title', 'author', 'isbn', 'publication_date',
                         'page_count', 'description']

        class ReaderSerializer(serializers.ModelSerializer):
            username = serializers.CharField(source='user.username', read_only=True)
            email = serializers.CharField(source='user.email', read_only=True)

            class Meta:
                model = Reader
                fields = ['id', 'username', 'email', 'reading_preference', 'max_books_per_month']

        class ReadingSessionSerializer(serializers.ModelSerializer):
            book_info = BookSerializer(source='book', read_only=True)
            reader_info = ReaderSerializer(source='reader', read_only=True)
            progress_percentage = serializers.SerializerMethodField()
            estimated_completion_date = serializers.SerializerMethodField()

            class Meta:
                model = ReadingSession
                fields = ['id', 'book', 'reader', 'book_info', 'reader_info',
                         'start_date', 'end_date', 'pages_read', 'reading_time_minutes',
                         'status', 'rating', 'notes', 'last_page_read',
                         'reading_speed_pages_per_hour', 'progress_percentage',
                         'estimated_completion_date']

            def get_progress_percentage(self, obj):
                """计算阅读进度百分比"""
                if obj.book.page_count > 0:
                    return round((obj.last_page_read / obj.book.page_count) * 100, 2)
                return 0

            def get_estimated_completion_date(self, obj):
                """估算完成日期"""
                if obj.status == 'completed' or obj.reading_speed_pages_per_hour <= 0:
                    return None

                remaining_pages = obj.book.page_count - obj.last_page_read
                if remaining_pages <= 0:
                    return None

                # 假设每天阅读2小时
                daily_reading_pages = obj.reading_speed_pages_per_hour * 2
                days_needed = remaining_pages / daily_reading_pages

                from datetime import timedelta
                completion_date = obj.start_date + timedelta(days=days_needed)
                return completion_date.date()

        class ReaderWithReadingSessionsSerializer(serializers.ModelSerializer):
            """包含阅读会话的读者序列化器"""
            username = serializers.CharField(source='user.username', read_only=True)
            current_readings = serializers.SerializerMethodField()
            completed_books = serializers.SerializerMethodField()
            reading_stats = serializers.SerializerMethodField()

            class Meta:
                model = Reader
                fields = ['id', 'username', 'reading_preference', 'max_books_per_month',
                         'current_readings', 'completed_books', 'reading_stats']

            def get_current_readings(self, obj):
                """获取当前正在阅读的书籍"""
                sessions = obj.reading_sessions.filter(status='reading').select_related('book')
                return ReadingSessionSerializer(sessions, many=True).data

            def get_completed_books(self, obj):
                """获取已完成阅读的书籍"""
                sessions = obj.reading_sessions.filter(status='completed').select_related('book')
                return ReadingSessionSerializer(sessions, many=True).data

            def get_reading_stats(self, obj):
                """获取阅读统计信息"""
                sessions = obj.reading_sessions.all()

                total_books = sessions.filter(status='completed').count()
                total_pages = sessions.aggregate(
                    total=Sum('pages_read')
                )['total'] or 0

                total_reading_time = sessions.aggregate(
                    total=Sum('reading_time_minutes')
                )['total'] or 0

                # 平均评分
                avg_rating = sessions.filter(
                    rating__isnull=False
                ).aggregate(avg=Avg('rating'))['avg']

                # 本月阅读情况
                from datetime import timedelta
                from django.utils import timezone
                month_ago = timezone.now() - timedelta(days=30)

                monthly_books = sessions.filter(
                    end_date__gte=month_ago,
                    status='completed'
                ).count()

                return {
                    'total_books_completed': total_books,
                    'total_pages_read': total_pages,
                    'total_reading_time_hours': round(total_reading_time / 60, 1),
                    'average_rating': round(avg_rating or 0, 2),
                    'books_this_month': monthly_books,
                    'average_speed_pages_per_hour': round(
                        sessions.filter(reading_speed_pages_per_hour__gt=0).aggregate(
                            avg=Avg('reading_speed_pages_per_hour')
                        )['avg'] or 0, 1
                    )
                }

        # 使用示例
        # GET /api/readers/1/
        # 响应示例:
        # {
        #     "id": 1,
        #     "username": "bookworm",
        #     "reading_preference": "Science Fiction",
        #     "max_books_per_month": 5,
        #     "current_readings": [
        #         {
        #             "id": 1,
        #             "book_info": {
        #                 "id": 1,
        #                 "title": "三体",
        #                 "author": "刘慈欣",
        #                 "isbn": "9787536692930",
        #                 "page_count": 302
        #             },
        #             "status": "reading",
        #             "last_page_read": 150,
        #             "progress_percentage": 49.67,
        #             "estimated_completion_date": "2024-02-15"
        #         }
        #     ],
        #     "completed_books": [
        #         {
        #             "id": 2,
        #             "book_info": {
        #                 "id": 2,
        #                 "title": "流浪地球",
        #                 "author": "刘慈欣",
        #                 "page_count": 200
        #             },
        #             "status": "completed",
        #             "rating": 5,
        #             "progress_percentage": 100.0
        #         }
        #     ],
        #     "reading_stats": {
        #         "total_books_completed": 15,
        #         "total_pages_read": 4500,
        #         "total_reading_time_hours": 120.5,
        #         "average_rating": 4.3,
        #         "books_this_month": 2,
        #         "average_speed_pages_per_hour": 37.3
        #     }
        # }
        ---
    b.创建和管理through实例
        创建和管理through模型实例需要特殊的处理方式,通常需要直接操作中间表模型而不是使用简单的add()操作。
        ---
        from rest_framework import serializers
        from django.db import transaction
        from django.utils import timezone

        class ReadingSessionCreateSerializer(serializers.ModelSerializer):
            """创建阅读会话的序列化器"""

            class Meta:
                model = ReadingSession
                fields = ['book', 'reader', 'reading_time_minutes', 'notes']

            def validate(self, attrs):
                """验证创建阅读会话的逻辑"""
                reader = attrs['reader']
                book = attrs['book']

                # 检查是否已经存在活跃的阅读会话
                if ReadingSession.objects.filter(
                    reader=reader,
                    book=book,
                    status='reading'
                ).exists():
                    raise serializers.ValidationError(
                        "该读者已经开始阅读这本书,不能重复创建会话"
                    )

                # 检查读者是否超过本月阅读限制
                current_month = timezone.now().date().replace(day=1)
                current_readings = ReadingSession.objects.filter(
                    reader=reader,
                    start_date__gte=current_month,
                    status='reading'
                ).count()

                if current_readings >= reader.max_books_per_month:
                    raise serializers.ValidationError(
                        f"已达到本月阅读限制({reader.max_books_per_month}本)"
                    )

                return attrs

        class ReadingSessionUpdateSerializer(serializers.ModelSerializer):
            """更新阅读会话的序列化器"""

            class Meta:
                model = ReadingSession
                fields = ['pages_read', 'reading_time_minutes', 'last_page_read',
                         'status', 'rating', 'notes', 'reading_speed_pages_per_hour']

            def validate_rating(self, value):
                """验证评分范围"""
                if value and (value < 1 or value > 5):
                    raise serializers.ValidationError("评分必须在1-5之间")
                return value

            def validate_last_page_read(self, value):
                """验证最后阅读页数"""
                request = self.context.get('request')
                if request and hasattr(request, 'current_session'):
                    book = request.current_session.book
                    if value > book.page_count:
                        raise serializers.ValidationError(
                            f"最后阅读页数不能超过书本总页数({book.page_count})"
                        )
                return value

            def validate(self, attrs):
                """验证更新逻辑"""
                status = attrs.get('status')
                rating = attrs.get('rating')

                # 只有完成的阅读会话才能评分
                if rating and status != 'completed':
                    raise serializers.ValidationError("只有完成的阅读会话才能评分")

                return attrs

        class BulkReadingSessionSerializer(serializers.Serializer):
            """批量创建阅读会话的序列化器"""
            reader_id = serializers.IntegerField()
            book_ids = serializers.ListField(
                child=serializers.IntegerField(),
                min_length=1,
                max_length=3  # 一次最多添加3本书
            )

            def validate_reader_id(self, value):
                """验证读者是否存在"""
                try:
                    Reader.objects.get(id=value)
                    return value
                except Reader.DoesNotExist:
                    raise serializers.ValidationError("读者不存在")

            def validate_book_ids(self, value):
                """验证书籍是否存在"""
                existing_count = Book.objects.filter(id__in=value).count()
                if existing_count != len(value):
                    raise serializers.ValidationError("部分书籍不存在")
                return value

            @transaction.atomic
            def create(self, validated_data):
                """批量创建阅读会话"""
                reader_id = validated_data['reader_id']
                book_ids = validated_data['book_ids']
                reader = Reader.objects.get(id=reader_id)

                created_sessions = []
                errors = []

                for book_id in book_ids:
                    try:
                        # 检查是否已存在
                        if ReadingSession.objects.filter(
                            reader=reader, book_id=book_id
                        ).exists():
                            errors.append(f"书籍 {book_id} 已在阅读列表中")
                            continue

                        # 创建会话
                        session = ReadingSession.objects.create(
                            reader=reader,
                            book_id=book_id
                        )
                        created_sessions.append(session)

                    except Exception as e:
                        errors.append(f"书籍 {book_id} 添加失败: {str(e)}")

                if errors and not created_sessions:
                    raise serializers.ValidationError({"errors": errors})

                return {
                    'success_count': len(created_sessions),
                    'error_count': len(errors),
                    'errors': errors,
                    'sessions': created_sessions
                }

        class ReadingProgressSerializer(serializers.Serializer):
            """阅读进度更新序列化器"""
            session_id = serializers.IntegerField()
            pages_read = serializers.IntegerField(min_value=1)
            reading_time_minutes = serializers.IntegerField(min_value=1)
            notes = serializers.CharField(required=False, allow_blank=True)

            @transaction.atomic
            def save(self):
                """更新阅读进度"""
                session_id = self.validated_data['session_id']
                pages_read = self.validated_data['pages_read']
                reading_time_minutes = self.validated_data['reading_time_minutes']
                notes = self.validated_data.get('notes', '')

                try:
                    session = ReadingSession.objects.get(id=session_id)

                    # 更新进度
                    session.pages_read += pages_read
                    session.reading_time_minutes += reading_time_minutes
                    session.last_page_read += pages_read

                    # 计算阅读速度(页/小时)
                    if reading_time_minutes > 0:
                        session.reading_speed_pages_per_hour = (
                            pages_read / reading_time_minutes
                        ) * 60

                    # 添加笔记
                    if notes:
                        if session.notes:
                            session.notes += f"\n\n[{timezone.now().strftime('%Y-%m-%d %H:%M')}]: {notes}"
                        else:
                            session.notes = f"[{timezone.now().strftime('%Y-%m-%d %H:%M')}]: {notes}"

                    # 检查是否完成阅读
                    if session.last_page_read >= session.book.page_count:
                        session.status = 'completed'
                        session.end_date = timezone.now()
                        session.last_page_read = session.book.page_count

                    session.save()
                    return session

                except ReadingSession.DoesNotExist:
                    raise serializers.ValidationError("阅读会话不存在")

        # 视图实现
        from rest_framework import generics, status, views
        from rest_framework.response import Response

        class ReadingProgressView(views.APIView):
            """更新阅读进度的接口"""

            def post(self, request):
                serializer = ReadingProgressSerializer(data=request.data)
                serializer.is_valid(raise_exception=True)

                session = serializer.save()

                response_serializer = ReadingSessionSerializer(session)
                return Response({
                    'message': '阅读进度更新成功',
                    'session': response_serializer.data
                })

        # API使用示例:

        # 1. 创建阅读会话
        # POST /api/reading-sessions/
        # {
        #     "book": 1,
        #     "reader": 1,
        #     "reading_time_minutes": 30,
        #     "notes": "开始阅读第一章"
        # }

        # 2. 更新阅读进度
        # POST /api/reading-progress/
        # {
        #     "session_id": 1,
        #     "pages_read": 20,
        #     "reading_time_minutes": 45,
        #     "notes": "第一章很有趣"
        # }

        # 3. 批量添加阅读会话
        # POST /api/reading-sessions/bulk/
        # {
        #     "reader_id": 1,
        #     "book_ids": [2, 3, 4]
        # }

        # 4. 完成阅读会话
        # PATCH /api/reading-sessions/1/
        # {
        #     "status": "completed",
        #     "rating": 5
        # }
        ---

7.4 嵌套写入操作

01.嵌套对象的创建
    a.基础嵌套创建
        嵌套写入允许在一个请求中创建主对象及其关联对象,但需要正确配置序列化器和处理验证逻辑。
        ---
        from rest_framework import serializers
        from django.db import transaction

        class CommentNestedSerializer(serializers.ModelSerializer):
            """嵌套评论序列化器"""
            author_username = serializers.CharField(source='author.username', read_only=True)

            class Meta:
                model = Comment
                fields = ['content', 'author_username']
                read_only_fields = ['author']

        class PostWithNestedCommentsSerializer(serializers.ModelSerializer):
            """支持嵌套创建评论的文章序列化器"""
            comments = CommentNestedSerializer(many=True)
            author = serializers.HiddenField(default=serializers.CurrentUserDefault())

            class Meta:
                model = Post
                fields = ['title', 'content', 'author', 'comments']

            @transaction.atomic
            def create(self, validated_data):
                """创建文章和嵌套的评论"""
                # 提取嵌套的评论数据
                comments_data = validated_data.pop('comments', [])

                # 创建文章
                post = Post.objects.create(**validated_data)

                # 创建评论
                for comment_data in comments_data:
                    Comment.objects.create(post=post, **comment_data)

                return post

            def to_representation(self, instance):
                """自定义返回格式"""
                data = super().to_representation(instance)
                # 返回完整的文章和评论信息
                data['comments'] = CommentNestedSerializer(
                    instance.comments.all(),
                    many=True
                ).data
                return data

        # 视图实现
        from rest_framework import generics

        class PostWithCommentsCreateView(generics.CreateAPIView):
            serializer_class = PostWithNestedCommentsSerializer

            def perform_create(self, serializer):
                # author已经通过HiddenField设置
                serializer.save()

        # 请求示例:
        # POST /api/posts/with-comments/
        # {
        #     "title": "嵌套文章",
        #     "content": "这是一篇包含嵌套评论的文章...",
        #     "comments": [
        #         {
        #             "content": "第一条评论"
        #         },
        #         {
        #             "content": "第二条评论"
        #         }
        #     ]
        # }

        # 响应示例:
        # {
        #     "title": "嵌套文章",
        #     "content": "这是一篇包含嵌套评论的文章...",
        #     "author": 5,
        #     "comments": [
        #         {
        #             "content": "第一条评论",
        #             "author_username": "admin"
        #         },
        #         {
        #             "content": "第二条评论",
        #             "author_username": "admin"
        #         }
        #     ]
        # }
        ---
    b.多层嵌套创建
        处理更深层次的嵌套关系,如文章 -> 评论 -> 回复的创建,需要递归处理和复杂的验证逻辑。
        ---
        # models.py - 多层嵌套模型
        class CommentReply(models.Model):
            """评论回复模型"""
            comment = models.ForeignKey(Comment, on_delete=models.CASCADE, related_name='replies')
            author = models.ForeignKey(User, on_delete=models.CASCADE)
            content = models.TextField()
            created_at = models.DateTimeField(auto_now_add=True)
            parent_reply = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='child_replies')

            class Meta:
                ordering = ['created_at']

        # 序列化器
        class ReplyNestedSerializer(serializers.ModelSerializer):
            author_username = serializers.CharField(source='author.username', read_only=True)
            replies = serializers.SerializerMethodField()  # 支持无限嵌套回复

            class Meta:
                model = CommentReply
                fields = ['id', 'content', 'author_username', 'created_at', 'replies']
                read_only_fields = ['author']

            def get_replies(self, obj):
                """递归获取子回复"""
                child_replies = obj.child_replies.all()
                return ReplyNestedSerializer(child_replies, many=True).data

        class CommentWithRepliesSerializer(serializers.ModelSerializer):
            author_username = serializers.CharField(source='author.username', read_only=True)
            replies = ReplyNestedSerializer(many=True, required=False)

            class Meta:
                model = Comment
                fields = ['content', 'author_username', 'created_at', 'replies']
                read_only_fields = ['author']

        class PostWithNestedSerializer(serializers.ModelSerializer):
            """支持多层嵌套的文章序列化器"""
            comments = CommentWithRepliesSerializer(many=True, required=False)
            author = serializers.HiddenField(default=serializers.CurrentUserDefault())

            class Meta:
                model = Post
                fields = ['title', 'content', 'author', 'comments']

            @transaction.atomic
            def create(self, validated_data):
                """创建多层嵌套结构"""
                comments_data = validated_data.pop('comments', [])
                post = Post.objects.create(**validated_data)

                # 创建评论及其回复
                for comment_data in comments_data:
                    replies_data = comment_data.pop('replies', [])

                    # 创建评论
                    comment = Comment.objects.create(post=post, **comment_data)

                    # 创建回复
                    for reply_data in replies_data:
                        CommentReply.objects.create(
                            comment=comment,
                            author=self.context['request'].user,
                            **reply_data
                        )

                return post

        # 请求示例:
        # POST /api/posts/deep-nested/
        # {
        #     "title": "多层嵌套文章",
        #     "content": "支持评论和回复的文章...",
        #     "comments": [
        #         {
        #             "content": "主要评论",
        #             "replies": [
        #                 {
        #                     "content": "第一层回复"
        #                 },
        #                 {
        #                     "content": "另一个第一层回复"
        #                 }
        #             ]
        #         }
        #     ]
        # }
        ---

02.嵌套对象的更新
    a.部分嵌套更新
        处理嵌套对象的更新需要区分新增、修改和删除操作,避免数据不一致的问题。
        ---
        from rest_framework import serializers
        from django.db import transaction

        class PostUpdateWithCommentsSerializer(serializers.ModelSerializer):
            """支持嵌套更新的文章序列化器"""
            comments = CommentNestedSerializer(many=True, required=False)

            class Meta:
                model = Post
                fields = ['title', 'content', 'comments']

            @transaction.atomic
            def update(self, instance, validated_data):
                """更新文章和嵌套评论"""
                comments_data = validated_data.pop('comments', None)

                # 更新文章基本信息
                for attr, value in validated_data.items():
                    setattr(instance, attr, value)
                instance.save()

                # 处理嵌套评论的更新
                if comments_data is not None:
                    self._update_comments(instance, comments_data)

                return instance

            def _update_comments(self, post, comments_data):
                """更新评论的逻辑"""
                # 简单策略:删除所有旧评论,创建新评论
                # 在实际应用中,可能需要更复杂的匹配逻辑

                # 方案1:完全替换(简单但不够精确)
                post.comments.all().delete()
                for comment_data in comments_data:
                    Comment.objects.create(
                        post=post,
                        author=self.context['request'].user,
                        **comment_data
                    )

        # 更精确的嵌套更新策略
        class CommentUpdateSerializer(serializers.ModelSerializer):
            """支持ID的评论序列化器,用于精确更新"""
            id = serializers.IntegerField(required=False)  # 允许传递ID
            _destroy = serializers.BooleanField(write_only=True, required=False)  # 删除标记

            class Meta:
                model = Comment
                fields = ['id', 'content', '_destroy']

        class PostPreciseUpdateSerializer(serializers.ModelSerializer):
            """精确的嵌套更新序列化器"""
            comments = CommentUpdateSerializer(many=True, required=False)

            class Meta:
                model = Post
                fields = ['title', 'content', 'comments']

            @transaction.atomic
            def update(self, instance, validated_data):
                comments_data = validated_data.pop('comments', None)

                # 更新文章
                for attr, value in validated_data.items():
                    setattr(instance, attr, value)
                instance.save()

                if comments_data is not None:
                    self._precise_update_comments(instance, comments_data)

                return instance

            def _precise_update_comments(self, post, comments_data):
                """精确更新评论"""
                existing_comment_ids = set()
                new_comments = []

                for comment_data in comments_data:
                    comment_id = comment_data.get('id')
                    destroy_flag = comment_data.pop('_destroy', False)

                    if comment_id:
                        # 更新现有评论
                        try:
                            comment = post.comments.get(id=comment_id)
                            if destroy_flag:
                                comment.delete()
                            else:
                                for attr, value in comment_data.items():
                                    setattr(comment, attr, value)
                                comment.save()
                                existing_comment_ids.add(comment_id)
                        except Comment.DoesNotExist:
                            # 如果评论不存在,当作新评论处理
                            new_comments.append(comment_data)
                    else:
                        # 新评论
                        new_comments.append(comment_data)

                # 创建新评论
                for comment_data in new_comments:
                    Comment.objects.create(
                        post=post,
                        author=self.context['request'].user,
                        **comment_data
                    )

        # API使用示例:

        # 1. 添加新评论
        # PATCH /api/posts/1/precise-update/
        # {
        #     "title": "更新后的标题",
        #     "comments": [
        #         {"content": "新评论1"},
        #         {"content": "新评论2"}
        #     ]
        # }

        # 2. 更新现有评论
        # PATCH /api/posts/1/precise-update/
        # {
        #     "comments": [
        #         {"id": 10, "content": "更新后的评论内容"}
        #     ]
        # }

        # 3. 删除评论
        # PATCH /api/posts/1/precise-update/
        # {
        #     "comments": [
        #         {"id": 11, "_destroy": true}
        #     ]
        # }

        # 4. 混合操作
        # PATCH /api/posts/1/precise-update/
        # {
        #     "comments": [
        #         {"id": 12, "content": "更新内容"},
        #         {"content": "新增评论"},
        #         {"id": 13, "_destroy": true}
        #     ]
        # }
        ---
    b.批量嵌套操作
        批量处理多个嵌套对象的创建和更新,需要考虑性能和数据一致性的问题。
        ---
        from rest_framework import serializers
        from django.db import transaction
        from django.core.exceptions import ValidationError

        class BulkNestedOperationSerializer(serializers.Serializer):
            """批量嵌套操作序列化器"""
            operation_type = serializers.ChoiceField(choices=['create', 'update', 'delete'])
            posts = serializers.ListField(
                child=serializers.DictField(),
                min_length=1,
                max_length=10  # 限制批量操作数量
            )

            def validate_posts(self, value):
                """验证批量数据"""
                if self.initial_data.get('operation_type') == 'update':
                    # 更新操作必须包含ID
                    for i, post_data in enumerate(value):
                        if 'id' not in post_data:
                            raise serializers.ValidationError(f"位置 {i} 的文章缺少ID")
                return value

            @transaction.atomic
            def execute(self):
                """执行批量操作"""
                operation_type = self.validated_data['operation_type']
                posts_data = self.validated_data['posts']
                results = []
                errors = []

                try:
                    if operation_type == 'create':
                        results = self._bulk_create_posts(posts_data)
                    elif operation_type == 'update':
                        results = self._bulk_update_posts(posts_data)
                    elif operation_type == 'delete':
                        results = self._bulk_delete_posts(posts_data)

                except Exception as e:
                    errors.append(f"批量操作失败: {str(e)}")
                    transaction.set_rollback(True)

                return {
                    'operation_type': operation_type,
                    'success_count': len(results),
                    'error_count': len(errors),
                    'results': results,
                    'errors': errors
                }

            def _bulk_create_posts(self, posts_data):
                """批量创建文章"""
                created_posts = []
                user = self.context['request'].user

                for post_data in posts_data:
                    comments_data = post_data.pop('comments', [])

                    # 创建文章
                    post = Post.objects.create(author=user, **post_data)

                    # 创建评论
                    for comment_data in comments_data:
                        Comment.objects.create(
                            post=post,
                            author=user,
                            **comment_data
                        )

                    created_posts.append({
                        'post_id': post.id,
                        'title': post.title,
                        'comments_count': len(comments_data)
                    })

                return created_posts

            def _bulk_update_posts(self, posts_data):
                """批量更新文章"""
                updated_posts = []
                user = self.context['request'].user

                for post_data in posts_data:
                    post_id = post_data.pop('id')
                    comments_data = post_data.pop('comments', None)

                    try:
                        post = Post.objects.get(id=post_id)

                        # 更新文章信息
                        for attr, value in post_data.items():
                            setattr(post, attr, value)
                        post.save()

                        # 更新评论
                        if comments_data is not None:
                            post.comments.all().delete()  # 简化策略:替换所有评论
                            for comment_data in comments_data:
                                Comment.objects.create(
                                    post=post,
                                    author=user,
                                    **comment_data
                                )

                        updated_posts.append({
                            'post_id': post.id,
                            'title': post.title,
                            'comments_count': len(comments_data) if comments_data else 0
                        })

                    except Post.DoesNotExist:
                        raise ValidationError(f"文章 ID {post_id} 不存在")

                return updated_posts

            def _bulk_delete_posts(self, posts_data):
                """批量删除文章"""
                deleted_posts = []

                for post_data in posts_data:
                    post_id = post_data['id']

                    try:
                        post = Post.objects.get(id=post_id)
                        post_title = post.title
                        post.delete()

                        deleted_posts.append({
                            'post_id': post_id,
                            'title': post_title,
                            'deleted': True
                        })

                    except Post.DoesNotExist:
                        raise ValidationError(f"文章 ID {post_id} 不存在")

                return deleted_posts

        # 视图实现
        from rest_framework import views
        from rest_framework.response import Response
        from rest_framework import status

        class BulkNestedOperationView(views.APIView):
            """批量嵌套操作视图"""

            def post(self, request):
                """执行批量操作"""
                serializer = BulkNestedOperationSerializer(
                    data=request.data,
                    context={'request': request}
                )
                serializer.is_valid(raise_exception=True)

                result = serializer.execute()

                if result['error_count'] > 0 and result['success_count'] == 0:
                    return Response(result, status=status.HTTP_400_BAD_REQUEST)

                return Response(result, status=status.HTTP_200_OK)

        # API使用示例:

        # 1. 批量创建
        # POST /api/posts/bulk-nested/
        # {
        #     "operation_type": "create",
        #     "posts": [
        #         {
        #             "title": "批量文章1",
        #             "content": "内容1...",
        #             "comments": [
        #                 {"content": "评论1"}
        #             ]
        #         },
        #         {
        #             "title": "批量文章2",
        #             "content": "内容2...",
        #             "comments": [
        #                 {"content": "评论2"},
        #                 {"content": "评论3"}
        #             ]
        #         }
        #     ]
        # }

        # 2. 批量更新
        # POST /api/posts/bulk-nested/
        # {
        #     "operation_type": "update",
        #     "posts": [
        #         {
        #             "id": 1,
        #             "title": "更新后的标题1",
        #             "comments": [
        #                 {"content": "更新后的评论"}
        #             ]
        #         },
        #         {
        #             "id": 2,
        #             "title": "更新后的标题2"
        #         }
        #     ]
        # }

        # 3. 批量删除
        # POST /api/posts/bulk-nested/
        # {
        #     "operation_type": "delete",
        #     "posts": [
        #         {"id": 1},
        #         {"id": 2}
        #     ]
        # }
        ---

03.嵌套关系的删除
    a.级联删除策略
        处理嵌套对象删除时需要考虑级联关系、数据完整性和业务规则的约束。
        ---
        from rest_framework import serializers
        from django.db import transaction
        from django.db.models import ProtectedError

        class PostDeleteSerializer(serializers.Serializer):
            """文章删除序列化器,支持嵌套删除控制"""
            delete_comments = serializers.BooleanField(default=True)
            delete_strategy = serializers.ChoiceField(
                choices=['cascade', 'soft_delete', 'archive'],
                default='cascade'
            )

            def validate(self, attrs):
                """验证删除权限和条件"""
                request = self.context.get('request')
                post = self.context.get('post')

                # 检查删除权限
                if not request.user.has_perm('posts.delete_post'):
                    if post.author != request.user:
                        raise serializers.ValidationError("没有删除权限")

                # 检查文章状态
                if hasattr(post, 'status') and post.status == 'archived':
                    if attrs.get('delete_strategy') != 'cascade':
                        raise serializers.ValidationError("已归档的文章只能级联删除")

                # 检查关联数据
                if not attrs.get('delete_comments') and post.comments.exists():
                    raise serializers.ValidationError("文章存在评论,必须选择删除评论")

                return attrs

            @transaction.atomic
            def delete(self, post):
                """执行删除操作"""
                delete_strategy = self.validated_data['delete_strategy']
                delete_comments = self.validated_data['delete_comments']

                if delete_strategy == 'cascade':
                    return self._cascade_delete(post, delete_comments)
                elif delete_strategy == 'soft_delete':
                    return self._soft_delete(post)
                elif delete_strategy == 'archive':
                    return self._archive_post(post)

            def _cascade_delete(self, post, delete_comments):
                """级联删除"""
                try:
                    if delete_comments:
                        # 删除所有评论
                        post.comments.all().delete()

                    # 删除文章
                    post.delete()

                    return {
                        'success': True,
                        'strategy': 'cascade',
                        'deleted_comments_count': post.comments.count() if delete_comments else 0
                    }

                except ProtectedError as e:
                    raise serializers.ValidationError(f"删除失败,存在依赖关系: {e}")

            def _soft_delete(self, post):
                """软删除(标记为已删除)"""
                post.is_deleted = True
                post.deleted_at = timezone.now()
                post.save(update_fields=['is_deleted', 'deleted_at'])

                # 软删除关联的评论
                post.comments.update(is_deleted=True, deleted_at=timezone.now())

                return {
                    'success': True,
                    'strategy': 'soft_delete',
                    'affected_comments_count': post.comments.count()
                }

            def _archive_post(self, post):
                """归档文章"""
                post.status = 'archived'
                post.archived_at = timezone.now()
                post.save(update_fields=['status', 'archived_at'])

                return {
                    'success': True,
                    'strategy': 'archive',
                    'archived_at': post.archived_at
                }

        # 视图实现
        from rest_framework import generics, status
        from rest_framework.response import Response
        from rest_framework.exceptions import NotFound

        class PostDeleteView(generics.DestroyAPIView):
            """文章删除视图,支持嵌套删除控制"""
            queryset = Post.objects.all()

            def delete(self, request, *args, **kwargs):
                """自定义删除方法"""
                post = self.get_object()

                serializer = PostDeleteSerializer(
                    data=request.data,
                    context={
                        'request': request,
                        'post': post
                    }
                )
                serializer.is_valid(raise_exception=True)

                result = serializer.delete(post)

                if result['success']:
                    return Response({
                        'message': f"文章删除成功",
                        'details': result
                    }, status=status.HTTP_204_NO_CONTENT)

                return Response({
                    'message': "删除失败",
                    'details': result
                }, status=status.HTTP_400_BAD_REQUEST)

        # 批量删除视图
        class BulkPostDeleteView(views.APIView):
            """批量删除文章视图"""

            def post(self, request):
                post_ids = request.data.get('post_ids', [])
                delete_comments = request.data.get('delete_comments', True)
                delete_strategy = request.data.get('delete_strategy', 'cascade')

                if not post_ids:
                    return Response({
                        'error': '请提供要删除的文章ID列表'
                    }, status=status.HTTP_400_BAD_REQUEST)

                deleted_posts = []
                failed_posts = []

                for post_id in post_ids:
                    try:
                        post = Post.objects.get(id=post_id)

                        # 检查权限
                        if not request.user.has_perm('posts.delete_post') and post.author != request.user:
                            failed_posts.append({
                                'post_id': post_id,
                                'error': '删除权限不足'
                            })
                            continue

                        # 执行删除
                        delete_data = {
                            'delete_comments': delete_comments,
                            'delete_strategy': delete_strategy
                        }

                        serializer = PostDeleteSerializer(
                            data=delete_data,
                            context={
                                'request': request,
                                'post': post
                            }
                        )
                        serializer.is_valid(raise_exception=True)

                        result = serializer.delete(post)

                        if result['success']:
                            deleted_posts.append({
                                'post_id': post_id,
                                'title': post.title,
                                'strategy': result['strategy']
                            })
                        else:
                            failed_posts.append({
                                'post_id': post_id,
                                'error': '删除操作失败'
                            })

                    except Post.DoesNotExist:
                        failed_posts.append({
                            'post_id': post_id,
                            'error': '文章不存在'
                        })
                    except Exception as e:
                        failed_posts.append({
                            'post_id': post_id,
                            'error': str(e)
                        })

                response_data = {
                    'total_requested': len(post_ids),
                    'success_count': len(deleted_posts),
                    'failed_count': len(failed_posts),
                    'deleted_posts': deleted_posts,
                    'failed_posts': failed_posts
                }

                status_code = status.HTTP_200_OK if deleted_posts else status.HTTP_400_BAD_REQUEST
                return Response(response_data, status=status_code)

        # API使用示例:

        # 1. 删除单个文章(包含评论)
        # DELETE /api/posts/1/delete/
        # {
        #     "delete_comments": true,
        #     "delete_strategy": "cascade"
        # }

        # 2. 软删除文章
        # DELETE /api/posts/1/delete/
        # {
        #     "delete_comments": false,
        #     "delete_strategy": "soft_delete"
        # }

        # 3. 归档文章
        # DELETE /api/posts/1/delete/
        # {
        #     "delete_strategy": "archive"
        # }

        # 4. 批量删除文章
        # POST /api/posts/bulk-delete/
        # {
        #     "post_ids": [1, 2, 3],
        #     "delete_comments": true,
        #     "delete_strategy": "cascade"
        # }
        ---

04.事务处理和错误回滚
    a.嵌套事务管理
        复杂的嵌套操作需要仔细的事务管理,确保数据一致性和错误恢复能力。
        ---
        from rest_framework import serializers
        from django.db import transaction, DatabaseError
        from django.core.exceptions import ValidationError
        import logging
        import time

        logger = logging.getLogger(__name__)

        class NestedTransactionSerializer(serializers.Serializer):
            """嵌套事务管理序列化器"""

            def create_with_transaction(self, validated_data):
                """使用嵌套事务创建数据"""
                try:
                    with transaction.atomic():
                        # 使用保存点,支持部分回滚
                        savepoint = transaction.savepoint()

                        try:
                            # 尝试创建数据
                            result = self.perform_create(validated_data)

                            # 验证数据完整性
                            self.validate_post_creation(result)

                            # 提交事务
                            transaction.savepoint_commit(savepoint)

                            return {
                                'success': True,
                                'data': result,
                                'message': '创建成功'
                            }

                        except (ValidationError, DatabaseError) as e:
                            # 回滚到保存点
                            transaction.savepoint_rollback(savepoint)
                            logger.error(f"嵌套创建失败: {e}")
                            raise serializers.ValidationError(str(e))

                except Exception as e:
                    logger.error(f"事务管理失败: {e}")
                    raise serializers.ValidationError("服务器内部错误")

            def perform_create(self, validated_data):
                """实际创建逻辑,子类需要实现"""
                raise NotImplementedError("子类必须实现perform_create方法")

            def validate_post_creation(self, result):
                """验证创建后的数据完整性"""
                pass

        class PostTransactionSerializer(NestedTransactionSerializer):
            """文章事务序列化器"""
            title = serializers.CharField(max_length=200)
            content = serializers.TextField()
            comments = serializers.ListField(
                child=serializers.CharField(max_length=1000),
                required=False,
                max_length=5  # 限制嵌套数量
            )
            tags = serializers.ListField(
                child=serializers.CharField(max_length=50),
                required=False
            )

            def validate_title(self, value):
                """验证标题唯一性"""
                if Post.objects.filter(title=value).exists():
                    raise serializers.ValidationError("标题已存在")
                return value

            def validate(self, attrs):
                """跨字段验证"""
                comments = attrs.get('comments', [])
                tags = attrs.get('tags', [])

                # 验证内容和嵌套数据的一致性
                if len(comments) > 0 and len(attrs.get('content', '')) < 100:
                    raise serializers.ValidationError("有评论的文章内容不能少于100字符")

                # 验证标签数量
                if len(tags) > 10:
                    raise serializers.ValidationError("标签数量不能超过10个")

                return attrs

            def perform_create(self, validated_data):
                """执行创建操作"""
                comments_data = validated_data.pop('comments', [])
                tags_data = validated_data.pop('tags', [])

                user = self.context['request'].user

                # 创建文章
                post = Post.objects.create(author=user, **validated_data)

                # 创建评论
                for comment_content in comments_data:
                    Comment.objects.create(
                        post=post,
                        author=user,
                        content=comment_content
                    )

                # 创建或获取标签
                for tag_name in tags_data:
                    tag, _ = Tag.objects.get_or_create(name=tag_name)
                    post.tags.add(tag)

                return {
                    'post': post,
                    'comments_count': len(comments_data),
                    'tags_count': len(tags_data)
                }

            def validate_post_creation(self, result):
                """验证创建后的完整性"""
                post = result['post']

                # 检查文章是否正确创建
                if not post.id:
                    raise ValidationError("文章创建失败")

                # 检查评论数量是否匹配
                actual_comments_count = post.comments.count()
                expected_comments_count = result['comments_count']

                if actual_comments_count != expected_comments_count:
                    raise ValidationError(f"评论数量不匹配: 期望{expected_comments_count}, 实际{actual_comments_count}")

                # 检查标签数量是否匹配
                actual_tags_count = post.tags.count()
                expected_tags_count = result['tags_count']

                if actual_tags_count != expected_tags_count:
                    raise ValidationError(f"标签数量不匹配: 期望{expected_tags_count}, 实际{actual_tags_count}")

        # 视图实现
        from rest_framework import views, status
        from rest_framework.response import Response
        from django.views.decorators.csrf import csrf_exempt
        from django.utils.decorators import method_decorator

        @method_decorator(csrf_exempt, name='dispatch')
        class PostTransactionCreateView(views.APIView):
            """支持事务管理的文章创建视图"""

            def post(self, request):
                """创建文章,支持事务管理"""
                serializer = PostTransactionSerializer(
                    data=request.data,
                    context={'request': request}
                )
                serializer.is_valid(raise_exception=True)

                try:
                    result = serializer.create_with_transaction(serializer.validated_data)

                    if result['success']:
                        # 序列化返回数据
                        post_data = result['data']['post']
                        post_serializer = PostDetailSerializer(post_data)

                        return Response({
                            'success': True,
                            'message': result['message'],
                            'data': {
                                'post': post_serializer.data,
                                'comments_count': result['data']['comments_count'],
                                'tags_count': result['data']['tags_count']
                            }
                        }, status=status.HTTP_201_CREATED)

                except serializers.ValidationError as e:
                    return Response({
                        'success': False,
                        'error': str(e)
                    }, status=status.HTTP_400_BAD_REQUEST)

                except Exception as e:
                    logger.error(f"创建文章时发生未知错误: {e}")
                    return Response({
                        'success': False,
                        'error': '服务器内部错误'
                    }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # 事务重试机制
        class TransactionRetryMixin:
            """事务重试混入类"""

            def create_with_retry(self, serializer_class, data, context, max_retries=3):
                """带重试机制的事务创建"""
                for attempt in range(max_retries):
                    try:
                        serializer = serializer_class(data=data, context=context)
                        serializer.is_valid(raise_exception=True)

                        with transaction.atomic():
                            result = serializer.save()
                            return result

                    except DatabaseError as e:
                        if attempt == max_retries - 1:  # 最后一次尝试
                            raise serializers.ValidationError(f"数据库操作失败,已重试{max_retries}次")

                        logger.warning(f"数据库操作失败,第{attempt + 1}次重试: {e}")
                        # 短暂延迟后重试
                        time.sleep(0.1 * (attempt + 1))

                    except serializers.ValidationError:
                        # 验证错误不重试
                        raise

        # 使用重试机制的视图
        class PostWithRetryCreateView(TransactionRetryMixin, views.APIView):
            """带重试机制的文章创建视图"""

            def post(self, request):
                try:
                    result = self.create_with_retry(
                        PostTransactionSerializer,
                        request.data,
                        {'request': request}
                    )

                    return Response({
                        'success': True,
                        'message': '文章创建成功',
                        'data': result
                    }, status=status.HTTP_201_CREATED)

                except serializers.ValidationError as e:
                    return Response({
                        'success': False,
                        'error': str(e)
                    }, status=status.HTTP_400_BAD_REQUEST)

        # API使用示例:

        # 1. 正常创建(使用事务)
        # POST /api/posts/transaction/
        # {
        #     "title": "事务测试文章",
        #     "content": "这是一篇测试事务管理的文章,内容足够长...",
        #     "comments": [
        #         "第一条评论",
        #         "第二条评论"
        #     ],
        #     "tags": ["Django", "REST", "事务"]
        # }

        # 成功响应:
        # {
        #     "success": True,
        #     "message": "创建成功",
        #     "data": {
        #         "post": {
        #             "id": 1,
        #             "title": "事务测试文章",
        #             "content": "这是一篇测试事务管理的文章..."
        #         },
        #         "comments_count": 2,
        #         "tags_count": 3
        #     }
        # }

        # 2. 验证失败(事务回滚)
        # POST /api/posts/transaction/
        # {
        #     "title": "已存在的标题",  # 会触发唯一性验证失败
        #     "content": "短",  # 会触发内容长度验证失败
        #     "comments": ["评论"]
        # }

        # 失败响应:
        # {
        #     "success": False,
        #     "error": "标题已存在"
        # }
        ---

7.5 关系性能优化

01.N+1查询问题诊断
    a.识别N+1查询
        N+1查询是DRF开发中最常见的性能问题之一,需要掌握诊断和解决的方法。
        ---
        from django.db import connection
        from django.test.utils import override_settings
        import time

        class N1QueryDiagnostic:
            """N+1查询诊断工具类"""

            def __init__(self):
                self.reset()

            def reset(self):
                """重置计数器"""
                connection.queries_log.clear()
                self.query_count = 0
                self.start_time = None

            def start_diagnosis(self):
                """开始诊断"""
                self.reset()
                self.start_time = time.time()

            def end_diagnosis(self):
                """结束诊断"""
                end_time = time.time()
                self.query_count = len(connection.queries)
                self.duration = end_time - self.start_time

                return {
                    'query_count': self.query_count,
                    'duration': self.duration,
                    'queries': connection.queries.copy()
                }

            def analyze_queries(self, description=""):
                """分析查询模式"""
                queries = connection.queries_log

                # 分析查询类型分布
                query_types = {}
                table_accesses = {}

                for query in queries:
                    sql = query['sql'].upper()

                    # 统计查询类型
                    if 'SELECT' in sql:
                        query_types['SELECT'] = query_types.get('SELECT', 0) + 1
                    elif 'INSERT' in sql:
                        query_types['INSERT'] = query_types.get('INSERT', 0) + 1
                    elif 'UPDATE' in sql:
                        query_types['UPDATE'] = query_types.get('UPDATE', 0) + 1
                    elif 'DELETE' in sql:
                        query_types['DELETE'] = query_types.get('DELETE', 0) + 1

                    # 统计表访问
                    for table in ['post', 'comment', 'user', 'tag']:
                        if table in sql:
                            table_accesses[table] = table_accesses.get(table, 0) + 1

                return {
                    'description': description,
                    'query_count': len(queries),
                    'query_types': query_types,
                    'table_accesses': table_accesses,
                    'queries': queries
                }

        # 在视图中使用诊断工具
        class PostListViewDiagnosis(generics.ListAPIView):
            """带诊断功能的文章列表视图"""
            queryset = Post.objects.all()
            serializer_class = PostWithCommentsSerializer

            def get_queryset(self):
                """获取查询集,支持诊断模式"""
                diagnostic_mode = self.request.query_params.get('diagnostic') == 'true'

                if diagnostic_mode:
                    # 启用查询日志
                    from django.conf import settings
                    if not settings.DEBUG:
                        # 临时启用DEBUG来记录查询
                        settings.DEBUG = True

                    # 开始诊断
                    diagnostic = N1QueryDiagnostic()
                    diagnostic.start_diagnosis()

                    # 执行未优化的查询
                    queryset = super().get_queryset()

                    # 触发查询
                    serializer = self.get_serializer(queryset, many=True)
                    data = serializer.data

                    # 分析查询
                    result = diagnostic.analyze_queries("未优化的文章列表查询")

                    # 将诊断信息添加到响应中
                    self.diagnostic_result = result
                    return queryset

                return super().get_queryset()

            def list(self, request, *args, **kwargs):
                """重写list方法以包含诊断信息"""
                response = super().list(request, *args, **kwargs)

                if hasattr(self, 'diagnostic_result'):
                    diagnostic_data = self.diagnostic_result.copy()

                    # 检测N+1问题
                    is_n1_problem = self.detect_n1_problem(diagnostic_data)
                    diagnostic_data['n1_analysis'] = {
                        'is_problem': is_n1_problem,
                        'recommendations': self.get_optimization_recommendations(diagnostic_data)
                    }

                    response.data['diagnostic'] = diagnostic_data

                return response

            def detect_n1_problem(self, diagnostic_data):
                """检测是否存在N+1查询问题"""
                query_count = diagnostic_data['query_count']
                table_accesses = diagnostic_data['table_accesses']

                # 简单的N+1检测逻辑
                # 如果查询总数超过10次,且有多个表被重复访问,可能是N+1问题
                if query_count > 10:
                    high_access_tables = [table for table, count in table_accesses.items() if count > 5]
                    return len(high_access_tables) > 0

                return False

            def get_optimization_recommendations(self, diagnostic_data):
                """获取优化建议"""
                recommendations = []
                table_accesses = diagnostic_data['table_accesses']

                if 'comment' in table_accesses and table_accesses['comment'] > 5:
                    recommendations.append({
                        'issue': '评论表访问次数过多',
                        'solution': '使用prefetch_related()预加载评论',
                        'code': 'queryset = Post.objects.prefetch_related("comments")'
                    })

                if 'user' in table_accesses and table_accesses['user'] > 5:
                    recommendations.append({
                        'issue': '用户表访问次数过多',
                        'solution': '使用select_related()预加载作者信息',
                        'code': 'queryset = Post.objects.select_related("author")'
                    })

                return recommendations

        # API使用示例:
        # GET /api/posts/diagnostic/?diagnostic=true
        # 响应示例:
        # {
        #     "count": 10,
        #     "results": [...],  # 正常的API响应数据
        #     "diagnostic": {
        #         "description": "未优化的文章列表查询",
        #         "query_count": 25,
        #         "query_types": {"SELECT": 25},
        #         "table_accesses": {
        #             "post": 10,
        #             "user": 10,
        #             "comment": 5
        #         },
        #         "n1_analysis": {
        #             "is_problem": true,
        #             "recommendations": [
        #                 {
        #                     "issue": "用户表访问次数过多",
        #                     "solution": "使用select_related()预加载作者信息",
        #                     "code": "queryset = Post.objects.select_related(\"author\")"
        #                 }
        #             ]
        #         }
        #     }
        # }
                ---
            b.查询性能分析工具
                使用Django Debug Toolbar和自定义工具来分析查询性能,找出瓶颈。
                ---
        from django.db import connection, reset_queries
        from django.conf import settings
        import time
        import functools

        def query_profiler(func):
            """查询性能分析装饰器"""
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
        # 启用查询日志
        if not settings.DEBUG:
            settings.DEBUG = True

        # 清空之前的查询记录
        reset_queries()

        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()

        queries = connection.queries
        total_time = sum(float(q['time']) for q in queries)
        duration = end_time - start_time

        # 分析查询性能
        slow_queries = [q for q in queries if float(q['time']) > 0.1]

        # 按执行时间排序
        queries_sorted = sorted(queries, key=lambda x: float(x['time']), reverse=True)

        profile_data = {
            'function': func.__name__,
            'total_queries': len(queries),
            'total_query_time': round(total_time, 4),
            'function_duration': round(duration, 4),
            'slow_queries_count': len(slow_queries),
            'slowest_query': queries_sorted[0] if queries_sorted else None,
            'top_5_slowest': queries_sorted[:5]
        }

        # 将性能数据附加到结果中(如果result是dict或list)
        if isinstance(result, dict):
            result['_query_profile'] = profile_data
        elif hasattr(result, 'data') and isinstance(result.data, dict):
            result.data['_query_profile'] = profile_data

        return result
            return wrapper

        class PerformanceAnalysisView(generics.ListAPIView):
            """性能分析视图"""
            queryset = Post.objects.all()
            serializer_class = PostWithCommentsSerializer

            @query_profiler
            def list(self, request, *args, **kwargs):
        """带性能分析的列表方法"""
        return super().list(request, *args, **kwargs)

            def get_queryset(self):
        """获取查询集,支持不同的优化策略"""
        optimization = self.request.query_params.get('optimization', 'none')

        if optimization == 'select_related':
            # 优化1:使用select_related
            return Post.objects.select_related('author').all()
        elif optimization == 'prefetch_related':
            # 优化2:使用prefetch_related
            return Post.objects.prefetch_related('comments', 'tags').all()
        elif optimization == 'combined':
            # 优化3:组合优化
            return Post.objects.select_related('author').prefetch_related(
                'comments', 'comments__author', 'tags'
            ).all()
        else:
            # 未优化版本
            return super().get_queryset()

        # 查询性能分析API
        class QueryAnalysisAPIView(views.APIView):
            """查询性能分析API"""

            def post(self, request):
        """执行查询性能分析"""
        query_strategies = [
            {
                'name': '无优化',
                'queryset': Post.objects.all()
            },
            {
                'name': 'select_related(author)',
                'queryset': Post.objects.select_related('author').all()
            },
            {
                'name': 'prefetch_related(comments)',
                'queryset': Post.objects.prefetch_related('comments').all()
            },
            {
                'name': '组合优化',
                'queryset': Post.objects.select_related('author').prefetch_related(
                    'comments', 'tags'
                ).all()
            }
        ]

        results = []
        limit = min(int(request.data.get('limit', 10)), 50)  # 限制测试数量

        for strategy in query_strategies:
            result = self.analyze_queryset(strategy['queryset'][:limit], strategy['name'])
            results.append(result)

        return Response({
            'test_parameters': {
                'limit': limit,
                'strategies_tested': len(query_strategies)
            },
            'results': results,
            'recommendations': self.generate_recommendations(results)
        })

            def analyze_queryset(self, queryset, strategy_name):
        """分析查询集性能"""
        reset_queries()

        start_time = time.time()

        # 强制执行查询
        list(queryset)

        end_time = time.time()

        queries = connection.queries
        total_query_time = sum(float(q['time']) for q in queries)

        return {
            'strategy': strategy_name,
            'query_count': len(queries),
            'total_query_time': round(total_query_time, 4),
            'total_duration': round(end_time - start_time, 4),
            'average_query_time': round(total_query_time / len(queries), 4) if queries else 0,
            'slowest_query': max(queries, key=lambda x: float(x['time'])) if queries else None
        }

            def generate_recommendations(self, results):
        """生成优化建议"""
        recommendations = []

        # 找出最佳策略
        best_strategy = min(results, key=lambda x: x['query_count'])

        recommendations.append({
            'type': 'best_strategy',
            'message': f"推荐使用 '{best_strategy['strategy']}' 策略",
            'reason': f"查询数量最少({best_strategy['query_count']}个)"
        })

        # 分析N+1问题
        worst_strategy = max(results, key=lambda x: x['query_count'])
        if worst_strategy['query_count'] > worst_strategy['total_duration'] * 10:
            recommendations.append({
                'type': 'n1_problem',
                'message': "检测到可能的N+1查询问题",
                'reason': f"'{worst_strategy['strategy']}' 策略执行了{worst_strategy['query_count']}个查询"
            })

        return recommendations

        # API使用示例:
        # POST /api/analysis/query-performance/
        # {
        #     "limit": 20
        # }
        #
        # 响应示例:
        # {
        #     "test_parameters": {
        #         "limit": 20,
        #         "strategies_tested": 4
        #     },
        #     "results": [
        #         {
        #             "strategy": "无优化",
        #             "query_count": 61,
        #             "total_query_time": 0.0234,
        #             "total_duration": 0.0456,
        #             "average_query_time": 0.0004
        #         },
        #         {
        #             "strategy": "select_related(author)",
        #             "query_count": 21,
        #             "total_query_time": 0.0087,
        #             "total_duration": 0.0123,
        #             "average_query_time": 0.0004
        #         },
        #         {
        #             "strategy": "prefetch_related(comments)",
        #             "query_count": 25,
        #             "total_query_time": 0.0092,
        #             "total_duration": 0.0145,
        #             "average_query_time": 0.0004
        #         },
        #         {
        #             "strategy": "组合优化",
        #             "query_count": 3,
        #             "total_query_time": 0.0031,
        #             "total_duration": 0.0056,
        #             "average_query_time": 0.0010
        #         }
        #     ],
        #     "recommendations": [
        #         {
        #             "type": "best_strategy",
        #             "message": "推荐使用 '组合优化' 策略",
        #             "reason": "查询数量最少(3个)"
        #         }
        #     ]
        # }
        ---
02.prefetch_related的使用
    a.基础prefetch用法
        prefetch_related用于优化反向关系和多对多关系,通过额外的查询来减少数据库访问次数。
        ---
        from rest_framework import serializers
        from django.db.models import Prefetch
        from django.db.models import Count, Q

        class PostOptimizedSerializer(serializers.ModelSerializer):
            """优化后的文章序列化器"""
            author_info = serializers.SerializerMethodField()
            comments_summary = serializers.SerializerMethodField()
            tags_summary = serializers.SerializerMethodField()

            class Meta:
        model = Post
        fields = ['id', 'title', 'content', 'author_info', 'comments_summary',
                'tags_summary', 'created_at']

            def get_author_info(self, obj):
        """获取作者信息(通过select_related优化)"""
        return {
            'id': obj.author.id,
            'username': obj.author.username,
            'email': obj.author.email
        }

            def get_comments_summary(self, obj):
        """获取评论摘要"""
        return {
            'count': obj.comments.count(),
            'recent_comment': obj.comments.first().content if obj.comments.exists() else None
        }

            def get_tags_summary(self, obj):
        """获取标签摘要"""
        return [tag.name for tag in obj.tags.all()[:5]]  # 只显示前5个标签

        # 基础prefetch优化视图
        class PostOptimizedListView(generics.ListAPIView):
            serializer_class = PostOptimizedSerializer

            def get_queryset(self):
        """使用prefetch_related优化查询"""
        queryset = Post.objects.select_related('author').prefetch_related(
            'comments__author',  # 预加载评论及其作者
            'tags'  # 预加载标签
        )

        # 可选:限制查询结果数量以提高性能
        limit = int(self.request.query_params.get('limit', 20))
        return queryset[:limit]

        # 高级prefetch用法
        class PostAdvancedOptimizedListView(generics.ListAPIView):
            serializer_class = PostOptimizedSerializer

            def get_queryset(self):
        """高级prefetch优化"""
        user = self.request.user

        # 使用Prefetch对象进行精确控制
        queryset = Post.objects.select_related('author').prefetch_related(
            # 只预加载最近的评论
            Prefetch(
                'comments',
                queryset=Comment.objects.order_by('-created_at')[:3],
                to_attr='recent_comments'
            ),
            # 只预加载有文章的标签
            Prefetch(
                'tags',
                queryset=Tag.objects.annotate(
                    post_count=Count('posts')
                ).filter(post_count__gt=0)
            )
        )

        # 根据用户权限过滤
        if not user.is_staff:
            queryset = queryset.filter(
                Q(status='published') | Q(author=user)
            )

        return queryset

            def get_serializer_context(self):
        """传递额外的上下文给序列化器"""
        context = super().get_serializer_context()
        context['show_all_comments'] = self.request.query_params.get('all_comments') == 'true'
        return context

        # 条件性prefetch
        class ConditionalPrefetchView(generics.ListAPIView):
            serializer_class = PostOptimizedSerializer

            def get_queryset(self):
        """根据请求参数动态决定prefetch策略"""
        include_comments = self.request.query_params.get('include_comments') == 'true'
        include_tags = self.request.query_params.get('include_tags') == 'true'
        comment_limit = int(self.request.query_params.get('comment_limit', 5))

        # 基础查询
        queryset = Post.objects.select_related('author')

        # 动态添加prefetch
        prefetch_relations = []

        if include_comments:
            # 根据comment_limit动态预加载评论
            comments_queryset = Comment.objects.order_by('-created_at')[:comment_limit]
            prefetch_relations.append(
                Prefetch('comments', queryset=comments_queryset, to_attr='limited_comments')
            )

        if include_tags:
            # 按文章数量排序预加载标签
            tags_queryset = Tag.objects.annotate(
                post_count=Count('posts')
            ).order_by('-post_count')
            prefetch_relations.append(
                Prefetch('tags', queryset=tags_queryset)
            )

        if prefetch_relations:
            queryset = queryset.prefetch_related(*prefetch_relations)

        return queryset

        # 预取数据的序列化器
        class PostWithPrefetchedDataSerializer(serializers.ModelSerializer):
            """使用预取数据的序列化器"""
            author_username = serializers.CharField(source='author.username', read_only=True)

            # 使用预取的评论数据
            recent_comments = serializers.SerializerMethodField()

            # 使用预取的标签数据
            popular_tags = serializers.SerializerMethodField()

            class Meta:
        model = Post
        fields = ['id', 'title', 'content', 'author_username',
                'recent_comments', 'popular_tags', 'created_at']

            def get_recent_comments(self, obj):
        """从预取的数据中获取评论"""
        if hasattr(obj, 'limited_comments'):
            # 使用预取的评论
            comments = obj.limited_comments
        elif hasattr(obj, 'recent_comments'):
            # 使用预取的最近评论
            comments = obj.recent_comments
        else:
            # 回退到正常查询
            comments = obj.comments.all()[:3]

        return [{
            'id': comment.id,
            'content': comment.content[:100],  # 截取前100字符
            'author': comment.author.username if hasattr(comment, 'author') else 'Unknown',
            'created_at': comment.created_at
        } for comment in comments]

            def get_popular_tags(self, obj):
        """获取热门标签"""
        tags = obj.tags.all()

        # 如果标签有post_count属性(来自预取),按它排序
        if tags and hasattr(tags[0], 'post_count'):
            tags = sorted(tags, key=lambda x: getattr(x, 'post_count', 0), reverse=True)

        return [{
            'id': tag.id,
            'name': tag.name,
            'post_count': getattr(tag, 'post_count', 0)
        } for tag in tags[:5]]

        # API使用示例:

        # 1. 基础优化查询
        # GET /api/posts/optimized/
        # 自动使用select_related和prefetch_related优化

        # 2. 高级优化查询
        # GET /api/posts/advanced-optimized/
        # 使用Prefetch对象进行精确控制

        # 3. 条件性预取
        # GET /api/posts/conditional-prefetch/?include_comments=true&include_tags=true&comment_limit=3
        # 根据参数动态决定预取策略

        # 4. 性能对比
        # GET /api/posts/diagnostic/?diagnostic=true
        # 查看查询性能分析
        ---
    b.复杂的Prefetch查询
        处理复杂的查询场景,如嵌套关系的预取、条件预取和自定义预取逻辑。
        ---
        from django.db.models import Prefetch, Count, Avg, Q, Case, When, IntegerField
        from django.db.models.functions import Coalesce
        from django.core.paginator import Paginator

        class ComplexPrefetchView(generics.ListAPIView):
            """复杂Prefetch查询示例"""

            def get_queryset(self):
        """复杂的prefetch查询"""
        # 获取请求参数
        popular_only = self.request.query_params.get('popular_only') == 'true'
        include_stats = self.request.query_params.get('include_stats') == 'true'
        category = self.request.query_params.get('category')

        # 基础查询 - 预加载作者
        queryset = Post.objects.select_related('author')

        # 条件过滤
        if category:
            queryset = queryset.filter(category__slug=category)

        # 复杂的prefetch逻辑
        prefetch_list = []

        # 1. 评论的复杂预取
        comments_prefetch = self._build_comments_prefetch(popular_only, include_stats)
        prefetch_list.append(comments_prefetch)

        # 2. 标签的统计预取
        tags_prefetch = self._build_tags_prefetch(include_stats)
        prefetch_list.append(tags_prefetch)

        # 3. 相关文章预取
        related_prefetch = self._build_related_posts_prefetch()
        prefetch_list.append(related_prefetch)

        queryset = queryset.prefetch_related(*prefetch_list)

        return queryset

            def _build_comments_prefetch(self, popular_only, include_stats):
        """构建评论的复杂预取"""
        # 基础评论查询
        comments_queryset = Comment.objects.select_related('author')

        if popular_only:
            # 只预取受欢迎的评论(假设有likes字段)
            comments_queryset = comments_queryset.filter(
                likes__gte=5
            ).annotate(
                like_count=Count('likes')
            ).order_by('-like_count')

        if include_stats:
            # 包含统计信息
            comments_queryset = comments_queryset.annotate(
                reply_count=Count('replies'),
                recent_activity=Case(
                    When(
                        created_at__gte=timezone.now() - timedelta(days=7),
                        then=1
                    ),
                    default=0,
                    output_field=IntegerField()
                )
            )

        return Prefetch(
            'comments',
            queryset=comments_queryset,
            to_attr='filtered_comments'
        )

            def _build_tags_prefetch(self, include_stats):
        """构建标签的统计预取"""
        tags_queryset = Tag.objects.annotate(
            post_count=Count('posts'),
            avg_rating=Coalesce(
                Avg('posts__rating'),
                0
            )
        )

        if include_stats:
            # 包含更多统计信息
            tags_queryset = tags_queryset.annotate(
                recent_posts_count=Count(
                    'posts',
                    filter=Q(posts__created_at__gte=timezone.now() - timedelta(days=30))
                ),
                trending_score=Count(
                    'posts',
                    filter=Q(posts__created_at__gte=timezone.now() - timedelta(days=7))
                ) * 10 + Count('posts') * 0.1
            )

        return Prefetch(
            'tags',
            queryset=tags_queryset.order_by('-post_count'),
            to_attr='stats_tags'
        )

            def _build_related_posts_prefetch(self):
        """构建相关文章的预取"""
        from django.db.models import F
        from django.db.models.functions import Greatest

        # 查找相同类别的其他文章
        related_posts = Post.objects.filter(
            category=F('category')
        ).exclude(
            id=F('id')
        ).select_related('author').annotate(
            relevance_score=Greatest(
                Count('comments') * 0.3,
                F('views') * 0.001,
                1
            )
        ).order_by('-relevance_score')[:3]

        return Prefetch(
            'category__posts',
            queryset=related_posts,
            to_attr='related_posts'
        )

        # 复杂的序列化器
        class ComplexPostSerializer(serializers.ModelSerializer):
            """处理复杂预取数据的序列化器"""
            author_info = serializers.SerializerMethodField()
            comments_analysis = serializers.SerializerMethodField()
            tags_insights = serializers.SerializerMethodField()
            related_content = serializers.SerializerMethodField()

            class Meta:
        model = Post
        fields = ['id', 'title', 'content', 'author_info',
                'comments_analysis', 'tags_insights', 'related_content',
                'created_at']

            def get_author_info(self, obj):
        """作者信息"""
        return {
            'id': obj.author.id,
            'username': obj.author.username,
            'total_posts': obj.author.posts.count(),
            'engagement_rate': getattr(obj.author, 'engagement_rate', 0)
        }

            def get_comments_analysis(self, obj):
        """评论分析"""
        if hasattr(obj, 'filtered_comments'):
            comments = obj.filtered_comments
        else:
            comments = obj.comments.all()

        total_comments = obj.comments.count()  # 总数
        filtered_count = len(comments)  # 过滤后的数量

        analysis = {
            'total_count': total_comments,
            'filtered_count': filtered_count,
            'comments': []
        }

        for comment in comments:
            comment_data = {
                'id': comment.id,
                'content': comment.content[:100],
                'author': comment.author.username,
                'created_at': comment.created_at
            }

            # 添加统计数据(如果存在)
            if hasattr(comment, 'like_count'):
                comment_data['likes'] = comment.like_count
            if hasattr(comment, 'reply_count'):
                comment_data['replies'] = comment.reply_count
            if hasattr(comment, 'recent_activity'):
                comment_data['is_recent'] = bool(comment.recent_activity)

            analysis['comments'].append(comment_data)

        return analysis

            def get_tags_insights(self, obj):
        """标签洞察"""
        if hasattr(obj, 'stats_tags'):
            tags = obj.stats_tags
        else:
            tags = obj.tags.all()

        insights = {
            'total_tags': obj.tags.count(),
            'featured_tags': []
        }

        for tag in tags:
            tag_data = {
                'id': tag.id,
                'name': tag.name,
                'posts_count': getattr(tag, 'post_count', 0),
                'avg_rating': round(getattr(tag, 'avg_rating', 0), 2)
            }

            # 添加额外统计数据(如果存在)
            if hasattr(tag, 'recent_posts_count'):
                tag_data['recent_posts'] = tag.recent_posts_count
            if hasattr(tag, 'trending_score'):
                tag_data['trending_score'] = round(tag.trending_score, 2)

            insights['featured_tags'].append(tag_data)

        return insights

            def get_related_content(self, obj):
        """相关内容"""
        related_posts = []

        # 检查是否有预取的相关文章
        if hasattr(obj, 'category') and hasattr(obj.category, 'related_posts'):
            related_posts = obj.category.related_posts
        else:
            # 回退查询
            related_posts = Post.objects.filter(
                category=obj.category
            ).exclude(id=obj.id).select_related('author')[:3]

        return [{
            'id': post.id,
            'title': post.title,
            'author': post.author.username,
            'relevance_score': getattr(post, 'relevance_score', 0),
            'created_at': post.created_at
        } for post in related_posts]

        # 动态Prefetch视图
        class DynamicPrefetchView(generics.ListAPIView):
            """支持动态Prefetch的视图"""
            serializer_class = ComplexPostSerializer

            def get_queryset(self):
        """根据请求动态构建prefetch"""
        prefetch_config = self._parse_prefetch_config()

        # 基础查询
        queryset = Post.objects.select_related('author', 'category')

        # 动态添加prefetch
        prefetch_list = []

        for config in prefetch_config:
            prefetch = self._build_dynamic_prefetch(config)
            if prefetch:
                prefetch_list.append(prefetch)

        if prefetch_list:
            queryset = queryset.prefetch_related(*prefetch_list)

        return queryset

            def _parse_prefetch_config(self):
        """解析prefetch配置"""
        config_param = self.request.query_params.get('prefetch', 'basic')

        # 预定义的配置
        configs = {
            'basic': [
                {'field': 'comments', 'limit': 3},
                {'field': 'tags'}
            ],
            'detailed': [
                {'field': 'comments', 'limit': 10, 'include_replies': True},
                {'field': 'tags', 'include_stats': True}
            ],
            'minimal': [
                {'field': 'tags'}
            ]
        }

        return configs.get(config_param, configs['basic'])

            def _build_dynamic_prefetch(self, config):
        """动态构建prefetch对象"""
        field = config['field']
        limit = config.get('limit')

        if field == 'comments':
            queryset = Comment.objects.select_related('author')
            if limit:
                queryset = queryset[:limit]
            if config.get('include_replies'):
                queryset = queryset.prefetch_related('replies')
            return Prefetch('comments', queryset=queryset, to_attr=f'dynamic_{field}')

        elif field == 'tags':
            queryset = Tag.objects.all()
            if config.get('include_stats'):
                queryset = queryset.annotate(post_count=Count('posts'))
            return Prefetch('tags', queryset=queryset, to_attr=f'dynamic_{field}')

        return None

        # API使用示例:

        # 1. 复杂预取查询
        # GET /api/posts/complex-prefetch/?popular_only=true&include_stats=true&category=tech

        # 2. 动态prefetch
        # GET /api/posts/dynamic-prefetch/?prefetch=detailed

        # 3. 自定义配置
        # GET /api/posts/dynamic-prefetch/?prefetch=minimal

        # 响应示例:
        # {
        #     "count": 10,
        #     "results": [
        #         {
        #             "id": 1,
        #             "title": "Django性能优化",
        #             "content": "...",
        #             "author_info": {
        #                 "id": 5,
        #                 "username": "admin",
        #                 "total_posts": 15,
        #                 "engagement_rate": 0.85
        #             },
        #             "comments_analysis": {
        #                 "total_count": 25,
        #                 "filtered_count": 5,
        #                 "comments": [
        #                     {
        #                         "id": 100,
        #                         "content": "很有用的文章",
        #                         "author": "user1",
        #                         "likes": 12,
        #                         "replies": 3,
        #                         "is_recent": true
        #                     }
        #                 ]
        #             },
        #             "tags_insights": {
        #                 "total_tags": 4,
        #                 "featured_tags": [
        #                     {
        #                         "id": 1,
        #                         "name": "Django",
        #                         "posts_count": 45,
        #                         "avg_rating": 4.3,
        #                         "recent_posts": 8,
        #                         "trending_score": 12.5
        #                     }
        #                 ]
        #             },
        #             "related_content": [
        #                 {
        #                     "id": 2,
        #                     "title": "Django最佳实践",
        #                     "author": "expert",
        #                     "relevance_score": 8.7
        #                 }
        #             ]
        #         }
        #     ]
        # }
        ---

03.自定义预加载策略
    a.基于业务逻辑的预加载
        根据具体的业务需求设计自定义的预加载策略,平衡性能和功能需求。
        ---
        from django.db.models import Prefetch, Count, Avg, Q, F
        from django.db.models.functions import Coalesce
        from django.core.cache import cache
        from django.utils.functional import cached_property

        class BusinessLogicPrefetch:
            """基于业务逻辑的预加载策略"""

            @staticmethod
            def get_queryset_for_user_view(user, limit=20):
        """用户视图的文章查询集"""
        if user.is_authenticated:
            # 认证用户:显示所有文章 + 用户互动
            return Post.objects.select_related('author').prefetch_related(
                Prefetch(
                    'comments',
                    queryset=Comment.objects.select_related('author').annotate(
                        is_from_user=Count('id', filter=Q(author=user)),
                        user_likes=Count('likes', filter=Q(likes__user=user))
                    ),
                    to_attr='user_comments'
                ),
                Prefetch(
                    'tags',
                    queryset=Tag.objects.annotate(
                        user_follows=Count('followers', filter=Q(followers=user))
                    ),
                    to_attr='user_tags'
                )
            )
        else:
            # 匿名用户:只显示公开信息
            return Post.objects.select_related('author').prefetch_related(
                Prefetch(
                    'comments',
                    queryset=Comment.objects.select_related('author')[:5],
                    to_attr='public_comments'
                ),
                'tags'
            )[:limit]

            @staticmethod
            def get_queryset_for_admin_view(status_filter=None):
        """管理员视图的文章查询集"""
        queryset = Post.objects.select_related('author').prefetch_related(
            # 预加载所有评论,包含统计信息
            Prefetch(
                'comments',
                queryset=Comment.objects.select_related('author').annotate(
                    spam_score=Count('reports', filter=Q(reports__type='spam')),
                    pending_moderation=Count('id', filter=Q(status='pending'))
                ),
                to_attr='admin_comments'
            ),
            # 预加载标签和分类
            'tags',
            'category'
        )

        if status_filter:
            queryset = queryset.filter(status=status_filter)

        return queryset

            @staticmethod
            def get_queryset_for_analytics_view(time_range=30):
        """分析视图的文章查询集"""
        from datetime import timedelta
        from django.utils import timezone

        cutoff_date = timezone.now() - timedelta(days=time_range)

        return Post.objects.prefetch_related(
            Prefetch(
                'comments',
                queryset=Comment.objects.filter(
                    created_at__gte=cutoff_date
                ).annotate(
                    sentiment_score=Avg('sentiments__score'),
                    engagement_rate=Count('likes') / Count('views', distinct=True)
                ),
                to_attr='analytics_comments'
            ),
            Prefetch(
                'tags',
                queryset=Tag.objects.annotate(
                    trending_posts=Count('posts', filter=Q(posts__created_at__gte=cutoff_date))
                ).filter(trending_posts__gt=0),
                to_attr='trending_tags'
            )
        ).filter(
            created_at__gte=cutoff_date
        )

        class SmartPrefetchMixin:
            """智能预加载混入类"""

            def get_smart_queryset(self):
        """根据上下文智能选择预加载策略"""
        request = self.request
        user = request.user

        # 基础查询
        queryset = Post.objects.all()

        # 根据用户类型选择策略
        if user.is_staff:
            return self._admin_prefetch(queryset)
        elif user.is_authenticated:
            return self._authenticated_prefetch(queryset)
        else:
            return self._anonymous_prefetch(queryset)

            def _admin_prefetch(self, queryset):
        """管理员预加载策略"""
        return queryset.select_related('author', 'category').prefetch_related(
            Prefetch(
                'comments',
                queryset=Comment.objects.select_related('author').order_by('-created_at'),
                to_attr='all_comments'
            ),
            'tags'
        )

            def _authenticated_prefetch(self, queryset):
        """认证用户预加载策略"""
        return queryset.select_related('author').prefetch_related(
            Prefetch(
                'comments',
                queryset=Comment.objects.select_related('author').order_by('-created_at')[:10],
                to_attr='recent_comments'
            ),
            Prefetch(
                'tags',
                queryset=Tag.objects.annotate(post_count=Count('posts')).order_by('-post_count'),
                to_attr='popular_tags'
            )
        )

            def _anonymous_prefetch(self, queryset):
        """匿名用户预加载策略"""
        return queryset.select_related('author').prefetch_related(
            Prefetch(
                'comments',
                queryset=Comment.objects.filter(
                    is_approved=True
                ).select_related('author')[:5],
                to_attr='approved_comments'
            ),
            'tags'
        )

        # 缓存增强的预加载策略
        class CacheEnhancedPrefetch:
            """缓存增强的预加载"""

            @staticmethod
            def get_queryset_with_cache(cache_key_prefix, cache_timeout=300):
        """带缓存的预加载查询集"""
        def cached_queryset_factory():
            return Post.objects.select_related('author').prefetch_related(
                Prefetch(
                    'comments',
                    queryset=Comment.objects.select_related('author').order_by('-created_at')[:3],
                    to_attr='cached_comments'
                ),
                'tags'
            )

        # 尝试从缓存获取
        cache_key = f"{cache_key_prefix}:latest_posts"
        cached_data = cache.get(cache_key)

        if cached_data:
            # 返回缓存的查询结果
            return Post.objects.filter(id__in=[item['id'] for item in cached_data])

        # 执行查询并缓存结果
        queryset = cached_queryset_factory()

        # 预热缓存
        posts = list(queryset[:10])  # 限制缓存数量
        cache.set(cache_key, posts, cache_timeout)

        return queryset.filter(id__in=[post.id for post in posts])

        # 视图实现
        class SmartPostListView(SmartPrefetchMixin, generics.ListAPIView):
            """智能预加载的文章列表视图"""
            serializer_class = PostWithSmartDataSerializer

            def get_queryset(self):
        """使用智能预加载"""
        return self.get_smart_queryset()

            def get_serializer_context(self):
        """传递用户上下文"""
        context = super().get_serializer_context()
        context['user'] = self.request.user
        context['view_type'] = 'smart_list'
        return context

        class AdminPostListView(generics.ListAPIView):
            """管理员文章列表视图"""
            serializer_class = AdminPostSerializer
            permission_classes = [IsAdminUser]

            def get_queryset(self):
        """使用管理员预加载策略"""
        return BusinessLogicPrefetch.get_queryset_for_admin_view(
            status_filter=self.request.query_params.get('status')
        )

        class AnalyticsPostListView(generics.ListAPIView):
            """分析用文章列表视图"""
            serializer_class = AnalyticsPostSerializer
            permission_classes = [IsAdminUser]

            def get_queryset(self):
        """使用分析预加载策略"""
        time_range = int(self.request.query_params.get('days', 30))
        return BusinessLogicPrefetch.get_queryset_for_analytics_view(time_range)

        # 自定义序列化器
        class PostWithSmartDataSerializer(serializers.ModelSerializer):
            """智能数据的文章序列化器"""
            author_info = serializers.SerializerMethodField()
            comments_data = serializers.SerializerMethodField()
            tags_data = serializers.SerializerMethodField()
            user_interaction = serializers.SerializerMethodField()

            class Meta:
        model = Post
        fields = ['id', 'title', 'content', 'author_info', 'comments_data',
                'tags_data', 'user_interaction', 'created_at']

            def get_author_info(self, obj):
        """根据预加载的数据获取作者信息"""
        return {
            'username': obj.author.username,
            'post_count': getattr(obj.author, 'post_count', 0)
        }

            def get_comments_data(self, obj):
        """根据用户类型获取评论数据"""
        user = self.context.get('user')

        if user and user.is_staff:
            # 管理员看到所有评论
            comments = getattr(obj, 'all_comments', [])
        elif user:
            # 认证用户看到的评论
            comments = getattr(obj, 'recent_comments', [])
        else:
            # 匿名用户看到的评论
            comments = getattr(obj, 'approved_comments', [])

        return [{
            'id': comment.id,
            'content': comment.content[:100],
            'author': comment.author.username,
            'created_at': comment.created_at
        } for comment in comments]

            def get_tags_data(self, obj):
        """获取标签数据"""
        if hasattr(obj, 'popular_tags'):
            tags = obj.popular_tags
        elif hasattr(obj, 'user_tags'):
            tags = obj.user_tags
        else:
            tags = obj.tags.all()

        return [{
            'name': tag.name,
            'post_count': getattr(tag, 'post_count', 0),
            'user_follows': getattr(tag, 'user_follows', 0)
        } for tag in tags]

            def get_user_interaction(self, obj):
        """用户交互信息"""
        user = self.context.get('user')
        if not user or not user.is_authenticated:
            return None

        return {
            'has_bookmarked': False,  # 需要从其他模型查询
            'view_count': 0,
            'rating': None
        }

        # API使用示例:

        # 1. 智能预加载列表
        # GET /api/posts/smart/
        # 根据用户类型自动选择最优预加载策略

        # 2. 管理员列表
        # GET /api/posts/admin/?status=published
        # 管理员专用的详细预加载

        # 3. 分析数据
        # GET /api/posts/analytics/?days=30
        # 包含分析数据的预加载

        # 4. 缓存增强的列表
        # GET /api/posts/cached/
        # 使用缓存优化性能
        ---

7.6 超链接关系

01.HyperlinkedModelSerializer基础
    a.基本超链接关系序列化
        HyperlinkedModelSerializer使用URL来表示关系,提供更好的API可发现性和HATEOAS支持。
        ---
        from rest_framework import serializers
        from rest_framework.reverse import reverse
        from django.urls import path, include
        from .models import Post, Comment, User, Category

        # 基本超链接序列化器
        class UserHyperlinkedSerializer(serializers.HyperlinkedModelSerializer):
            """用户超链接序列化器"""
            posts_url = serializers.HyperlinkedIdentityField(
                view_name='user-posts-list',
                lookup_field='username'
            )

            class Meta:
                model = User
                fields = ['url', 'id', 'username', 'email', 'first_name', 'last_name', 'posts_url']
                extra_kwargs = {
                    'url': {'view_name': 'user-detail', 'lookup_field': 'username'}
                }

        class CategoryHyperlinkedSerializer(serializers.HyperlinkedModelSerializer):
            """分类超链接序列化器"""
            posts_count = serializers.SerializerMethodField()

            class Meta:
                model = Category
                fields = ['url', 'id', 'name', 'slug', 'description', 'posts_count']
                extra_kwargs = {
                    'url': {'view_name': 'category-detail', 'lookup_field': 'slug'}
                }

            def get_posts_count(self, obj):
                return obj.posts.count()

        class PostHyperlinkedSerializer(serializers.HyperlinkedModelSerializer):
            """文章超链接序列化器"""
            author = serializers.HyperlinkedRelatedField(
                view_name='user-detail',
                lookup_field='username',
                read_only=True
            )
            category = serializers.HyperlinkedRelatedField(
                view_name='category-detail',
                lookup_field='slug',
                read_only=True
            )
            comments_url = serializers.HyperlinkedIdentityField(
                view_name='post-comments-list'
            )
            tags = serializers.HyperlinkedRelatedField(
                view_name='tag-detail',
                many=True,
                read_only=True,
                lookup_field='slug'
            )

            class Meta:
                model = Post
                fields = [
                    'url', 'id', 'title', 'content', 'author', 'category',
                    'tags', 'comments_url', 'created_at', 'updated_at'
                ]
                extra_kwargs = {
                    'url': {'view_name': 'post-detail'}
                }

        class CommentHyperlinkedSerializer(serializers.HyperlinkedModelSerializer):
            """评论超链接序列化器"""
            post = serializers.HyperlinkedRelatedField(
                view_name='post-detail',
                read_only=True
            )
            author = serializers.HyperlinkedRelatedField(
                view_name='user-detail',
                lookup_field='username',
                read_only=True
            )

            class Meta:
                model = Comment
                fields = ['url', 'id', 'post', 'author', 'content', 'created_at']
                extra_kwargs = {
                    'url': {'view_name': 'comment-detail'}
                }

        # URL配置示例
        # urls.py
        from django.urls import path
        from . import views

        urlpatterns = [
            # 用户相关URL
            path('users/', views.UserListAPIView.as_view(), name='user-list'),
            path('users/<str:username>/', views.UserDetailAPIView.as_view(), name='user-detail'),
            path('users/<str:username>/posts/', views.UserPostListAPIView.as_view(), name='user-posts-list'),

            # 分类相关URL
            path('categories/', views.CategoryListAPIView.as_view(), name='category-list'),
            path('categories/<slug:slug>/', views.CategoryDetailAPIView.as_view(), name='category-detail'),

            # 文章相关URL
            path('posts/', views.PostListAPIView.as_view(), name='post-list'),
            path('posts/<int:pk>/', views.PostDetailAPIView.as_view(), name='post-detail'),
            path('posts/<int:pk>/comments/', views.PostCommentListAPIView.as_view(), name='post-comments-list'),

            # 评论相关URL
            path('comments/', views.CommentListAPIView.as_view(), name='comment-list'),
            path('comments/<int:pk>/', views.CommentDetailAPIView.as_view(), name='comment-detail'),
        ]

        # 请求示例
        # GET /api/posts/1/
        # 响应示例:
        # {
        #     "url": "http://api.example.com/api/posts/1/",
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author": "http://api.example.com/api/users/admin/",
        #     "category": "http://api.example.com/api/categories/python/",
        #     "tags": [
        #         "http://api.example.com/api/tags/django/",
        #         "http://api.example.com/api/tags/rest/"
        #     ],
        #     "comments_url": "http://api.example.com/api/posts/1/comments/",
        #     "created_at": "2024-01-15T10:30:00Z",
        #     "updated_at": "2024-01-15T14:20:00Z"
        # }
                ---
            b.自定义超链接字段
                通过自定义HyperlinkedRelatedField实现更复杂的超链接关系处理。
                ---
        from rest_framework import serializers
        from rest_framework.reverse import reverse
        from django.urls import NoReverseMatch

        class CustomHyperlinkedRelatedField(serializers.HyperlinkedRelatedField):
            """自定义超链接关系字段"""

            def __init__(self, **kwargs):
                self.lookup_field = kwargs.pop('lookup_field', 'pk')
                self.url_kwargs = kwargs.pop('url_kwargs', {})
                super().__init__(**kwargs)

            def get_url(self, obj, view_name, request, format):
                """生成自定义URL"""
                if obj.pk is None:
                    return None

                # 构建URL参数
                kwargs = {self.lookup_field: getattr(obj, self.lookup_field)}
                kwargs.update(self.url_kwargs)

                try:
                    return reverse(view_name, kwargs=kwargs, request=request, format=format)
                except NoReverseMatch:
                    return None

            def to_representation(self, value):
                """自定义表示方式"""
                url = super().to_representation(value)
                if url:
                    return {
                        'url': url,
                        'type': value.__class__.__name__.lower(),
                        'id': getattr(value, self.lookup_field)
                    }
                return None

        class PostWithCustomLinksSerializer(serializers.HyperlinkedModelSerializer):
            """带自定义链接的文章序列化器"""
            author = CustomHyperlinkedRelatedField(
                view_name='user-detail',
                lookup_field='username',
                read_only=True
            )
            category = CustomHyperlinkedRelatedField(
                view_name='category-detail',
                lookup_field='slug',
                read_only=True
            )

            # 自定义操作链接
            like_url = serializers.SerializerMethodField()
            share_url = serializers.SerializerMethodField()
            report_url = serializers.SerializerMethodField()

            class Meta:
                model = Post
                fields = [
                    'url', 'id', 'title', 'content', 'author', 'category',
                    'like_url', 'share_url', 'report_url', 'created_at'
                ]
                extra_kwargs = {
                    'url': {'view_name': 'post-detail'}
                }

            def get_like_url(self, obj):
                request = self.context.get('request')
                if request:
                    return reverse('post-like', kwargs={'pk': obj.pk}, request=request)
                return None

            def get_share_url(self, obj):
                request = self.context.get('request')
                if request:
                    return reverse('post-share', kwargs={'pk': obj.pk}, request=request)
                return None

            def get_report_url(self, obj):
                request = self.context.get('request')
                if request:
                    return reverse('post-report', kwargs={'pk': obj.pk}, request=request)
                return None

        # 响应示例:
        # {
        #     "url": "http://api.example.com/api/posts/1/",
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author": {
        #         "url": "http://api.example.com/api/users/admin/",
        #         "type": "user",
        #         "id": "admin"
        #     },
        #     "category": {
        #         "url": "http://api.example.com/api/categories/python/",
        #         "type": "category",
        #         "id": "python"
        #     },
        #     "like_url": "http://api.example.com/api/posts/1/like/",
        #     "share_url": "http://api.example.com/api/posts/1/share/",
        #     "report_url": "http://api.example.com/api/posts/1/report/",
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---

02.HyperlinkedRelatedField使用
    a.读写超链接关系
        实现既可以读取又可以写入的超链接关系字段。
        ---
        from rest_framework import serializers
        from rest_framework.reverse import reverse
        from django.urls import NoReverseMatch

        class ReadWriteHyperlinkedRelatedField(serializers.HyperlinkedRelatedField):
            """可读写超链接关系字段"""

            def to_internal_value(self, data):
                """将URL转换为模型实例"""
                try:
                    # 如果是字符串,可能是直接值(如用户名或slug)
                    if isinstance(data, str):
                        if self.lookup_field == 'username':
                            return User.objects.get(username=data)
                        elif self.lookup_field == 'slug':
                            # 假设有slug字段
                            if hasattr(self.queryset.model, 'slug'):
                                return self.queryset.model.objects.get(slug=data)
                            else:
                                raise serializers.ValidationError("该模型不支持slug查找")
                        else:
                            return self.queryset.get(pk=data)

                    # 如果是URL,解析URL获取lookup值
                    request = self.context.get('request')
                    if request and data.startswith('http'):
                        # 移除基础URL,只保留路径部分
                        base_url = request.build_absolute_uri('/')
                        if data.startswith(base_url):
                            path = data[len(base_url)-1:]
                            # 解析路径获取lookup值
                            parts = path.strip('/').split('/')
                            if len(parts) >= 2:
                                lookup_value = parts[-1]
                                return self.queryset.get(**{self.lookup_field: lookup_value})

                    raise serializers.ValidationError(f"无法解析URL: {data}")

                except (User.DoesNotExist, self.queryset.model.DoesNotExist):
                    raise serializers.ValidationError("未找到相关对象")
                except Exception as e:
                    raise serializers.ValidationError(f"解析失败: {str(e)}")

            def to_representation(self, value):
                """将模型实例转换为URL"""
                request = self.context.get('request')
                if not request:
                    return None

                try:
                    lookup_value = getattr(value, self.lookup_field)
                    if lookup_value is None:
                        return None

                    # 构建URL
                    return reverse(self.view_name, kwargs={self.lookup_field: lookup_value}, request=request)
                except NoReverseMatch:
                    return None

        class PostReadWriteHyperlinkedSerializer(serializers.HyperlinkedModelSerializer):
            """支持读写超链接的文章序列化器"""
            author = ReadWriteHyperlinkedRelatedField(
                view_name='user-detail',
                lookup_field='username',
                queryset=User.objects.all()
            )
            category = ReadWriteHyperlinkedRelatedField(
                view_name='category-detail',
                lookup_field='slug',
                queryset=Category.objects.all(),
                required=False,
                allow_null=True
            )
            tags = ReadWriteHyperlinkedRelatedField(
                view_name='tag-detail',
                lookup_field='slug',
                many=True,
                queryset=Tag.objects.all(),
                required=False
            )

            class Meta:
                model = Post
                fields = [
                    'url', 'id', 'title', 'content', 'author', 'category',
                    'tags', 'created_at', 'updated_at'
                ]
                extra_kwargs = {
                    'url': {'view_name': 'post-detail'}
                }

            def validate_author(self, value):
                """验证作者"""
                request = self.context.get('request')
                if request and request.user.is_authenticated:
                    # 检查是否有权限为这个用户创建文章
                    if not request.user.is_staff and value != request.user:
                        raise serializers.ValidationError("只能为自己创建文章")
                return value

        # 创建请求示例:
        # POST /api/posts/
        # {
        #     "title": "新文章",
        #     "content": "文章内容...",
        #     "author": "admin",  # 直接使用用户名
        #     "category": "python",  # 直接使用分类slug
        #     "tags": ["django", "rest"]  # 直接使用标签slug
        # }

        # 或者使用URL:
        # {
        #     "title": "新文章",
        #     "content": "文章内容...",
        #     "author": "http://api.example.com/api/users/admin/",
        #     "category": "http://api.example.com/api/categories/python/",
        #     "tags": [
        #         "http://api.example.com/api/tags/django/",
        #         "http://api.example.com/api/tags/rest/"
        #     ]
        # }
        ---
    b.多层级超链接关系
        处理嵌套的超链接关系,支持深层次的资源关联。
        ---
        from rest_framework import serializers
        from rest_framework.reverse import reverse

        class NestedHyperlinkedIdentityField(serializers.HyperlinkedIdentityField):
            """嵌套超链接身份字段"""

            def __init__(self, view_name=None, **kwargs):
                self.parent_lookup_field = kwargs.pop('parent_lookup_field', None)
                self.nested_kwargs = kwargs.pop('nested_kwargs', {})
                super().__init__(view_name, **kwargs)

            def get_url(self, obj, view_name, request, format):
                """生成嵌套URL"""
                if obj.pk is None:
                    return None

                kwargs = {'pk': obj.pk}

                # 添加父级lookup参数
                if self.parent_lookup_field:
                    parent_field = self.parent_lookup_field.replace('_set', '')
                    if hasattr(obj, parent_field):
                        parent_obj = getattr(obj, parent_field)
                        if hasattr(parent_obj, 'pk'):
                            kwargs[f'{parent_field}_id'] = parent_obj.pk

                # 添加嵌套参数
                kwargs.update(self.nested_kwargs)

                try:
                    return reverse(view_name, kwargs=kwargs, request=request, format=format)
                except NoReverseMatch:
                    return None

        class CommentWithNestedLinksSerializer(serializers.HyperlinkedModelSerializer):
            """带嵌套链接的评论序列化器"""
            post = serializers.HyperlinkedRelatedField(
                view_name='post-detail',
                read_only=True
            )
            author = serializers.HyperlinkedRelatedField(
                view_name='user-detail',
                lookup_field='username',
                read_only=True
            )

            # 嵌套操作链接
            reply_url = NestedHyperlinkedIdentityField(
                view_name='comment-reply',
                parent_lookup_field='post'
            )
            like_url = NestedHyperlinkedIdentityField(
                view_name='comment-like',
                parent_lookup_field='post'
            )

            class Meta:
                model = Comment
                fields = [
                    'url', 'id', 'post', 'author', 'content',
                    'reply_url', 'like_url', 'created_at'
                ]
                extra_kwargs = {
                    'url': {'view_name': 'comment-detail'}
                }

        # 复杂嵌套关系的序列化器
        class ComplexPostHyperlinkedSerializer(serializers.HyperlinkedModelSerializer):
            """复杂嵌套超链接关系序列化器"""
            author = serializers.HyperlinkedRelatedField(
                view_name='user-detail',
                lookup_field='username',
                read_only=True
            )
            category = serializers.HyperlinkedRelatedField(
                view_name='category-detail',
                lookup_field='slug',
                read_only=True
            )

            # 嵌套资源URL
            comments_url = NestedHyperlinkedIdentityField(
                view_name='post-comments-list',
                parent_lookup_field='author'
            )
            tags_url = NestedHyperlinkedIdentityField(
                view_name='post-tags-list',
                parent_lookup_field='category'
            )

            # 操作链接
            edit_url = serializers.SerializerMethodField()
            delete_url = serializers.SerializerMethodField()
            statistics_url = NestedHyperlinkedIdentityField(
                view_name='post-statistics',
                parent_lookup_field='author'
            )

            class Meta:
                model = Post
                fields = [
                    'url', 'id', 'title', 'content', 'author', 'category',
                    'comments_url', 'tags_url', 'edit_url', 'delete_url',
                    'statistics_url', 'created_at', 'updated_at'
                ]
                extra_kwargs = {
                    'url': {'view_name': 'post-detail'}
                }

            def get_edit_url(self, obj):
                """获取编辑URL"""
                request = self.context.get('request')
                if request and request.user == obj.author:
                    return reverse('post-edit', kwargs={'pk': obj.pk}, request=request)
                return None

            def get_delete_url(self, obj):
                """获取删除URL"""
                request = self.context.get('request')
                if request and (request.user == obj.author or request.user.is_staff):
                    return reverse('post-delete', kwargs={'pk': obj.pk}, request=request)
                return None

        # 多层级嵌套响应示例:
        # {
        #     "url": "http://api.example.com/api/posts/1/",
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author": "http://api.example.com/api/users/admin/",
        #     "category": "http://api.example.com/api/categories/python/",
        #     "comments_url": "http://api.example.com/api/posts/1/comments/",
        #     "tags_url": "http://api.example.com/api/posts/1/tags/",
        #     "edit_url": "http://api.example.com/api/posts/1/edit/",
        #     "delete_url": "http://api.example.com/api/posts/1/delete/",
        #     "statistics_url": "http://api.example.com/api/posts/1/statistics/",
        #     "created_at": "2024-01-15T10:30:00Z",
        #     "updated_at": "2024-01-15T14:20:00Z"
        # }
        ---

03.URL反向解析
    a.自定义URL配置
        灵活的URL配置支持各种超链接关系的反向解析。
        ---
        from django.urls import path, re_path, include
        from . import views

        # 超链接URL配置
        urlpatterns = [
            # 用户资源
            path('users/', views.UserListAPIView.as_view(), name='user-list'),
            path('users/<str:username>/', views.UserDetailAPIView.as_view(), name='user-detail'),
            path('users/<str:username>/profile/', views.UserProfileView.as_view(), name='user-profile'),
            path('users/<str:username>/posts/', views.UserPostListView.as_view(), name='user-posts-list'),
            path('users/<str:username>/comments/', views.UserCommentListView.as_view(), name='user-comments-list'),

            # 分类资源
            path('categories/', views.CategoryListAPIView.as_view(), name='category-list'),
            path('categories/<slug:slug>/', views.CategoryDetailAPIView.as_view(), name='category-detail'),
            path('categories/<slug:slug>/posts/', views.CategoryPostListView.as_view(), name='category-posts-list'),
            path('categories/<slug:slug>/statistics/', views.CategoryStatisticsView.as_view(), name='category-statistics'),

            # 标签资源
            path('tags/', views.TagListAPIView.as_view(), name='tag-list'),
            path('tags/<slug:slug>/', views.TagDetailAPIView.as_view(), name='tag-detail'),
            path('tags/<slug:slug>/posts/', views.TagPostListView.as_view(), name='tag-posts-list'),

            # 文章资源(主要资源)
            path('posts/', views.PostListAPIView.as_view(), name='post-list'),
            path('posts/<int:pk>/', views.PostDetailAPIView.as_view(), name='post-detail'),

            # 文章相关操作
            path('posts/<int:pk>/edit/', views.PostEditView.as_view(), name='post-edit'),
            path('posts/<int:pk>/delete/', views.PostDeleteView.as_view(), name='post-delete'),
            path('posts/<int:pk>/like/', views.PostLikeView.as_view(), name='post-like'),
            path('posts/<int:pk>/unlike/', views.PostUnlikeView.as_view(), name='post-unlike'),
            path('posts/<int:pk>/share/', views.PostShareView.as_view(), name='post-share'),
            path('posts/<int:pk>/report/', views.PostReportView.as_view(), name='post-report'),
            path('posts/<int:pk>/statistics/', views.PostStatisticsView.as_view(), name='post-statistics'),

            # 文章的嵌套资源
            path('posts/<int:pk>/comments/', views.PostCommentListView.as_view(), name='post-comments-list'),
            path('posts/<int:pk>/comments/new/', views.PostCommentCreateView.as_view(), name='post-comment-create'),
            path('posts/<int:pk>/tags/', views.PostTagListView.as_view(), name='post-tags-list'),
            path('posts/<int:pk>/tags/add/', views.PostTagAddView.as_view(), name='post-tag-add'),

            # 评论资源
            path('comments/', views.CommentListAPIView.as_view(), name='comment-list'),
            path('comments/<int:pk>/', views.CommentDetailAPIView.as_view(), name='comment-detail'),
            path('comments/<int:pk>/reply/', views.CommentReplyView.as_view(), name='comment-reply'),
            path('comments/<int:pk>/like/', views.CommentLikeView.as_view(), name='comment-like'),
            path('comments/<int:pk>/edit/', views.CommentEditView.as_view(), name='comment-edit'),
            path('comments/<int:pk>/delete/', views.CommentDeleteView.as_view(), name='comment-delete'),
        ]

        # API版本化支持
        api_v1_patterns = [
            path('', include([
                path('v1/', include(urlpatterns)),
            ]))
        ]

        urlpatterns += [
            path('api/', include(api_v1_patterns)),
        ]

        # 自置URL反向解析工具
        class URLHelper:
            """URL反向解析工具"""

            @staticmethod
            def get_resource_url(resource_type, lookup_value, request=None, version='v1'):
                """获取资源URL"""
                view_names = {
                    'user': f'api:{version}:user-detail',
                    'post': f'api:{version}:post-detail',
                    'comment': f'api:{version}:comment-detail',
                    'category': f'api:{version}:category-detail',
                    'tag': f'api:{version}:tag-detail'
                }

                view_name = view_names.get(resource_type)
                if not view_name:
                    return None

                lookup_fields = {
                    'user': 'username',
                    'category': 'slug',
                    'tag': 'slug',
                    'post': 'pk',
                    'comment': 'pk'
                }

                lookup_field = lookup_fields.get(resource_type, 'pk')

                try:
                    return reverse(view_name, kwargs={lookup_field: lookup_value}, request=request)
                except NoReverseMatch:
                    return None

            @staticmethod
            def get_action_url(resource_type, resource_id, action, request=None, version='v1'):
                """获取操作URL"""
                action_views = {
                    'post': {
                        'edit': f'api:{version}:post-edit',
                        'delete': f'api:{version}:post-delete',
                        'like': f'api:{version}:post-like',
                        'unlike': f'api:{version}:post-unlike',
                        'share': f'api:{version}:post-share',
                        'report': f'api:{version}:post-report',
                        'statistics': f'api:{version}:post-statistics'
                    },
                    'comment': {
                        'reply': f'api:{version}:comment-reply',
                        'like': f'api:{version}:comment-like',
                        'edit': f'api:{version}:comment-edit',
                        'delete': f'api:{version}:comment-delete'
                    }
                }

                if resource_type not in action_views or action not in action_views[resource_type]:
                    return None

                view_name = action_views[resource_type][action]
                return reverse(view_name, kwargs={'pk': resource_id}, request=request)

            @staticmethod
            def get_nested_url(parent_type, parent_id, child_resource, request=None, version='v1'):
                """获取嵌套资源URL"""
                nested_views = {
                    'user': {
                        'posts': f'api:{version}:user-posts-list',
                        'comments': f'api:{version}:user-comments-list'
                    },
                    'post': {
                        'comments': f'api:{version}:post-comments-list',
                        'tags': f'api:{version}:post-tags-list'
                    },
                    'category': {
                        'posts': f'api:{version}:category-posts-list'
                    },
                    'tag': {
                        'posts': f'api:{version}:tag-posts-list'
                    }
                }

                if parent_type not in nested_views or child_resource not in nested_views[parent_type]:
                    return None

                view_name = nested_views[parent_type][child_resource]

                # 确定lookup字段
                lookup_fields = {'user': 'username'}
                lookup_field = lookup_fields.get(parent_type, 'pk')

                return reverse(view_name, kwargs={lookup_field: parent_id}, request=request)
        ---
    b.动态URL生成
        根据上下文和权限动态生成URL,提供更精细的API控制。
        ---
        from rest_framework import serializers
        from rest_framework.reverse import reverse
        from django.core.exceptions import PermissionDenied

        class DynamicURLField(serializers.SerializerMethodField):
            """动态URL字段"""

            def __init__(self, view_name=None, lookup_field='pk', **kwargs):
                self.view_name = view_name
                self.lookup_field = lookup_field
                self.permission_check = kwargs.pop('permission_check', None)
                super().__init__(**kwargs)

            def to_representation(self, value):
                request = self.context.get('request')
                if not request:
                    return None

                # 权限检查
                if self.permission_check and not self.permission_check(value, request.user):
                    return None

                try:
                    lookup_value = getattr(value, self.lookup_field)
                    return reverse(self.view_name, kwargs={self.lookup_field: lookup_value}, request=request)
                except (AttributeError, NoReverseMatch):
                    return None

        class ConditionalHyperlinkedModelSerializer(serializers.HyperlinkedModelSerializer):
            """条件性超链接模型序列化器"""

            def get_field_names(self, declared_fields, info):
                """根据权限动态选择字段"""
                field_names = super().get_field_names(declared_fields, info)
                request = self.context.get('request')

                if request and request.user:
                    # 如果不是管理员,隐藏某些字段
                    if not request.user.is_staff:
                        field_names = [name for name in field_names if not name.startswith('admin_')]

                return field_names

        class PostWithDynamicURLsSerializer(ConditionalHyperlinkedModelSerializer):
            """带动态URL的文章序列化器"""
            author = serializers.HyperlinkedRelatedField(
                view_name='user-detail',
                lookup_field='username',
                read_only=True
            )

            # 条件性URL
            edit_url = DynamicURLField(
                view_name='post-edit',
                permission_check=lambda obj, user: obj.author == user or user.is_staff
            )
            delete_url = DynamicURLField(
                view_name='post-delete',
                permission_check=lambda obj, user: obj.author == user or user.is_staff
            )
            moderate_url = DynamicURLField(
                view_name='post-moderate',
                permission_check=lambda obj, user: user.is_staff or user.is_moderator
            )
            feature_url = DynamicURLField(
                view_name='post-feature',
                permission_check=lambda obj, user: user.is_staff
            )

            # 用户操作URL
            like_url = serializers.SerializerMethodField()
            unlike_url = serializers.SerializerMethodField()
            bookmark_url = serializers.SerializerMethodField()

            class Meta:
                model = Post
                fields = [
                    'url', 'id', 'title', 'content', 'author',
                    'edit_url', 'delete_url', 'moderate_url', 'feature_url',
                    'like_url', 'unlike_url', 'bookmark_url',
                    'created_at', 'updated_at'
                ]
                extra_kwargs = {
                    'url': {'view_name': 'post-detail'}
                }

            def get_like_url(self, obj):
                """获取点赞URL(用户已登录且未点赞时显示)"""
                request = self.context.get('request')
                if request and request.user.is_authenticated:
                    # 检查用户是否已点赞(假设有Like模型)
                    from .models import Like
                    if not Like.objects.filter(user=request.user, post=obj).exists():
                        return reverse('post-like', kwargs={'pk': obj.pk}, request=request)
                return None

            def get_unlike_url(self, obj):
                """获取取消点赞URL(用户已登录且已点赞时显示)"""
                request = self.context.get('request')
                if request and request.user.is_authenticated:
                    from .models import Like
                    if Like.objects.filter(user=request.user, post=obj).exists():
                        return reverse('post-unlike', kwargs={'pk': obj.pk}, request=request)
                return None

            def get_bookmark_url(self, obj):
                """获取收藏URL"""
                request = self.context.get('request')
                if request and request.user.is_authenticated:
                    from .models import Bookmark
                    if Bookmark.objects.filter(user=request.user, post=obj).exists():
                        return reverse('post-unbookmark', kwargs={'pk': obj.pk}, request=request)
                    else:
                        return reverse('post-bookmark', kwargs={'pk': obj.pk}, request=request)
                return None

        # 版本化URL支持
        class VersionedHyperlinkedModelSerializer(serializers.HyperlinkedModelSerializer):
            """版本化超链接模型序列化器"""

            def __init__(self, *args, **kwargs):
                self.api_version = kwargs.pop('api_version', 'v1')
                super().__init__(*args, **kwargs)

            def build_url(self, field_name, view_name, lookup_value):
                """构建版本化URL"""
                request = self.context.get('request')
                if request:
                    # 检查请求中的API版本
                    requested_version = request.META.get('HTTP_API_VERSION', self.api_version)

                    # 尝试使用请求版本的URL
                    versioned_view_name = f"api:{requested_version}:{view_name.split(':')[-1]}"
                    try:
                        return reverse(versioned_view_name, kwargs={'pk': lookup_value}, request=request)
                    except NoReverseMatch:
                        # 回退到默认版本
                        pass

                # 使用默认版本的URL
                default_view_name = f"api:{self.api_version}:{view_name.split(':')[-1]}"
                try:
                    return reverse(default_view_name, kwargs={'pk': lookup_value}, request=request)
                except NoReverseMatch:
                    return None

        class VersionedPostSerializer(VersionedHyperlinkedModelSerializer):
            """版本化文章序列化器"""
            author = serializers.HyperlinkedRelatedField(
                view_name='user-detail',
                lookup_field='username',
                read_only=True
            )

            class Meta:
                model = Post
                fields = ['url', 'id', 'title', 'content', 'author', 'created_at']

            def build_relational_url(self, field_name, view_name, obj):
                """构建关系URL"""
                if view_name:
                    lookup_value = getattr(obj, 'pk')
                    return self.build_url(field_name, view_name, lookup_value)
                return None

        # 响应示例(根据用户权限和状态动态生成):
        # 普通用户看到的文章:
        # {
        #     "url": "http://api.example.com/api/v1/posts/1/",
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "author": "http://api.example.com/api/v1/users/admin/",
        #     "edit_url": "http://api.example.com/api/v1/posts/1/edit/",  # 如果是作者
        #     "delete_url": null,  # 如果不是作者或管理员
        #     "moderate_url": null,  # 如果不是版主
        #     "feature_url": null,  # 如果不是管理员
        #     "like_url": "http://api.example.com/api/v1/posts/1/like/",  # 如果未点赞
        #     "unlike_url": null,  # 如果已点赞
        #     "bookmark_url": "http://api.example.com/api/v1/posts/1/bookmark/",
        #     "created_at": "2024-01-15T10:30:00Z"
        # }
        ---

04.HATEOAS设计模式
    a.实现HATEOAS API
        遵循HATEOAS(Hypermedia as the Engine of Application State)原则设计API。
        ---
        from rest_framework import serializers
        from rest_framework.reverse import reverse
        from django.urls import NoReverseMatch

        class HALSerializer(serializers.HyperlinkedModelSerializer):
            """HAL(Hypertext Application Language)序列化器基类"""

            def to_representation(self, instance):
                """添加HAL格式支持"""
                data = super().to_representation(instance)
                request = self.context.get('request')

                if request:
                    # 添加_links
                    data['_links'] = self.get_hal_links(instance, request)

                    # 添加_embedded(嵌套资源)
                    embedded = self.get_hal_embedded(instance, request)
                    if embedded:
                        data['_embedded'] = embedded

                return data

            def get_hal_links(self, instance, request):
                """获取HAL链接"""
                links = {
                    'self': {
                        'href': self.get_self_url(instance, request)
                    }
                }

                # 添加关系链接
                links.update(self.get_relation_links(instance, request))

                # 添加操作链接
                links.update(self.get_action_links(instance, request))

                return links

            def get_self_url(self, instance, request):
                """获取self URL"""
                try:
                    return reverse(
                        f'{instance.__class__.__name__.lower()}-detail',
                        kwargs={'pk': instance.pk},
                        request=request
                    )
                except NoReverseMatch:
                    return None

            def get_relation_links(self, instance, request):
                """获取关系链接"""
                links = {}

                # 基于模型关系自动生成链接
                for field in instance._meta.get_fields():
                    if field.many_to_one or field.one_to_one:
                        # 外键关系
                        related_obj = getattr(instance, field.name, None)
                        if related_obj:
                            links[field.name] = {
                                'href': self.get_related_url(related_obj, request),
                                'title': str(related_obj)
                            }
                    elif field.many_to_many or field.one_to_many:
                        # 多对多或一对多关系
                        related_manager = getattr(instance, field.name, None)
                        if related_manager:
                            collection_name = field.name
                            links[collection_name] = {
                                'href': self.get_collection_url(instance, collection_name, request)
                            }

                return links

            def get_action_links(self, instance, request):
                """获取操作链接"""
                links = {}
                user = request.user if request else None

                # 根据模型和方法生成操作链接
                action_methods = [
                    ('edit', 'edit', lambda obj, user: self.can_edit(obj, user)),
                    ('delete', 'delete', lambda obj, user: self.can_delete(obj, user)),
                    ('like', 'like', lambda obj, user: user and user.is_authenticated),
                    ('share', 'share', lambda obj, user: True),
                ]

                for action, suffix, permission_check in action_methods:
                    if permission_check(instance, user):
                        view_name = f'{instance.__class__.__name__.lower()}-{suffix}'
                        try:
                            url = reverse(view_name, kwargs={'pk': instance.pk}, request=request)
                            links[action] = {'href': url}
                        except NoReverseMatch:
                            pass

                return links

            def get_hal_embedded(self, instance, request):
                """获取嵌套资源"""
                embedded = {}

                # 嵌入重要的一对一关系
                for field in instance._meta.get_fields():
                    if field.many_to_one and field.name in ['author', 'user']:
                        related_obj = getattr(instance, field.name, None)
                        if related_obj:
                            embedded[field.name] = self.serialize_related(related_obj, request)

                return embedded

            def get_related_url(self, obj, request):
                """获取相关对象URL"""
                try:
                    model_name = obj.__class__.__name__.lower()
                    return reverse(f'{model_name}-detail', kwargs={'pk': obj.pk}, request=request)
                except NoReverseMatch:
                    return None

            def get_collection_url(self, instance, collection_name, request):
                """获取集合URL"""
                try:
                    model_name = instance.__class__.__name__.lower()
                    return reverse(f'{model_name}-{collection_name}', kwargs={'pk': instance.pk}, request=request)
                except NoReverseMatch:
                    return None

            def serialize_related(self, obj, request):
                """序列化相关对象"""
                # 简单的相关对象序列化
                return {
                    'id': obj.pk,
                    'url': self.get_related_url(obj, request),
                    'title': str(obj)
                }

            def can_edit(self, obj, user):
                """检查编辑权限"""
                return user and (user == obj.author or user.is_staff)

            def can_delete(self, obj, user):
                """检查删除权限"""
                return user and (user == obj.author or user.is_staff)

        class PostHALSerializer(HALSerializer):
            """文章HAL序列化器"""

            class Meta:
                model = Post
                fields = ['id', 'title', 'content', 'created_at', 'updated_at']
                extra_kwargs = {
                    'url': {'view_name': 'post-detail'}
                }

            def get_relation_links(self, instance, request):
                """获取文章的关系链接"""
                links = super().get_relation_links(instance, request)

                # 添加自定义关系链接
                if instance.author:
                    links['author'] = {
                        'href': reverse('user-detail', kwargs={'username': instance.author.username}, request=request),
                        'title': instance.author.get_full_name() or instance.author.username,
                        'profile': {
                            'href': reverse('user-profile', kwargs={'username': instance.author.username}, request=request)
                        }
                    }

                if instance.category:
                    links['category'] = {
                        'href': reverse('category-detail', kwargs={'slug': instance.category.slug}, request=request),
                        'title': instance.category.name
                    }

                # 添加集合链接
                links['comments'] = {
                    'href': reverse('post-comments-list', kwargs={'pk': instance.pk}, request=request),
                    'title': f'评论 ({instance.comments.count()})'
                }

                if instance.tags.exists():
                    links['tags'] = {
                        'href': reverse('post-tags-list', kwargs={'pk': instance.pk}, request=request),
                        'title': f'标签 ({instance.tags.count()})'
                    }

                return links

            def get_action_links(self, instance, request):
                """获取文章的操作链接"""
                links = super().get_action_links(instance, request)
                user = request.user if request else None

                # 添加文章特定操作
                if user and user.is_authenticated:
                    # 检查是否已点赞
                    from .models import Like
                    if Like.objects.filter(user=user, post=instance).exists():
                        links['unlike'] = {
                            'href': reverse('post-unlike', kwargs={'pk': instance.pk}, request=request),
                            'title': '取消点赞',
                            'method': 'DELETE'
                        }
                    else:
                        links['like'] = {
                            'href': reverse('post-like', kwargs={'pk': instance.pk}, request=request),
                            'title': '点赞',
                            'method': 'POST'
                        }

                    # 收藏链接
                    from .models import Bookmark
                    if Bookmark.objects.filter(user=user, post=instance).exists():
                        links['unbookmark'] = {
                            'href': reverse('post-unbookmark', kwargs={'pk': instance.pk}, request=request),
                            'title': '取消收藏',
                            'method': 'DELETE'
                        }
                    else:
                        links['bookmark'] = {
                            'href': reverse('post-bookmark', kwargs={'pk': instance.pk}, request=request),
                            'title': '收藏',
                            'method': 'POST'
                        }

                # 管理员操作
                if user and user.is_staff:
                    links['feature'] = {
                        'href': reverse('post-feature', kwargs={'pk': instance.pk}, request=request),
                        'title': '设为精选',
                        'method': 'POST'
                    }

                return links

            def get_hal_embedded(self, instance, request):
                """获取嵌套资源"""
                embedded = super().get_hal_embedded(instance, request)

                # 嵌入作者信息
                if instance.author:
                    embedded['author'] = {
                        'id': instance.author.pk,
                        'username': instance.author.username,
                        'name': instance.author.get_full_name(),
                        'avatar': instance.author.profile.avatar.url if instance.author.profile.avatar else None,
                        '_links': {
                            'self': {
                                'href': reverse('user-detail', kwargs={'username': instance.author.username}, request=request)
                            },
                            'profile': {
                                'href': reverse('user-profile', kwargs={'username': instance.author.username}, request=request)
                            }
                        }
                    }

                # 嵌入最近的评论
                recent_comments = instance.comments.all()[:3]
                if recent_comments:
                    embedded['comments'] = []
                    for comment in recent_comments:
                        embedded['comments'].append({
                            'id': comment.pk,
                            'content': comment.content[:100],
                            'author': comment.author.username,
                            'created_at': comment.created_at,
                            '_links': {
                                'self': {
                                    'href': reverse('comment-detail', kwargs={'pk': comment.pk}, request=request)
                                }
                            }
                        })

                return embedded

        # HAL格式响应示例:
        # {
        #     "id": 1,
        #     "title": "Django REST Framework入门",
        #     "content": "这是一篇关于DRF的文章...",
        #     "created_at": "2024-01-15T10:30:00Z",
        #     "updated_at": "2024-01-15T14:20:00Z",
        #     "_links": {
        #         "self": {
        #             "href": "http://api.example.com/api/posts/1/"
        #         },
        #         "author": {
        #             "href": "http://api.example.com/api/users/admin/",
        #             "title": "管理员",
        #             "profile": {
        #                 "href": "http://api.example.com/api/users/admin/profile/"
        #             }
        #         },
        #         "category": {
        #             "href": "http://api.example.com/api/categories/python/",
        #             "title": "Python"
        #         },
        #         "comments": {
        #             "href": "http://api.example.com/api/posts/1/comments/",
        #             "title": "评论 (15)"
        #         },
        #         "tags": {
        #             "href": "http://api.example.com/api/posts/1/tags/",
        #             "title": "标签 (3)"
        #         },
        #         "like": {
        #             "href": "http://api.example.com/api/posts/1/like/",
        #             "title": "点赞",
        #             "method": "POST"
        #         },
        #         "bookmark": {
        #             "href": "http://api.example.com/api/posts/1/bookmark/",
        #             "title": "收藏",
        #             "method": "POST"
        #         }
        #     },
        #     "_embedded": {
        #         "author": {
        #             "id": 1,
        #             "username": "admin",
        #             "name": "管理员",
        #             "_links": {
        #                 "self": {
        #                     "href": "http://api.example.com/api/users/admin/"
        #                 }
        #             }
        #         },
        #         "comments": [
        #             {
        #                 "id": 1,
        #                 "content": "很好的文章",
        #                 "author": "user1",
        #                 "created_at": "2024-01-15T11:00:00Z",
        #                 "_links": {
        #                     "self": {
        #                         "href": "http://api.example.com/api/comments/1/"
        #                     }
        #                 }
        #             }
        #         ]
        #     }
        # }
        ---
    b.超链接导航和发现
        实现API资源的自动发现和导航功能。
        ---
        from rest_framework import serializers, views
        from rest_framework.response import Response
        from rest_framework.reverse import reverse

        class APIRootView(views.APIView):
            """API根视图,提供资源发现"""

            def get(self, request, format=None):
                """返回所有可用的API端点"""
                data = {
                    '_links': {
                        'self': {'href': reverse('api-root', request=request)},
                        'users': {
                            'href': reverse('user-list', request=request),
                            'title': '用户列表',
                            'description': '获取所有用户信息'
                        },
                        'posts': {
                            'href': reverse('post-list', request=request),
                            'title': '文章列表',
                            'description': '获取所有文章信息'
                        },
                        'categories': {
                            'href': reverse('category-list', request=request),
                            'title': '分类列表',
                            'description': '获取所有文章分类'
                        },
                        'tags': {
                            'href': reverse('tag-list', request=request),
                            'title': '标签列表',
                            'description': '获取所有文章标签'
                        },
                        'comments': {
                            'href': reverse('comment-list', request=request),
                            'title': '评论列表',
                            'description': '获取所有评论信息'
                        }
                    },
                    '_embedded': {
                        'stats': self.get_api_stats(),
                        'info': self.get_api_info()
                    }
                }

                # 根据用户权限添加额外链接
                if request.user.is_authenticated:
                    data['_links'].update({
                        'my_profile': {
                            'href': reverse('user-detail', kwargs={'username': request.user.username}, request=request),
                            'title': '我的资料'
                        },
                        'my_posts': {
                            'href': reverse('user-posts-list', kwargs={'username': request.user.username}, request=request),
                            'title': '我的文章'
                        },
                        'create_post': {
                            'href': reverse('post-create', request=request),
                            'title': '创建文章',
                            'method': 'POST'
                        }
                    })

                if request.user.is_staff:
                    data['_links'].update({
                        'admin': {
                            'href': reverse('admin-dashboard', request=request),
                            'title': '管理员面板'
                        },
                        'statistics': {
                            'href': reverse('api-statistics', request=request),
                            'title': 'API统计信息'
                        }
                    })

                return Response(data)

            def get_api_stats(self):
                """获取API统计信息"""
                from .models import Post, User, Comment, Category, Tag

                return {
                    'posts_count': Post.objects.count(),
                    'users_count': User.objects.count(),
                    'comments_count': Comment.objects.count(),
                    'categories_count': Category.objects.count(),
                    'tags_count': Tag.objects.count()
                }

            def get_api_info(self):
                """获取API信息"""
                return {
                    'version': '1.0.0',
                    'name': 'Blog API',
                    'description': '基于Django REST Framework的博客API',
                    'documentation': {
                        'href': '/api/docs/',
                        'title': 'API文档'
                    },
                    'support': {
                        'email': '[email protected]',
                        'github': 'https://github.com/example/blog-api'
                    }
                }

        class ResourceExplorerSerializer(serializers.Serializer):
            """资源探索序列化器"""

            def to_representation(self, instance):
                """为资源生成探索链接"""
                request = self.context.get('request')
                if not request:
                    return {}

                model_name = instance.__class__.__name__.lower()

                return {
                    'resource': {
                        'id': instance.pk,
                        'type': model_name,
                        'title': str(instance)
                    },
                    '_links': {
                        'self': {
                            'href': reverse(f'{model_name}-detail', kwargs={'pk': instance.pk}, request=request)
                        },
                        'collection': {
                            'href': reverse(f'{model_name}-list', request=request)
                        }
                    },
                    '_actions': self.get_available_actions(instance, request),
                    '_relations': self.get_available_relations(instance, request)
                }

            def get_available_actions(self, instance, request):
                """获取可用操作"""
                actions = []
                user = request.user

                # 通用操作
                if user and user.is_authenticated:
                    actions.extend([
                        {'name': 'like', 'method': 'POST', 'href': reverse(f'{instance.__class__.__name__.lower()}-like', kwargs={'pk': instance.pk}, request=request)},
                        {'name': 'share', 'method': 'POST', 'href': reverse(f'{instance.__class__.__name__.lower()}-share', kwargs={'pk': instance.pk}, request=request)}
                    ])

                # 权限相关操作
                if self.can_edit(instance, user):
                    actions.append({'name': 'edit', 'method': 'PUT/PATCH', 'href': reverse(f'{instance.__class__.__name__.lower()}-edit', kwargs={'pk': instance.pk}, request=request)})

                if self.can_delete(instance, user):
                    actions.append({'name': 'delete', 'method': 'DELETE', 'href': reverse(f'{instance.__class__.__name__.lower()}-detail', kwargs={'pk': instance.pk}, request=request)})

                return actions

            def get_available_relations(self, instance, request):
                """获取可用关系"""
                relations = []

                for field in instance._meta.get_fields():
                    if field.many_to_one or field.one_to_one:
                        related_obj = getattr(instance, field.name, None)
                        if related_obj:
                            relations.append({
                                'name': field.name,
                                'type': 'single',
                                'href': reverse(f'{related_obj.__class__.__name__.lower()}-detail', kwargs={'pk': related_obj.pk}, request=request),
                                'title': str(related_obj)
                            })
                    elif field.many_to_many or field.one_to_many:
                        relations.append({
                            'name': field.name,
                            'type': 'collection',
                            'href': reverse(f'{instance.__class__.__name__.lower()}-{field.name}', kwargs={'pk': instance.pk}, request=request)
                        })

                return relations

            def can_edit(self, instance, user):
                """检查编辑权限"""
                return user and (hasattr(instance, 'author') and instance.author == user or user.is_staff)

            def can_delete(self, instance, user):
                """检查删除权限"""
                return user and user.is_staff

        # 资源探索视图
        class ResourceExplorerView(views.APIView):
            """资源探索视图"""

            def get(self, request, model_name=None, pk=None):
                """探索API资源"""
                if model_name and pk:
                    # 探索特定资源
                    return self.explore_resource(request, model_name, pk)
                else:
                    # 探索所有资源类型
                    return self.explore_resources(request)

            def explore_resources(self, request):
                """探索所有资源类型"""
                available_resources = {
                    'posts': {
                        'href': reverse('post-list', request=request),
                        'description': '博客文章',
                        'fields': ['id', 'title', 'content', 'author', 'category', 'tags', 'created_at']
                    },
                    'users': {
                        'href': reverse('user-list', request=request),
                        'description': '用户信息',
                        'fields': ['id', 'username', 'email', 'first_name', 'last_name']
                    },
                    'categories': {
                        'href': reverse('category-list', request=request),
                        'description': '文章分类',
                        'fields': ['id', 'name', 'slug', 'description']
                    },
                    'tags': {
                        'href': reverse('tag-list', request=request),
                        'description': '文章标签',
                        'fields': ['id', 'name', 'slug']
                    },
                    'comments': {
                        'href': reverse('comment-list', request=request),
                        'description': '评论信息',
                        'fields': ['id', 'post', 'author', 'content', 'created_at']
                    }
                }

                return Response({
                    '_links': {
                        'self': {'href': reverse('resource-explorer', request=request)},
                        'api_root': {'href': reverse('api-root', request=request)}
                    },
                    'resources': available_resources
                })

            def explore_resource(self, request, model_name, pk):
                """探索特定资源"""
                model_map = {
                    'posts': Post,
                    'users': User,
                    'categories': Category,
                    'tags': Tag,
                    'comments': Comment
                }

                if model_name not in model_map:
                    return Response({'error': f'未知资源类型: {model_name}'}, status=404)

                model_class = model_map[model_name]
                try:
                    instance = model_class.objects.get(pk=pk)
                except model_class.DoesNotExist:
                    return Response({'error': '资源不存在'}, status=404)

                serializer = ResourceExplorerSerializer(instance, context={'request': request})
                return Response(serializer.data)

        # 响应示例 - API根节点:
        # {
        #     "_links": {
        #         "self": {"href": "http://api.example.com/api/"},
        #         "users": {
        #             "href": "http://api.example.com/api/users/",
        #             "title": "用户列表",
        #             "description": "获取所有用户信息"
        #         },
        #         "posts": {
        #             "href": "http://api.example.com/api/posts/",
        #             "title": "文章列表",
        #             "description": "获取所有文章信息"
        #         }
        #     },
        #     "_embedded": {
        #         "stats": {
        #             "posts_count": 150,
        #             "users_count": 45,
        #             "comments_count": 1200
        #         },
        #         "info": {
        #             "version": "1.0.0",
        #             "name": "Blog API"
        #         }
        #     }
        # }

        # 响应示例 - 资源探索:
        # {
        #     "resource": {
        #         "id": 1,
        #         "type": "post",
        #         "title": "Django REST Framework入门"
        #     },
        #     "_links": {
        #         "self": {"href": "http://api.example.com/api/posts/1/"},
        #         "collection": {"href": "http://api.example.com/api/posts/"}
        #     },
        #     "_actions": [
        #         {
        #             "name": "like",
        #             "method": "POST",
        #             "href": "http://api.example.com/api/posts/1/like/"
        #         },
        #         {
        #             "name": "edit",
        #             "method": "PUT/PATCH",
        #             "href": "http://api.example.com/api/posts/1/edit/"
        #         }
        #     ],
        #     "_relations": [
        #         {
        #             "name": "author",
        #             "type": "single",
        #             "href": "http://api.example.com/api/users/admin/",
        #             "title": "管理员"
        #         },
        #         {
        #             "name": "comments",
        #             "type": "collection",
        #             "href": "http://api.example.com/api/posts/1/comments/"
        #         }
        #     ]
        # }
        ---

8 API文档和测试

8.1 自动文档生成

01.drf-spectacular集成
    a.基础配置和集成
        drf-spectacular是DRF的现代化OpenAPI/Swagger文档生成工具,提供强大的API文档自动化功能。
        ---
        # settings.py - 基础配置
        INSTALLED_APPS = [
            # ... 其他应用
            'rest_framework',
            'drf_spectacular',  # 添加drf-spectacular
        ]

        # DRF配置
        REST_FRAMEWORK = {
            'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
            'DEFAULT_AUTHENTICATION_CLASSES': [
                'rest_framework.authentication.SessionAuthentication',
                'rest_framework.authentication.TokenAuthentication',
                'rest_framework_simplejwt.authentication.JWTAuthentication',
            ],
            'DEFAULT_PERMISSION_CLASSES': [
                'rest_framework.permissions.IsAuthenticated',
            ],
        }

        # Spectacular配置
        SPECTACULAR_SETTINGS = {
            'TITLE': 'Django REST Framework API',
            'DESCRIPTION': '一个功能完整的DRF API示例',
            'VERSION': '1.0.0',
            'SERVE_INCLUDE_SCHEMA': False,
            'COMPONENT_SPLIT_REQUEST': True,
            'COMPONENT_NO_READ_ONLY_REQUIRED': True,
            'SCHEMA_PATH_PREFIX': '/api/',
            'SERVERS': [
                {'url': 'http://localhost:8000', 'description': '开发服务器'},
                {'url': 'https://api.example.com', 'description': '生产服务器'},
            ],
            'TAGS': [
                {'name': '用户管理', 'description': '用户相关的操作'},
                {'name': '文章管理', 'description': '文章相关的操作'},
                {'name': '评论管理', 'description': '评论相关的操作'},
            ],
            'PREPROCESSING_HOOKS': [
                'drf_spectacular.hooks.preprocess_exclude_path_v2_prefix',
                'myapp.hooks.remove_unnecessary_fields',
            ],
            'POSTPROCESSING_HOOKS': [
                'drf_spectacular.hooks.postprocess_schema_enums',
            ],
            'ENUM_NAME_OVERRIDES': {},
            'GENERIC_ADDITIONAL_PROPERTIES': None,
            'SWAGGER_UI_SETTINGS': {
                'deepLinking': True,
                'persistAuthorization': True,
                'displayOperationId': True,
            },
            'REDOC_UI_SETTINGS': {
                'hideDownloadButton': True,
                'hideHostname': True,
                'noAutoAuth': False,
            },
            'OPERATION_ID_EXCLUDE_REMOVALS': [
                '^list$',
                '^create$',
                '^retrieve$',
                '^update$',
                '^partial_update$',
                '^destroy$',
            ],
        }

        # urls.py - 路由配置
        from django.urls import path, include
        from drf_spectacular.views import (
            SpectacularAPIView,
            SpectacularRedocView,
            SpectacularSwaggerView,
        )

        urlpatterns = [
            # API路由
            path('api/', include('myapp.urls')),

            # 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'),
        ]

        # 自定义预处理钩子
        # myapp/hooks.py
        from drf_spectacular.utils import OpenApiParameter
        drf_spectacular.hooks

        def remove_unnecessary_fields(result, generator, request, public):
            """移除不必要的字段"""
            # 移除内部使用的字段
            for path, path_item in result['paths'].items():
                for method, operation in path_item.items():
                    if 'parameters' in operation:
                        # 移除不必要的查询参数
                        operation['parameters'] = [
                            p for p in operation['parameters']
                            if p['name'] not in ['internal_field', 'debug_mode']
                        ]

            return result

        # 序列化器扩展
        from drf_spectacular.utils import extend_schema_field
        from drf_spectacular.types import OpenApiTypes

        class UserSchemaExtension(drf_spectacular.extensions.OpenApiSerializerExtension):
            """自定义用户序列化器扩展"""
            target_class = 'myapp.serializers.UserSerializer'

            def map_serializer(self, auto_schema, direction):
                """自定义序列化器映射"""
                return {
                    'properties': {
                        'id': {'type': 'integer', 'readOnly': True},
                        'username': {'type': 'string', 'minLength': 3, 'maxLength': 150},
                        'email': {'type': 'string', 'format': 'email'},
                        'is_active': {'type': 'boolean'},
                        'date_joined': {'type': 'string', 'format': 'date-time', 'readOnly': True},
                        'last_login': {'type': 'string', 'format': 'date-time', 'readOnly': True},
                    },
                    'required': ['username', 'email'],
                }

        @extend_schema_field(OpenApiTypes.DATETIME)
        class CreatedAtField(serializers.DateTimeField):
            """带自定义字段扩展的日期时间字段"""
            pass
        ---
    b.高级配置选项
        配置高级功能如自定义UI主题、多API版本支持和安全认证配置。
        ---
        # settings.py - 高级配置
        SPECTACULAR_SETTINGS = {
            # 基础配置
            'TITLE': 'Django REST Framework API',
            'DESCRIPTION': '''
            这是一个功能完整的DRF API示例,包含以下特性:

            - 用户认证和授权
            - 文章管理
            - 评论系统
            - 标签分类
            ''',
            'VERSION': '2.0.0',
            'CONTACT': {
                'name': 'API Support',
                'email': '[email protected]',
                'url': 'https://support.example.com',
            },
            'LICENSE': {
                'name': 'MIT License',
                'url': 'https://opensource.org/licenses/MIT',
            },
            'EXTERNAL_DOCS': {
                'description': '详细文档',
                'url': 'https://docs.example.com',
            },

            # 服务器配置
            'SERVERS': [
                {
                    'url': 'http://localhost:8000',
                    'description': '开发环境',
                    'variables': {
                        'port': {'default': '8000', 'enum': ['8000', '8001', '8002']},
                    }
                },
                {
                    'url': 'https://staging-api.example.com',
                    'description': '测试环境',
                },
                {
                    'url': 'https://api.example.com',
                    'description': '生产环境',
                },
            ],

            # 认证配置
            'SECURITY': [
                {
                    'bearerAuth': [],
                },
                {
                    'cookieAuth': [],
                },
            ],
            'SECURITY_DEFINITIONS': {
                'bearerAuth': {
                    'type': 'http',
                    'scheme': 'bearer',
                    'bearerFormat': 'JWT',
                },
                'cookieAuth': {
                    'type': 'apiKey',
                    'in': 'cookie',
                    'name': 'sessionid',
                },
            },

            # 分页配置
            'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
            'PAGE_SIZE': 20,

            # 自定义配置
            'SWAGGER_UI_DIST': 'sidoc',  # 使用第三方UI
            'SWAGGER_UI_FAVICON_HREF': 'https://example.com/favicon.ico',
            'REDOC_DIST': 'sidoc',
            'SWAGGER_UI_SETTINGS': {
                'deepLinking': True,
                'persistAuthorization': True,
                'displayRequestDuration': True,
                'filter': True,
                'showExtensions': True,
                'showCommonExtensions': True,
                'tryItOutEnabled': True,
                'requestSnippetsEnabled': True,
                'oauth2RedirectUrl': 'http://localhost:8000/api/docs/oauth2-redirect/',
            },
            'REDOC_UI_SETTINGS': {
                'hideDownloadButton': False,
                'hideHostname': False,
                'noAutoAuth': False,
                'pathInMiddlePanel': True,
                'hideLoading': True,
                'expandResponses': '200,201',
                'requiredPropsFirst': True,
            },
        }

        # 自定义API版本支持
        # myapp/views.py
        from drf_spectacular.utils import extend_schema, OpenApiParameter
        from drf_spectacular.types import OpenApiTypes

        class VersionedPostViewSet(viewsets.ModelViewSet):
            """支持版本的文章视图集"""
            queryset = Post.objects.all()
            serializer_class = PostSerializer

            def get_serializer_class(self):
                """根据版本返回不同的序列化器"""
                if self.request.version == 'v1':
                    return PostV1Serializer
                elif self.request.version == 'v2':
                    return PostV2Serializer
                return PostSerializer

            @extend_schema(
                summary='获取文章列表',
                description='获取所有文章的列表,支持分页和过滤',
                parameters=[
                    OpenApiParameter(
                        name='status',
                        description='文章状态过滤',
                        required=False,
                        type=OpenApiTypes.STR,
                        enum=['draft', 'published', 'archived']
                    ),
                    OpenApiParameter(
                        name='search',
                        description='搜索关键词',
                        required=False,
                        type=OpenApiTypes.STR
                    ),
                    OpenApiParameter(
                        name='ordering',
                        description='排序字段',
                        required=False,
                        type=OpenApiTypes.STR,
                        enum=['created_at', '-created_at', 'title', '-title']
                    ),
                ],
                responses={200: PostSerializer(many=True)},
                tags=['文章管理']
            )
            def list(self, request, *args, **kwargs):
                """自定义列表方法"""
                return super().list(request, *args, **kwargs)

            @extend_schema(
                summary='创建文章',
                description='创建一篇新文章',
                request=PostSerializer,
                responses={201: PostSerializer},
                tags=['文章管理']
            )
            def create(self, request, *args, **kwargs):
                """创建文章"""
                return super().create(request, *args, **kwargs)

        # 自定义错误处理
        # myapp/exceptions.py
        from rest_framework.views import exception_handler
        from drf_spectacular.utils import extend_schema
        from rest_framework.response import Response

        def custom_exception_handler(exc, context):
            """自定义异常处理器"""
            response = exception_handler(exc, context)

            if response is not None:
                # 添加自定义错误格式
                custom_response_data = {
                    'error': {
                        'type': exc.__class__.__name__,
                        'message': str(exc),
                        'code': getattr(exc, 'default_code', None),
                        'details': response.data,
                    },
                    'timestamp': timezone.now().isoformat(),
                    'path': context['request'].path,
                }

                response.data = custom_response_data

            return response

        # 序列化器文档化
        from drf_spectacular.utils import extend_schema_serializer
        from drf_spectacular.plumbing import build_basic_type
        from drf_spectacular.types import OpenApiTypes

        @extend_schema_serializer(
            examples=[
                {
                    'name': '创建文章示例',
                    'summary': '创建一篇新文章的示例',
                    'value': {
                        'title': 'Django REST Framework入门',
                        'content': '这是一篇关于DRF的文章...',
                        'category': 1,
                        'tags': [1, 2, 3],
                        'status': 'draft',
                    },
                    'request_only': True,
                },
                {
                    'name': '文章响应示例',
                    'summary': '文章详情响应示例',
                    'value': {
                        'id': 1,
                        'title': 'Django REST Framework入门',
                        'content': '这是一篇关于DRF的文章...',
                        'author': {
                            'id': 1,
                            'username': 'admin',
                            'email': '[email protected]',
                        },
                        'category': {
                            'id': 1,
                            'name': 'Python开发',
                            'slug': 'python-dev',
                        },
                        'tags': [
                            {'id': 1, 'name': 'Django'},
                            {'id': 2, 'name': 'REST'},
                        ],
                        'status': 'published',
                        'created_at': '2024-01-15T10:30:00Z',
                        'updated_at': '2024-01-15T11:00:00Z',
                    },
                    'response_only': True,
                },
            ]
        )
        class DocumentedPostSerializer(serializers.ModelSerializer):
            """带详细文档的文章序列化器"""
            author_info = serializers.SerializerMethodField(
                help_text='作者信息,包含用户名和邮箱'
            )
            comments_count = serializers.SerializerMethodField(
                help_text='文章评论数量'
            )

            class Meta:
                model = Post
                fields = [
                    'id', 'title', 'content', 'author_info', 'category', 'tags',
                    'status', 'comments_count', 'created_at', 'updated_at'
                ]
                extra_kwargs = {
                    'title': {
                        'help_text': '文章标题,最多200个字符',
                        'min_length': 5,
                        'max_length': 200,
                    },
                    'content': {
                        'help_text': '文章内容,支持Markdown格式',
                        'min_length': 10,
                    },
                    'status': {
                        'help_text': '文章状态',
                        'choices': Post.STATUS_CHOICES,
                    },
                }

            def get_author_info(self, obj):
                """获取作者信息"""
                return {
                    'id': obj.author.id,
                    'username': obj.author.username,
                    'email': obj.author.email,
                }

            def get_comments_count(self, obj):
                """获取评论数量"""
                return obj.comments.count()

        # API使用示例:
        # 1. 访问Swagger UI
        # GET /api/docs/
        # 显示交互式API文档

        # 2. 访问ReDoc文档
        # GET /api/redoc/
        # 显示美化的API文档

        # 3. 获取OpenAPI Schema
        # GET /api/schema/
        # 返回OpenAPI 3.0 JSON格式

        # 4. 下载API文档
        # GET /api/schema/?format=openapi-json
        # 下载JSON格式的API规范

        # 5. 生成客户端代码
        # 使用OpenAPI Generator等工具从schema生成客户端代码
        # openapi-generator-cli generate -i http://localhost:8000/api/schema/ \
        #   -g python -o ./client/
        ---

02.OpenAPI/Swagger配置
    a.OpenAPI规范配置
        配置详细的OpenAPI规范,包括API元数据、安全方案和数据模型定义。
        ---
        # settings.py - OpenAPI详细配置
        SPECTACULAR_SETTINGS = {
            # API基本信息
            'TITLE': 'Blog API Platform',
            'DESCRIPTION': '''
            ## 概述
            这是一个基于Django REST Framework的博客API平台,提供完整的博客功能。

            ## 功能特性
            - 🔐 多种认证方式(JWT, Session, Token)
            - 📝 文章CRUD操作
            - 💬 评论系统
            - 🏷️ 标签分类
            - 👥 用户管理
            - 🔍 全文搜索
            - 📊 数据统计

            ## 使用指南
            1. 注册账户或使用现有账户登录
            2. 获取认证token
            3. 在API文档页面点击"Authorize"进行认证
            4. 使用API进行各种操作
            ''',
            'VERSION': '1.0.0',
            'TERMS_OF_SERVICE': 'https://example.com/terms/',
            'CONTACT': {
                'name': 'API Team',
                'url': 'https://example.com/contact',
                'email': '[email protected]',
            },
            'LICENSE': {
                'name': 'Apache 2.0',
                'url': 'https://www.apache.org/licenses/LICENSE-2.0.html',
            },

            # 服务器配置
            'SERVERS': [
                {
                    'url': 'https://api.example.com/v1',
                    'description': '生产环境',
                },
                {
                    'url': 'https://staging-api.example.com/v1',
                    'description': '测试环境',
                },
                {
                    'url': 'http://localhost:8000/api/v1',
                    'description': '开发环境',
                },
            ],

            # 认证配置
            'SECURITY': [
                {'jwtAuth': []},
                {'sessionAuth': []},
                {'apiKeyAuth': []},
            ],
            'SECURITY_DEFINITIONS': {
                'jwtAuth': {
                    'type': 'http',
                    'scheme': 'bearer',
                    'bearerFormat': 'JWT',
                    'description': 'JWT认证,从登录接口获取token',
                },
                'sessionAuth': {
                    'type': 'apiKey',
                    'in': 'cookie',
                    'name': 'sessionid',
                    'description': 'Session认证,使用Django的session',
                },
                'apiKeyAuth': {
                    'type': 'apiKey',
                    'in': 'header',
                    'name': 'X-API-Key',
                    'description': 'API Key认证,用于程序访问',
                },
            },

            # 标签配置
            'TAGS': [
                {
                    'name': 'Authentication',
                    'description': '认证相关的API',
                    'externalDocs': {
                        'description': '认证文档',
                        'url': 'https://docs.example.com/auth',
                    }
                },
                {
                    'name': 'Users',
                    'description': '用户管理',
                },
                {
                    'name': 'Posts',
                    'description': '文章管理',
                },
                {
                    'name': 'Comments',
                    'description': '评论管理',
                },
                {
                    'name': 'Tags',
                    'description': '标签管理',
                },
                {
                    'name': 'Analytics',
                    'description': '数据统计',
                },
            ],

            # 模式配置
            'SWAGGER_UI_SETTINGS': {
                'deepLinking': True,
                'displayOperationId': True,
                'defaultModelsExpandDepth': 1,
                'defaultModelExpandDepth': 1,
                'displayRequestDuration': True,
                'docExpansion': 'list',
                'filter': True,
                'showExtensions': True,
                'showCommonExtensions': True,
                'tryItOutEnabled': True,
                'requestSnippetsEnabled': True,
                'persistAuthorization': True,
                'displayLoader': True,
                'showMutatedRequest': True,
                'supportedSubmitMethods': ['get', 'post', 'put', 'delete', 'patch'],
                'validatorUrl': None,
            },

            # 自定义配置
            'COMPONENT_SPLIT_REQUEST': True,
            'COMPONENT_NO_READ_ONLY_REQUIRED': True,
            'GENERIC_ADDITIONAL_PROPERTIES': None,
            'ENUM_NAME_OVERRIDES': {
                'PostStatusEnum': 'myapp.models.Post.PostStatus',
                'CommentStatusEnum': 'myapp.models.Comment.CommentStatus',
            },
            'ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE': True,
            'SET_DEFAULT_OPERATION_ID': False,
        }

        # 自定义OpenAPI扩展
        # myapp/openapi_extensions.py
        from drf_spectacular.extensions import OpenApiFilterExtension
        from drf_spectacular.plumbing import build_basic_type
        from rest_framework.filters import SearchFilter

        class CustomSearchFilterExtension(OpenApiFilterExtension):
            """自定义搜索过滤器扩展"""
            target_class = SearchFilter
            match_subclasses = True

            def get_schema_operation_parameters(self, auto_schema, *args, **kwargs):
                search_fields = getattr(auto_schema.view, 'search_fields', [])
                if not search_fields:
                    return []

                    return [
                        {
                            'name': 'search',
                            'in': 'query',
                            'required': False,
                            'description': f'搜索字段: {", ".join(search_fields)}',
                            'schema': build_basic_type(str),
                        }
                    ]

        # 自定义数据类型
        from drf_spectacular.utils import OpenApiExample, extend_schema
        from drf_spectacular.types import OpenApiTypes

        class ExtendedOpenApiTypes:
            """扩展的OpenAPI类型"""

            @staticmethod
            def EMAIL():
                return {
                    'type': 'string',
                    'format': 'email',
                    'pattern': r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
                    'description': '有效的邮箱地址',
                }

            @staticmethod
            def PASSWORD():
                return {
                    'type': 'string',
                    'format': 'password',
                    'minLength': 8,
                    'maxLength': 128,
                    'pattern': r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$',
                    'description': '至少8位,包含字母和数字',
                }

            @staticmethod
            def SLUG():
                return {
                    'type': 'string',
                    'pattern': r'^[-a-zA-Z0-9_]+$',
                    'maxLength': 50,
                    'description': 'URL友好的标识符',
                }

        # 序列化器中的OpenAPI配置
        from drf_spectacular.utils import extend_schema_field

        class UserRegistrationSerializer(serializers.ModelSerializer):
            """用户注册序列化器"""
            password_confirm = serializers.CharField(
                write_only=True,
                style={'input_type': 'password'},
                help_text='确认密码,必须与密码一致'
            )

            class Meta:
                model = User
                fields = ['username', 'email', 'password', 'password_confirm']

            def validate_email(self, value):
                """验证邮箱格式和唯一性"""
                if User.objects.filter(email=value).exists():
                    raise serializers.ValidationError('该邮箱已被注册')
                return value

            def validate(self, attrs):
                """验证密码一致性"""
                if attrs['password'] != attrs['password_confirm']:
                    raise serializers.ValidationError('两次输入的密码不一致')
                return attrs

            @extend_schema_field(ExtendedOpenApiTypes.EMAIL())
            def email(self):
                """邮箱字段的自定义文档"""
                pass

        # 视图中的OpenAPI配置
        from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiExample

        class PostViewSet(viewsets.ModelViewSet):
            """文章视图集,带详细OpenAPI配置"""
            queryset = Post.objects.all()
            serializer_class = PostSerializer

            @extend_schema(
                summary='获取文章列表',
                description='''
                获取文章列表,支持以下功能:
                - 分页显示
                - 按状态过滤
                - 关键词搜索
                - 排序
                - 按标签过滤

                ## 分页参数
                - page: 页码
                - page_size: 每页数量

                ## 过滤参数
                - status: 文章状态 (draft|published|archived)
                - category: 分类ID
                - tags: 标签ID,支持多个值

                ## 搜索
                - search: 搜索关键词,会搜索标题和内容

                ## 排序
                - ordering: 排序字段
                  可选值:created_at, -created_at, title, -title, views, -views
                ''',
                parameters=[
                    OpenApiParameter(
                        name='status',
                        description='文章状态',
                        required=False,
                        type=OpenApiTypes.STR,
                        enum=['draft', 'published', 'archived'],
                        examples=[
                            OpenApiExample(
                                '草稿状态',
                                value='draft',
                                description='只显示草稿文章'
                            ),
                            OpenApiExample(
                                '已发布状态',
                                value='published',
                                description='只显示已发布文章'
                            ),
                        ]
                    ),
                    OpenApiParameter(
                        name='search',
                        description='搜索关键词',
                        required=False,
                        type=OpenApiTypes.STR,
                        examples=[
                            OpenApiExample(
                                '搜索Django',
                                value='Django',
                                description='搜索包含Django的文章'
                            ),
                        ]
                    ),
                    OpenApiParameter(
                        name='ordering',
                        description='排序字段',
                        required=False,
                        type=OpenApiTypes.STR,
                        enum=['created_at', '-created_at', 'title', '-title'],
                    ),
                    OpenApiParameter(
                        name='page',
                        description='页码',
                        required=False,
                        type=OpenApiTypes.INT,
                        location=OpenApiParameter.QUERY,
                        default=1,
                    ),
                    OpenApiParameter(
                        name='page_size',
                        description='每页数量',
                        required=False,
                        type=OpenApiTypes.INT,
                        location=OpenApiParameter.QUERY,
                        default=20,
                        enum=[10, 20, 50, 100],
                    ),
                ],
                responses={
                    200: {
                        'description': '成功获取文章列表',
                        'content': {
                            'application/json': {
                                'schema': {
                                    'type': 'object',
                                    'properties': {
                                        'count': {'type': 'integer', 'description': '总数量'},
                                        'next': {'type': 'string', 'nullable': True, 'description': '下一页链接'},
                                        'previous': {'type': 'string', 'nullable': True, 'description': '上一页链接'},
                                        'results': {
                                            'type': 'array',
                                            'items': {'$ref': '#/components/schemas/Post'},
                                        }
                                    }
                                }
                            }
                        }
                    },
                    401: {
                        'description': '未认证',
                        'content': {
                            'application/json': {
                                'example': {
                                    'detail': 'Authentication credentials were not provided.'
                                }
                            }
                        }
                    }
                },
                tags=['Posts'],
                examples=[
                    OpenApiExample(
                        '获取已发布文章',
                        value={'status': 'published', 'page': 1, 'page_size': 10},
                        description='获取第一页的已发布文章'
                    ),
                ]
            )
            def list(self, request, *args, **kwargs):
                """获取文章列表"""
                return super().list(request, *args, **kwargs)

            @extend_schema(
                summary='创建文章',
                description='创建一篇新文章',
                request=PostSerializer,
                responses={
                    201: PostSerializer,
                    400: {
                        'description': '请求参数错误',
                        'content': {
                            'application/json': {
                                'example': {
                                    'title': ['This field is required.'],
                                    'content': ['This field is required.'],
                                }
                            }
                        }
                    }
                },
                tags=['Posts'],
                examples=[
                    OpenApiExample(
                        '创建技术文章',
                        value={
                            'title': 'Django REST Framework最佳实践',
                            'content': '本文介绍DRF的最佳实践...',
                            'category': 1,
                            'tags': [1, 2, 3],
                            'status': 'draft'
                        },
                        description='创建一篇技术博客文章'
                    ),
                ]
            )
            def create(self, request, *args, **kwargs):
                """创建文章"""
                return super().create(request, *args, **kwargs)

        # API使用示例:
        # 1. 获取完整的OpenAPI规范
        # GET /api/schema/
        # 返回包含所有配置的OpenAPI 3.0规范

        # 2. 访问Swagger UI,查看详细的API文档
        # http://localhost:8000/api/docs/

        # 3. 在Swagger UI中测试API
        # - 点击"Authorize"按钮进行认证
        # - 使用try it out功能测试API
        # - 查看请求和响应示例

        # 4. 下载API规范文件
        # wget http://localhost:8000/api/schema/?format=openapi

        # 5. 使用OpenAPI规范生成客户端代码
        # openapi-generator-cli generate -i http://localhost:8000/api/schema \
        #   -g typescript-axios -o ./client-typescript/
        ---
    b.自定义UI和样式
        自定义Swagger UI的外观和行为,提供更好的用户体验。
        ---
        # settings.py - UI自定义配置
        SPECTACULAR_SETTINGS = {
            # 基础UI配置
            'SWAGGER_UI_SETTINGS': {
                'deepLinking': True,
                'displayOperationId': False,
                'defaultModelsExpandDepth': 2,
                'defaultModelExpandDepth': 2,
                'displayRequestDuration': True,
                'docExpansion': 'none',
                'filter': True,
                'showExtensions': True,
                'showCommonExtensions': True,
                'tryItOutEnabled': True,
                'requestSnippetsEnabled': True,
                'persistAuthorization': True,
                'displayLoader': True,
                'showMutatedRequest': True,
                'supportedSubmitMethods': ['get', 'post', 'put', 'delete', 'patch'],
                'validatorUrl': None,
                'oauth2RedirectUrl': 'http://localhost:8000/api/docs/oauth2-redirect/',
                'showRequestHeaders': True,
                'customCss': '''
                    /* 自定义CSS样式 */
                    .swagger-ui .topbar {
                        background-color: #2c3e50;
                        border-bottom: 1px solid #34495e;
                    }

                    .swagger-ui .topbar .download-url-wrapper .select-label {
                        color: #ecf0f1;
                    }

                    .swagger-ui .info {
                        margin: 50px 0;
                    }

                    .swagger-ui .info .title {
                        color: #2c3e50;
                        font-size: 36px;
                    }

                    .swagger-ui .scheme-container {
                        background: #f8f9fa;
                        border-radius: 4px;
                        padding: 10px;
                        margin: 10px 0;
                    }

                    .swagger-ui .opblock.opblock-post {
                        border-color: #28a745;
                        background: rgba(40, 167, 69, 0.1);
                    }

                    .swagger-ui .opblock.opblock-get {
                        border-color: #007bff;
                        background: rgba(0, 123, 255, 0.1);
                    }

                    .swagger-ui .opblock.opblock-put {
                        border-color: #ffc107;
                        background: rgba(255, 193, 7, 0.1);
                    }

                    .swagger-ui .opblock.opblock-delete {
                        border-color: #dc3545;
                        background: rgba(220, 53, 69, 0.1);
                    }

                    .swagger-ui .highlight-code {
                        background: #f8f9fa;
                        border: 1px solid #e9ecef;
                        border-radius: 4px;
                        padding: 16px;
                        overflow: auto;
                    }

                    /* 暗色主题支持 */
                    @media (prefers-color-scheme: dark) {
                        .swagger-ui {
                            color: #e9ecef;
                            background-color: #212529;
                        }

                        .swagger-ui .info {
                            color: #e9ecef;
                        }

                        .swagger-ui .scheme-container {
                            background: #343a40;
                        }
                    }
                ''',
                'customSiteTitle': 'Blog API Platform - Documentation',
                'customfavIcon': '/static/favicon.ico',
                'customJs': '''
                    // 自定义JavaScript功能
                    console.log('Swagger UI loaded');

                    // 添加快捷键支持
                    document.addEventListener('keydown', function(e) {
                        // Ctrl+K 打开搜索
                        if (e.ctrlKey && e.key === 'k') {
                            e.preventDefault();
                            document.querySelector('.swagger-ui .filter input').focus();
                        }

                        // Ctrl+/ 聚焦操作ID搜索
                        if (e.ctrlKey && e.key === '/') {
                            e.preventDefault();
                            document.querySelector('.swagger-ui .opblock-summary-control').focus();
                        }
                    });

                    // 自动折叠/展开模型
                    function toggleAllModels() {
                        const models = document.querySelectorAll('.model-box');
                        models.forEach(model => {
                            model.classList.toggle('model-open');
                        });
                    }

                    // 添加复制响应功能
                    function addCopyButton() {
                        const responses = document.querySelectorAll('.highlight-code');
                        responses.forEach(response => {
                            const button = document.createElement('button');
                            button.textContent = 'Copy';
                            button.className = 'copy-button';
                            button.style.cssText = `
                                position: absolute;
                                top: 10px;
                                right: 10px;
                                background: #007bff;
                                color: white;
                                border: none;
                                padding: 4px 8px;
                                border-radius: 4px;
                                cursor: pointer;
                                font-size: 12px;
                            `;

                            button.addEventListener('click', function() {
                                navigator.clipboard.writeText(response.textContent);
                                button.textContent = 'Copied!';
                                setTimeout(() => {
                                    button.textContent = 'Copy';
                                }, 2000);
                            });

                            response.style.position = 'relative';
                            response.appendChild(button);
                        });
                    }

                    // 页面加载完成后执行
                    setTimeout(function() {
                        addCopyButton();
                    }, 1000);
                ''',
            },

            # ReDoc配置
            'REDOC_UI_SETTINGS': {
                'hideDownloadButton': False,
                'hideHostname': False,
                'noAutoAuth': False,
                'pathInMiddlePanel': True,
                'hideLoading': True,
                'expandResponses': '200,201',
                'requiredPropsFirst': True,
                'sortPropsAlphabetically': False,
                'sortEnumValuesAlphabetically': True,
                'theme': {
                    'colors': {
                        'primary': {
                            'main': '#2c3e50',
                        },
                        'success': {
                            'main': '#28a745',
                        },
                        'warning': {
                            'main': '#ffc107',
                        },
                        'error': {
                            'main': '#dc3545',
                        },
                    },
                    'typography': {
                        'fontFamily': 'Roboto, sans-serif',
                        'fontSize': '16px',
                        'lineHeight': '1.5',
                    },
                    'sidebar': {
                        'backgroundColor': '#f8f9fa',
                        'textColor': '#2c3e50',
                    },
                    'code': {
                        'backgroundColor': '#f1f3f4',
                        'color': '#d73a49',
                    },
                },
            },
        }

        # 自定义Swagger UI视图
        # myapp/views.py
        from drf_spectacular.views import SpectacularSwaggerView
        from django.shortcuts import render
        from django.http import HttpResponse

        class CustomSwaggerView(SpectacularSwaggerView):
            """自定义Swagger UI视图"""

            def get(self, request, *args, **kwargs):
                """自定义GET方法"""
                response = super().get(request, *args, **kwargs)

                # 添加自定义JavaScript
        custom_js = '''
        // 添加API使用统计
        function trackApiUsage(operationId) {
            fetch('/api/track-usage/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRFToken': getCookie('csrftoken')
                },
                body: JSON.stringify({
                    operation_id: operationId,
                    timestamp: new Date().toISOString()
                })
            });
        }

        // 获取Cookie
        function getCookie(name) {
            const value = `; ${document.cookie}`;
            const parts = value.split(`; ${name}=`);
            if (parts.length === 2) return parts.pop().split(';').shift();
        }

        // 监听API调用
        document.addEventListener('DOMContentLoaded', function() {
            const tryItOutButtons = document.querySelectorAll('.try-out__btn');
            tryItOutButtons.forEach(button => {
                button.addEventListener('click', function() {
                    const operationId = this.closest('.opblock').getAttribute('data-operation-id');
                    if (operationId) {
                        trackApiUsage(operationId);
                    }
                });
            });
        });
        '''

        # 注入自定义JS
        if hasattr(response, 'render'):
            response.render()
            content = response.content.decode('utf-8')
            content = content.replace('</body>', f'<script>{custom_js}</script></body>')
            response.content = content.encode('utf-8')

        return response

        # 自定义文档首页
        def custom_api_docs(request):
            """自定义API文档首页"""
            return render(request, 'api_docs.html', {
                'title': 'API Documentation',
                'description': 'Blog API Platform Documentation',
                'version': '1.0.0',
            })

        # 自定义模板
        # templates/api_docs.html
        <!DOCTYPE html>
        <html>
        <head>
            <title>{{ title }}</title>
            <meta charset="utf-8"/>
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet">
            <style>
                body {
                    font-family: 'Roboto', sans-serif;
                    margin: 0;
                    padding: 20px;
                    background-color: #f8f9fa;
                }
                .header {
                    background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
                    color: white;
                    padding: 40px;
                    text-align: center;
                    border-radius: 8px;
                    margin-bottom: 30px;
                }
                .header h1 {
                    margin: 0;
                    font-size: 2.5em;
                }
                .header p {
                    margin: 10px 0 0 0;
                    opacity: 0.9;
                }
                .doc-links {
                    display: flex;
                    justify-content: center;
                    gap: 20px;
                    margin-bottom: 30px;
                }
                .doc-link {
                    background: white;
                    padding: 20px 30px;
                    border-radius: 8px;
                    text-decoration: none;
                    color: #2c3e50;
                    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                    transition: transform 0.2s;
                }
                .doc-link:hover {
                    transform: translateY(-2px);
                    box-shadow: 0 4px 20px rgba(0,0,0,0.15);
                }
                .doc-link h3 {
                    margin: 0 0 10px 0;
                    color: #3498db;
                }
                .doc-link p {
                    margin: 0;
                    color: #6c757d;
                }
                .quick-start {
                    background: white;
                    padding: 30px;
                    border-radius: 8px;
                    margin-bottom: 30px;
                    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                }
                .code-block {
                    background: #f1f3f4;
                    padding: 15px;
                    border-radius: 4px;
                    font-family: 'Courier New', monospace;
                    overflow-x: auto;
                }
            </style>
        </head>
        <body>
            <div class="header">
                <h1>📚 {{ title }}</h1>
                <p>{{ description }} - Version {{ version }}</p>
            </div>

            <div class="doc-links">
                <a href="/api/docs/" class="doc-link">
                    <h3>🔧 Swagger UI</h3>
                    <p>交互式API文档</p>
                </a>
                <a href="/api/redoc/" class="doc-link">
                    <h3>📖 ReDoc</h3>
                    <p>美观的API文档</p>
                </a>
                <a href="/api/schema/" class="doc-link">
                    <h3>📋 OpenAPI Schema</h3>
                    <p>JSON格式的API规范</p>
                </a>
            </div>

            <div class="quick-start">
                <h2>🚀 快速开始</h2>
                <p>按照以下步骤开始使用API:</p>

                <h3>1. 获取认证Token</h3>
                <div class="code-block">
                    curl -X POST http://localhost:8000/api/auth/login/ \
                        -H "Content-Type: application/json" \
                        -d '{"username": "your_username", "password": "your_password"}'
                </div>

                <h3>2. 使用Token访问API</h3>
                <div class="code-block">
                    curl -X GET http://localhost:8000/api/posts/ \
                        -H "Authorization: Bearer YOUR_TOKEN"
                </div>

                <h3>3. 创建文章</h3>
                <div class="code-block">
                    curl -X POST http://localhost:8000/api/posts/ \
                        -H "Authorization: Bearer YOUR_TOKEN" \
                        -H "Content-Type: application/json" \
                        -d '{"title": "我的第一篇文章", "content": "文章内容..."}'
                </div>
            </div>

            <script>
                // 添加一些交互功能
                document.querySelectorAll('.doc-link').forEach(link => {
                    link.addEventListener('click', function(e) {
                        console.log('Navigating to:', this.href);
                    });
                });
            </script>
        </body>
        </html>

        # URL配置
        # urls.py
        urlpatterns = [
            # 自定义文档首页
            path('api/docs/', custom_api_docs, name='custom-api-docs'),

            # 自定义Swagger UI
            path('api/swagger/', CustomSwaggerView.as_view(url_name='schema'), name='custom-swagger'),

            # 其他路由...
            path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
            path('api/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
        ]

        # API使用示例:
        # 1. 访问自定义文档首页
        # GET /api/docs/
        # 显示带有快速开始指南的自定义首页

        # 2. 访问自定义Swagger UI
        # GET /api/swagger/
        # 显示带有自定义样式和功能的Swagger UI

        # 3. 使用ReDoc查看文档
        # GET /api/redoc/
        # 显示使用自定义主题的ReDoc文档

        # 4. 下载API规范
        # GET /api/schema/?format=openapi
        # 获取完整的OpenAPI规范JSON

        # 5. 使用API统计功能
        # 在Swagger UI中调用API时会自动记录使用情况
        # GET /api/track-usage/  # 查看使用统计
        ---

8.2 API测试基础

01.APITestCase使用
    a.基础测试配置
        DRF的APITestCase提供了完整的API测试环境,支持请求认证、响应验证和状态码检查。
        ---
        # tests/test_api.py - 基础API测试
        from django.urls import reverse
        from rest_framework import status
        from rest_framework.test import APITestCase
        from rest_framework.authtoken.models import Token
        from django.contrib.auth import get_user_model
        from django.core import mail
        from unittest.mock import patch

        User = get_user_model()

        class BaseAPITestCase(APITestCase):
            """API测试基类,提供通用的测试功能"""

            def setUp(self):
                """设置测试数据"""
                # 创建测试用户
                self.user = User.objects.create_user(
                    username='testuser',
                    email='[email protected]',
                    password='testpass123'
                )

                # 创建管理员用户
                self.admin_user = User.objects.create_user(
                    username='admin',
                    email='[email protected]',
                    password='adminpass123',
                    is_staff=True,
                    is_superuser=True
                )

                # 创建认证token
                self.user_token = Token.objects.create(user=self.user)
                self.admin_token = Token.objects.create(user=self.admin_user)

                # 设置认证头
                self.auth_header = {'HTTP_AUTHORIZATION': f'Token {self.user_token.key}'}
                self.admin_auth_header = {'HTTP_AUTHORIZATION': f'Token {self.admin_token.key}'}

                # 测试数据
                self.post_data = {
                    'title': '测试文章',
                    'content': '这是一篇测试文章的内容。',
                    'status': 'draft'
                }

                self.update_data = {
                    'title': '更新后的标题',
                    'content': '更新后的内容。'
                }

        class UserRegistrationTestCase(BaseAPITestCase):
            """用户注册测试"""

            def test_user_registration_success(self):
                """测试用户注册成功"""
                url = reverse('user-register')
                data = {
                    'username': 'newuser',
                    'email': '[email protected]',
                    'password': 'newpass123',
                    'password_confirm': 'newpass123'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_201_CREATED)
                self.assertEqual(User.objects.count(), 3)  # 加上setUp中的2个用户

                # 验证返回的用户信息
                self.assertEqual(response.data['username'], 'newuser')
                self.assertEqual(response.data['email'], '[email protected]')
                self.assertNotIn('password', response.data)

                # 验证数据库中的用户
                user = User.objects.get(username='newuser')
                self.assertTrue(user.check_password('newpass123'))

            def test_user_registration_missing_fields(self):
                """测试缺少必填字段的注册"""
                url = reverse('user-register')
                data = {
                    'username': 'newuser',
                    # 缺少email和password
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
                self.assertIn('email', response.data)
                self.assertIn('password', response.data)

            def test_user_registration_password_mismatch(self):
                """测试密码不匹配的情况"""
                url = reverse('user-register')
                data = {
                    'username': 'newuser',
                    'email': '[email protected]',
                    'password': 'newpass123',
                    'password_confirm': 'differentpass'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
                self.assertIn('non_field_errors', response.data)

            def test_user_registration_duplicate_username(self):
                """测试重复用户名"""
                url = reverse('user-register')
                data = {
                    'username': 'testuser',  # 已存在的用户名
                    'email': '[email protected]',
                    'password': 'newpass123',
                    'password_confirm': 'newpass123'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
                self.assertIn('username', response.data)

            def test_user_registration_invalid_email(self):
                """测试无效邮箱格式"""
                url = reverse('user-register')
                data = {
                    'username': 'newuser',
                    'email': 'invalid-email',  # 无效邮箱格式
                    'password': 'newpass123',
                    'password_confirm': 'newpass123'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
                self.assertIn('email', response.data)

        class UserLoginTestCase(BaseAPITestCase):
            """用户登录测试"""

            def test_user_login_success(self):
                """测试用户登录成功"""
                url = reverse('user-login')
                data = {
                    'username': 'testuser',
                    'password': 'testpass123'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertIn('token', response.data)
                self.assertIn('user', response.data)

                # 验证token
                token = response.data['token']
                self.assertEqual(Token.objects.get(key=token).user, self.user)

            def test_user_login_invalid_credentials(self):
                """测试无效凭据登录"""
                url = reverse('user-login')
                data = {
                    'username': 'testuser',
                    'password': 'wrongpassword'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

            def test_user_login_nonexistent_user(self):
                """测试不存在用户登录"""
                url = reverse('user-login')
                data = {
                    'username': 'nonexistent',
                    'password': 'somepassword'
                }

                response = self.client.post(url, data, format='json')

                self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

        class PostTestCase(BaseAPITestCase):
            """文章相关测试"""

            def test_create_post_unauthenticated(self):
                """测试未认证用户创建文章"""
                url = reverse('post-list')
                response = self.client.post(url, self.post_data, format='json')

                self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

            def test_create_post_authenticated(self):
                """测试认证用户创建文章"""
                url = reverse('post-list')
                response = self.client.post(url, self.post_data, **self.auth_header, format='json')

                self.assertEqual(response.status_code, status.HTTP_201_CREATED)
                self.assertEqual(response.data['title'], self.post_data['title'])
                self.assertEqual(response.data['content'], self.post_data['content'])
                self.assertEqual(response.data['author'], self.user.id)

            def test_create_post_invalid_data(self):
                """测试创建文章时提供无效数据"""
                url = reverse('post-list')
                invalid_data = {
                    'title': '',  # 空标题
                    'content': '内容'
                }

                response = self.client.post(url, invalid_data, **self.auth_header, format='json')

                self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
                self.assertIn('title', response.data)

            def test_list_posts_unauthenticated(self):
                """测试未认证用户获取文章列表"""
                url = reverse('post-list')
                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(len(response.data['results']), 0)  # 初始为空

            def test_list_posts_authenticated(self):
                """测试认证用户获取文章列表"""
                # 先创建一些文章
                Post.objects.create(
                    title='文章1',
                    content='内容1',
                    author=self.user
                )
                Post.objects.create(
                    title='文章2',
                    content='内容2',
                    author=self.user
                )

                url = reverse('post-list')
                response = self.client.get(url, **self.auth_header, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(len(response.data['results']), 2)

            def test_retrieve_post(self):
                """测试获取单篇文章详情"""
                post = Post.objects.create(
                    title='测试文章',
                    content='测试内容',
                    author=self.user
                )

                url = reverse('post-detail', kwargs={'pk': post.pk})
                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(response.data['title'], '测试文章')

            def test_retrieve_nonexistent_post(self):
                """测试获取不存在的文章"""
                url = reverse('post-detail', kwargs={'pk': 999})
                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

            def test_update_post_as_author(self):
                """测试作者更新自己的文章"""
                post = Post.objects.create(
                    title='原始标题',
                    content='原始内容',
                    author=self.user
                )

                url = reverse('post-detail', kwargs={'pk': post.pk})
                response = self.client.patch(url, self.update_data, **self.auth_header, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                post.refresh_from_db()
                self.assertEqual(post.title, self.update_data['title'])

            def test_update_post_not_author(self):
                """测试非作者尝试更新文章"""
                other_user = User.objects.create_user(
                    username='otheruser',
                    password='otherpass123'
                )
                other_token = Token.objects.create(user=other_user)
                other_auth = {'HTTP_AUTHORIZATION': f'Token {other_token.key}'}

                post = Post.objects.create(
                    title='原始标题',
                    content='原始内容',
                    author=self.user  # 作者是self.user
                )

                url = reverse('post-detail', kwargs={'pk': post.pk})
                response = self.client.patch(url, self.update_data, **other_auth, format='json')

                self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

            def test_delete_post_as_author(self):
                """测试作者删除自己的文章"""
                post = Post.objects.create(
                    title='待删除文章',
                    content='内容',
                    author=self.user
                )

                url = reverse('post-detail', kwargs={'pk': post.pk})
                response = self.client.delete(url, **self.auth_header)

                self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
                self.assertFalse(Post.objects.filter(pk=post.pk).exists())

            def test_delete_post_as_admin(self):
                """测试管理员删除文章"""
                post = Post.objects.create(
                    title='待删除文章',
                    content='内容',
                    author=self.user
                )

                url = reverse('post-detail', kwargs={'pk': post.pk})
                response = self.client.delete(url, **self.admin_auth_header)

                self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

            def test_post_list_pagination(self):
                """测试文章列表分页"""
                # 创建多篇文章
                for i in range(25):
                    Post.objects.create(
                        title=f'文章{i}',
                        content=f'内容{i}',
                        author=self.user
                    )

                url = reverse('post-list')
                response = self.client.get(url, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertIn('count', response.data)
                self.assertIn('next', response.data)
                self.assertIn('previous', response.data)
                self.assertIn('results', response.data)

                # 默认分页大小应该是20
                self.assertEqual(len(response.data['results']), 20)
                self.assertEqual(response.data['count'], 25)

                # 测试第2页
                response = self.client.get(f"{url}?page=2", format='json')
                self.assertEqual(len(response.data['results']), 5)
                self.assertIsNotNone(response.data['previous'])

            def test_post_search(self):
                """测试文章搜索功能"""
                Post.objects.create(
                    title='Django教程',
                    content='学习Django框架',
                    author=self.user
                )
                Post.objects.create(
                    title='Python入门',
                    content='Python编程基础',
                    author=self.user
                )

                url = reverse('post-list')
                response = self.client.get(f"{url}?search=Django", format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(len(response.data['results']), 1)
                self.assertEqual(response.data['results'][0]['title'], 'Django教程')

            def test_post_filtering(self):
                """测试文章过滤功能"""
                Post.objects.create(
                    title='草稿文章',
                    content='内容',
                    author=self.user,
                    status='draft'
                )
                Post.objects.create(
                    title='发布文章',
                    content='内容',
                    author=self.user,
                    status='published'
                )

                url = reverse('post-list')
                response = self.client.get(f"{url}?status=published", format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(len(response.data['results']), 1)
                self.assertEqual(response.data['results'][0]['status'], 'published')

        # 使用示例:
        # 运行所有测试:
        # python manage.py test tests.test_api

        # 运行特定测试类:
        # python manage.py test tests.test_api.UserRegistrationTestCase

        # 运行特定测试方法:
        # python manage.py test tests.test_api.UserRegistrationTestCase.test_user_registration_success

        # 查看测试覆盖率:
        # coverage run --source='.' manage.py test
        # coverage report
        # coverage html
        ---
    b.测试数据准备
        使用fixtures和factory模式准备测试数据,确保测试的独立性和可重复性。
        ---
        # tests/factories.py - 数据工厂模式
        import factory
        from django.contrib.auth import get_user_model
        from django.utils import timezone
        from datetime import timedelta
        import random

        User = get_user_model()

        class UserFactory(factory.django.DjangoModelFactory):
            """用户工厂"""
            class Meta:
                model = User

            username = factory.Sequence(lambda n: f"user{n}")
            email = factory.LazyAttribute(lambda obj: f"{obj.username}@example.com")
            first_name = factory.Faker('first_name')
            last_name = factory.Faker('last_name')
            is_active = True

            @factory.post_generation
            def password(self, create, extracted, **kwargs):
                """生成用户密码"""
                password = extracted or 'testpass123'
                self.set_password(password)
                self.save()

        class CategoryFactory(factory.django.DjangoModelFactory):
            """分类工厂"""
            class Meta:
                model = 'myapp.Category'

            name = factory.Faker('word')
            slug = factory.LazyAttribute(lambda obj: obj.name.lower())
            description = factory.Faker('text', max_nb_chars=200)

        class TagFactory(factory.django.DjangoModelFactory):
            """标签工厂"""
            class Meta:
                model = 'myapp.Tag'

            name = factory.Faker('word')
            slug = factory.LazyAttribute(lambda obj: obj.name.lower())

        class PostFactory(factory.django.DjangoModelFactory):
            """文章工厂"""
            class Meta:
                model = 'myapp.Post'

            title = factory.Faker('sentence', nb_words=6)
            content = factory.Faker('paragraph', nb_sentences=3)
            status = factory.Iterator(['draft', 'published', 'archived'])
            created_at = factory.LazyFunction(timezone.now)
            updated_at = factory.LazyFunction(timezone.now)
            author = factory.SubFactory(UserFactory)
            category = factory.SubFactory(CategoryFactory)

            @factory.post_generation
            def tags(self, create, extracted, **kwargs):
                """为文章添加标签"""
                if not create:
                    return

                if extracted:
                    # 使用提供的标签
                    self.tags.add(*extracted)
                else:
                    # 创建随机数量的标签
                    for _ in range(random.randint(1, 4)):
                        tag = TagFactory()
                        self.tags.add(tag)

        class CommentFactory(factory.django.DjangoModelFactory):
            """评论工厂"""
            class Meta:
                model = 'myapp.Comment'

            content = factory.Faker('paragraph', nb_sentences=2)
            created_at = factory.LazyFunction(timezone.now)
            post = factory.SubFactory(PostFactory)
            author = factory.SubFactory(UserFactory)

        # tests/test_factories.py - 工厂模式测试
        from django.test import TestCase
        from rest_framework.test import APITestCase
        from .factories import (
            UserFactory, CategoryFactory, TagFactory,
            PostFactory, CommentFactory
        )

        class FactoryTestCase(TestCase):
            """测试数据工厂"""

            def test_user_factory(self):
                """测试用户工厂"""
                user = UserFactory()
                self.assertIsNotNone(user.username)
                self.assertIsNotNone(user.email)
                self.assertTrue(user.check_password('testpass123'))

            def test_post_factory(self):
                """测试文章工厂"""
                post = PostFactory()
                self.assertIsNotNone(post.title)
                self.assertIsNotNone(post.content)
                self.assertIsNotNone(post.author)
                self.assertIn(post.status, ['draft', 'published', 'archived'])
                self.assertTrue(post.tags.count() >= 1)

            def test_factory_relationships(self):
                """测试工厂关系"""
                user = UserFactory(username='testuser')
                post = PostFactory(author=user)

                self.assertEqual(post.author, user)

            def test_batch_creation(self):
                """测试批量创建"""
                users = UserFactory.create_batch(10)
                posts = PostFactory.create_batch(20)

                self.assertEqual(len(users), 10)
                self.assertEqual(len(posts), 20)
                self.assertTrue(all(u.username.startswith('user') for u in users))

        # tests/fixtures.py - 测试夹具
        import json
        from datetime import datetime
        from django.core.management import call_command

        def create_test_data():
            """创建测试数据"""
            # 创建用户
            users = [
                UserFactory(username='admin', is_staff=True, is_superuser=True),
                UserFactory(username='editor'),
                UserFactory(username='writer'),
            ]

            # 创建分类
            categories = [
                CategoryFactory(name='技术', slug='tech'),
                CategoryFactory(name='生活', slug='life'),
                CategoryFactory(name='教育', slug='education'),
            ]

            # 创建标签
            tags = [
                TagFactory(name='Django', slug='django'),
                TagFactory(name='Python', slug='python'),
                TagFactory(name='Web开发', slug='web-dev'),
                TagFactory(name='数据库', slug='database'),
            ]

            # 创建文章
            posts = []
            for i in range(50):
                post = PostFactory(
                    title=f'测试文章{i}',
                    author=random.choice(users),
                    category=random.choice(categories),
                    status=random.choice(['draft', 'published', 'archived'])
                )
                posts.append(post)

            # 创建评论
            comments = []
            for post in posts[:20]:  # 只为前20篇文章创建评论
                for _ in range(random.randint(0, 5)):
                    comment = CommentFactory(
                        post=post,
                        author=random.choice(users)
                    )
                    comments.append(comment)

            return {
                'users': users,
                'categories': categories,
                'tags': tags,
                'posts': posts,
                'comments': comments
            }

        def export_test_data_to_json(filepath='test_data.json'):
            """导出测试数据到JSON文件"""
            call_command('dumpdata', 'auth.User', 'myapp', indent=2, output=filepath)

        def load_test_data_from_json(filepath='test_data.json'):
            """从JSON文件加载测试数据"""
            call_command('loaddata', filepath)

        # tests/test_with_factories.py - 使用工厂模式的API测试
        class PostAPITestCaseWithFactories(APITestCase):
            """使用工厂模式的API测试"""

            def setUp(self):
                """设置测试数据"""
                self.user = UserFactory()
                self.admin_user = UserFactory(is_staff=True, is_superuser=True)

                # 创建token
                from rest_framework.authtoken.models import Token
                self.user_token = Token.objects.create(user=self.user)
                self.admin_token = Token.objects.create(user=self.admin_user)

                self.auth_header = {'HTTP_AUTHORIZATION': f'Token {self.user_token.key}'}
                self.admin_auth_header = {'HTTP_AUTHORIZATION': f'Token {self.admin_token.key}'}

                # 创建测试文章
                self.posts = PostFactory.create_batch(10, author=self.user)
                self.published_posts = [
                    PostFactory(author=self.user, status='published')
                    for _ in range(5)
                ]

            def test_list_posts_with_pagination(self):
                """测试带分页的文章列表"""
                url = reverse('post-list')
                response = self.client.get(url, **self.auth_header)

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(response.data['count'], 15)  # 10 + 5
                self.assertEqual(len(response.data['results']), 10)  # 默认分页大小

            def test_filter_posts_by_status(self):
                """测试按状态过滤文章"""
                url = reverse('post-list')
                response = self.client.get(f"{url}?status=published", **self.auth_header)

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(len(response.data['results']), 5)

                for post in response.data['results']:
                    self.assertEqual(post['status'], 'published')

            def test_search_posts(self):
                """测试搜索文章"""
                # 创建特定标题的文章
                PostFactory(title='Django REST Framework教程', author=self.user)
                PostFactory(title='Python编程指南', author=self.user)

                url = reverse('post-list')
                response = self.client.get(f"{url}?search=Django", **self.auth_header)

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(len(response.data['results']), 1)
                self.assertIn('Django', response.data['results'][0]['title'])

            def test_create_post_with_tags(self):
                """测试创建带标签的文章"""
                tags = TagFactory.create_batch(3)
                url = reverse('post-list')

                data = {
                    'title': '新文章',
                    'content': '文章内容',
                    'tags': [tag.id for tag in tags]
                }

                response = self.client.post(url, data, **self.auth_header, format='json')
                self.assertEqual(response.status_code, status.HTTP_201_CREATED)

                post_id = response.data['id']
                post = Post.objects.get(id=post_id)
                self.assertEqual(post.tags.count(), 3)

            def test_nested_comment_creation(self):
                """测试创建嵌套评论"""
                post = PostFactory(author=self.user)
                parent_comment = CommentFactory(post=post, author=self.user)

                url = reverse('comment-list')
                data = {
                    'post': post.id,
                    'content': '这是一个回复',
                    'reply_to': parent_comment.id
                }

                response = self.client.post(url, data, **self.auth_header, format='json')
                self.assertEqual(response.status_code, status.HTTP_201_CREATED)

                reply = Comment.objects.get(id=response.data['id'])
                self.assertEqual(reply.reply_to, parent_comment)

            def test_bulk_operations(self):
                """测试批量操作"""
                # 批量创建文章
                url = reverse('post-bulk-create')
                posts_data = [
                    {'title': f'批量文章{i}', 'content': f'内容{i}'}
                    for i in range(3)
                ]

                response = self.client.post(url, {'posts': posts_data}, **self.auth_header, format='json')
                self.assertEqual(response.status_code, status.HTTP_201_CREATED)

                # 批量更新
                update_data = [{'id': post.id, 'status': 'published'} for post in self.posts[:3]]
                url = reverse('post-bulk-update')

                response = self.client.patch(url, {'posts': update_data}, **self.admin_auth_header, format='json')
                self.assertEqual(response.status_code, status.HTTP_200_OK)

                # 验证更新结果
                for post in self.posts[:3]:
                    post.refresh_from_db()
                    self.assertEqual(post.status, 'published')

        # tests/conftest.py - pytest配置
        import pytest
        from django.test import Client
        from django.contrib.auth import get_user_model
        from rest_framework.authtoken.models import Token
        from .factories import UserFactory, PostFactory

        User = get_user_model()

        @pytest.fixture
        def api_client():
            """API客户端fixture"""
            return Client()

        @pytest.fixture
        def user():
            """普通用户fixture"""
            return UserFactory()

        @pytest.fixture
        def admin_user():
            """管理员用户fixture"""
            return UserFactory(is_staff=True, is_superuser=True)

        @pytest.fixture
        def auth_client(user):
            """认证客户端fixture"""
            client = Client()
            token = Token.objects.create(user=user)
            client.defaults['HTTP_AUTHORIZATION'] = f'Token {token.key}'
            return client

        @pytest.fixture
        def admin_auth_client(admin_user):
            """管理员认证客户端fixture"""
            client = Client()
            token = Token.objects.create(user=admin_user)
            client.defaults['HTTP_AUTHORIZATION'] = f'Token {token.key}'
            return client

        @pytest.fixture
        def posts(user):
            """文章fixture"""
            return PostFactory.create_batch(10, author=user)

        @pytest.fixture
        def published_posts(user):
            """已发布文章fixture"""
            return PostFactory.create_batch(5, author=user, status='published')

        # pytest测试示例
        class TestPostAPIWithPytest:
            """使用pytest的API测试"""

            def test_list_posts(self, auth_client, posts):
                """测试获取文章列表"""
                url = reverse('post-list')
                response = auth_client.get(url)

                assert response.status_code == 200
                assert len(response.json()['results']) == 10

            def test_create_post(self, auth_client):
                """测试创建文章"""
                url = reverse('post-list')
                data = {
                    'title': '新文章',
                    'content': '文章内容'
                }

                response = auth_client.post(url, data, format='json')

                assert response.status_code == 201
                assert response.json()['title'] == '新文章'

            def test_unauthorized_access(self, api_client):
                """测试未授权访问"""
                url = reverse('post-list')
                response = api_client.post(url, {'title': '测试'}, format='json')

                assert response.status_code == 401

        # 使用示例:
        # 1. 使用Django测试框架:
        # python manage.py test tests.test_factories

        # 2. 使用pytest运行测试:
        # pytest tests/test_with_factories.py -v

        # 3. 生成测试覆盖率报告:
        # pytest --cov=myapp tests/

        # 4. 使用fixtures创建数据:
        # python manage.py shell
        # >>> from tests.factories import PostFactory
        # >>> posts = PostFactory.create_batch(10)

        # 5. 导出/导入测试数据:
        # >>> from tests.fixtures import export_test_data_to_json
        # >>> export_test_data_to_json('test_data.json')
        ---

02.测试客户端配置
    a.认证配置
        在测试中配置各种认证方式,确保测试环境能正确处理用户认证和权限。
        ---
        # tests/test_authentication.py - 认证测试
        from django.urls import reverse
        from rest_framework import status
        from rest_framework.test import APITestCase
        from rest_framework.authtoken.models import Token
        from rest_framework_simplejwt.tokens import RefreshToken
        from django.contrib.auth import get_user_model
        from unittest.mock import patch

        User = get_user_model()

        class AuthenticationTestCase(APITestCase):
            """认证功能测试"""

            def setUp(self):
                """设置测试用户"""
                self.user = User.objects.create_user(
                    username='testuser',
                    email='[email protected]',
                    password='testpass123'
                )
                self.admin_user = User.objects.create_user(
                    username='admin',
                    email='[email protected]',
                    password='adminpass123',
                    is_staff=True,
                    is_superuser=True
                )

            def test_token_authentication(self):
                """测试Token认证"""
                # 创建Token
                token = Token.objects.create(user=self.user)
                auth_header = {'HTTP_AUTHORIZATION': f'Token {token.key}'}

                url = reverse('post-list')
                response = self.client.get(url, **auth_header)

                self.assertEqual(response.status_code, status.HTTP_200_OK)

            def test_jwt_authentication(self):
                """测试JWT认证"""
                # 生成JWT token
                refresh = RefreshToken.for_user(self.user)
                access_token = str(refresh.access_token)
                auth_header = {'HTTP_AUTHORIZATION': f'Bearer {access_token}'}

                url = reverse('post-list')
                response = self.client.get(url, **auth_header)

                self.assertEqual(response.status_code, status.HTTP_200_OK)

            def test_session_authentication(self):
                """测试Session认证"""
                # 登录用户
                self.client.login(username='testuser', password='testpass123')

                url = reverse('post-list')
                response = self.client.get(url)

                self.assertEqual(response.status_code, status.HTTP_200_OK)

            def test_no_authentication(self):
                """测试无认证访问"""
                url = reverse('post-list')
                response = self.client.get(url)

                # 根据视图的权限设置,可能返回200或401
                self.assertIn(response.status_code, [200, 401])

            def test_invalid_token(self):
                """测试无效Token"""
                auth_header = {'HTTP_AUTHORIZATION': 'Token invalid_token'}

                url = reverse('post-list')
                response = self.client.get(url, **auth_header)

                self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

            def test_invalid_jwt(self):
                """测试无效JWT"""
                auth_header = {'HTTP_AUTHORIZATION': 'Bearer invalid_jwt'}

                url = reverse('post-list')
                response = self.client.get(url, **auth_header)

                self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

            def test_token_refresh(self):
                """测试JWT token刷新"""
                refresh = RefreshToken.for_user(self.user)

                url = reverse('token-refresh')
                response = self.client.post(url, {'refresh': str(refresh)}, format='json')

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertIn('access', response.data)

            def test_token_logout(self):
                """测试JWT token注销"""
                refresh = RefreshToken.for_user(self.user)

                url = reverse('token-logout')
                response = self.client.post(url, {'refresh': str(refresh)}, format='json')

                self.assertEqual(response.status_code, status.HTTP_205_RESET_CONTENT)

        class PermissionTestCase(APITestCase):
            """权限测试"""

            def setUp(self):
                """设置测试数据"""
                # 创建不同权限的用户
                self.regular_user = User.objects.create_user(
                    username='regular',
                    password='regularpass'
                )
                self.staff_user = User.objects.create_user(
                    username='staff',
                    password='staffpass',
                    is_staff=True
                )
                self.superuser = User.objects.create_user(
                    username='superuser',
                    password='superpass',
                    is_superuser=True
                )

                # 创建token
                self.regular_token = Token.objects.create(user=self.regular_user)
                self.staff_token = Token.objects.create(user=self.staff_user)
                self.super_token = Token.objects.create(user=self.superuser)

                # 创建测试文章
                self.post = Post.objects.create(
                    title='测试文章',
                    content='测试内容',
                    author=self.regular_user
                )

            def test_user_can_access_own_posts(self):
                """测试用户只能访问自己的文章"""
                auth_header = {'HTTP_AUTHORIZATION': f'Token {self.regular_token.key}'}

                url = reverse('user-posts', kwargs={'user_id': self.regular_user.id})
                response = self.client.get(url, **auth_header)

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(len(response.data), 1)

            def test_user_cannot_access_others_posts(self):
                """测试用户不能访问其他人的私有文章"""
                other_user = User.objects.create_user(
                    username='other',
                    password='otherpass'
                )
                other_token = Token.objects.create(user=other_user)

                auth_header = {'HTTP_AUTHORIZATION': f'Token {other_token.key}'}

                url = reverse('user-posts', kwargs={'user_id': self.regular_user.id})
                response = self.client.get(url, **auth_header)

                self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

            def test_staff_user_can_access_all_posts(self):
                """测试管理员可以访问所有文章"""
                auth_header = {'HTTP_AUTHORIZATION': f'Token {self.staff_token.key}'}

                url = reverse('post-list')
                response = self.client.get(url, **auth_header)

                self.assertEqual(response.status_code, status.HTTP_200_OK)

            def test_superuser_has_full_access(self):
                """测试超级用户有完全访问权限"""
                auth_header = {'HTTP_AUTHORIZATION': f'Token {self.super_token.key}'}

                url = reverse('admin-stats')
                response = self.client.get(url, **auth_header)

                self.assertEqual(response.status_code, status.HTTP_200_OK)

            def test_custom_permission_classes(self):
                """测试自定义权限类"""
                from myapp.permissions import IsAuthorOrReadOnly

                class CustomPermissionTestCase(APITestCase):
                    def setUp(self):
                        self.author = User.objects.create_user(
                            username='author',
                            password='authorpass'
                        )
                        self.reader = User.objects.create_user(
                            username='reader',
                            password='readerpass'
                        )

                        self.post = Post.objects.create(
                            title='测试文章',
                            content='测试内容',
                            author=self.author
                        )

                    def test_author_can_edit(self):
                        """测试作者可以编辑"""
                        token = Token.objects.create(user=self.author)
                        auth_header = {'HTTP_AUTHORIZATION': f'Token {token.key}'}

                        url = reverse('post-detail', kwargs={'pk': self.post.pk})
                        response = self.client.patch(
                            url,
                            {'title': '更新标题'},
                            **auth_header,
                            format='json'
                        )

                        self.assertEqual(response.status_code, status.HTTP_200_OK)

                    def test_reader_cannot_edit(self):
                        """测试读者不能编辑"""
                        token = Token.objects.create(user=self.reader)
                        auth_header = {'HTTP_AUTHORIZATION': f'Token {token.key}'}

                        url = reverse('post-detail', kwargs={'pk': self.post.pk})
                        response = self.client.patch(
                            url,
                            {'title': '更新标题'},
                            **auth_header,
                            format='json'
                        )

                        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

                    def test_reader_can_read(self):
                        """测试读者可以读取"""
                        token = Token.objects.create(user=self.reader)
                        auth_header = {'HTTP_AUTHORIZATION': f'Token {token.key}'}

                        url = reverse('post-detail', kwargs={'pk': self.post.pk})
                        response = self.client.get(url, **auth_header)

                        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # tests/test_client_config.py - 客户端配置测试
        class ClientConfigurationTestCase(APITestCase):
            """客户端配置测试"""

            def setUp(self):
                """设置测试环境"""
                self.user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

            def test_client_headers(self):
                """测试客户端头配置"""
                # 设置自定义头
                headers = {
                    'HTTP_USER_AGENT': 'TestClient/1.0',
                    'HTTP_ACCEPT': 'application/json',
                    'HTTP_CONTENT_TYPE': 'application/json',
                    'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'
                }

                url = reverse('post-list')
                response = self.client.get(url, **headers)

                self.assertEqual(response.status_code, status.HTTP_200_OK)

            def test_client_format(self):
                """测试客户端格式配置"""
                url = reverse('post-list')

                # JSON格式
                response = self.client.get(url, format='json')
                self.assertEqual(response.data, response.json())

                # 显式设置Accept头
                response = self.client.get(url, HTTP_ACCEPT='application/json')
                self.assertEqual(response['Content-Type'], 'application/json')

            def test_client_methods(self):
                """测试各种HTTP方法"""
                url = reverse('post-list')

                # GET请求
                response = self.client.get(url)
                self.assertIn(response.status_code, [200, 401])

                # POST请求
                response = self.client.post(url, {}, format='json')
                self.assertIn(response.status_code, [201, 400, 401])

                # PUT请求
                response = self.client.put(url, {}, format='json')
                self.assertIn(response.status_code, [405, 401])  # Method Not Allowed

                # PATCH请求
                response = self.client.patch(url, {}, format='json')
                self.assertIn(response.status_code, [405, 401])

                # DELETE请求
                response = self.client.delete(url)
                self.assertIn(response.status_code, [405, 401])

            def test_client_data_encoding(self):
                """测试客户端数据编码"""
                url = reverse('post-list')
                data = {
                    'title': '测试标题',
                    'content': '测试内容\n包含换行符',
                    'tags': ['标签1', '标签2']
                }

                # JSON编码
                response = self.client.post(url, data, format='json')

                # 表单编码
                response = self.client.post(url, data, format='multipart')

            def test_client_follow_redirects(self):
                """测试客户端重定向跟随"""
                # 创建一个会重定向的视图
                url = reverse('redirect-view')
                response = self.client.get(url, follow=True)

                self.assertEqual(response.status_code, 200)
                self.assertEqual(len(response.redirect_chain), 1)

            def test_client_session(self):
                """测试客户端会话"""
                # 设置会话数据
                session = self.client.session
                session['test_key'] = 'test_value'
                session.save()

                # 访问需要会话的视图
                url = reverse('session-view')
                response = self.client.get(url)

                self.assertEqual(response.status_code, 200)

            def test_client_cookies(self):
                """测试客户端Cookie"""
                # 设置Cookie
                self.client.cookies['test_cookie'] = 'test_value'

                url = reverse('cookie-view')
                response = self.client.get(url)

                # 验证Cookie被发送
                self.assertIn('test_cookie', self.client.cookies)

        # tests/test_middleware.py - 中间件测试
        class MiddlewareTestCase(APITestCase):
            """中间件测试"""

            def test_cors_middleware(self):
                """测试CORS中间件"""
                url = reverse('post-list')
                headers = {
                    'HTTP_ORIGIN': 'http://localhost:3000',
                    'HTTP_ACCESS_CONTROL_REQUEST_METHOD': 'GET',
                    'HTTP_ACCESS_CONTROL_REQUEST_HEADERS': 'authorization'
                }

                response = self.client.options(url, **headers)

                # 检查CORS头
                self.assertIn('Access-Control-Allow-Origin', response)
                self.assertIn('Access-Control-Allow-Methods', response)
                self.assertIn('Access-Control-Allow-Headers', response)

            def test_rate_limiting_middleware(self):
                """测试速率限制中间件"""
                url = reverse('api-test')
                token = Token.objects.create(user=self.user)
                auth_header = {'HTTP_AUTHORIZATION': f'Token {token.key}'}

                # 正常请求
                response = self.client.get(url, **auth_header)
                self.assertEqual(response.status_code, 200)

                # 快速连续请求(可能触发限制)
                for _ in range(100):  # 假设限制是每分钟100次
                    response = self.client.get(url, **auth_header)
                    if response.status_code == 429:  # Too Many Requests
                        break

            def test_logging_middleware(self):
                """测试日志中间件"""
                with self.assertLogs('api.requests', level='INFO') as log:
                    url = reverse('post-list')
                    self.client.get(url)

                # 验证日志记录
                self.assertEqual(len(log.output), 1)
                self.assertIn('GET /api/posts/', log.output[0])

        # 使用示例:
        # 1. 运行所有认证测试:
        # python manage.py test tests.test_authentication

        # 2. 运行权限测试:
        # python manage.py test tests.test_authentication.PermissionTestCase

        # 3. 测试特定认证方式:
        # python manage.py test tests.test_authentication.AuthenticationTestCase.test_jwt_authentication

        # 4. 使用不同的测试客户端配置:
        # python manage.py test tests.test_client_config.ClientConfigurationTestCase

        # 5. 运行中间件测试:
        # python manage.py test tests.test_middleware
        ---
        c.状态码验证
        验证API返回的状态码,确保符合RESTful API的标准和预期。
        ---
        # tests/test_status_codes.py - 状态码测试
        from django.urls import reverse
        from rest_framework import status
        from rest_framework.test import APITestCase
        from django.contrib.auth import get_user_model
        from rest_framework.authtoken.models import Token

        User = get_user_model()

        class StatusCodeTestCase(APITestCase):
            """HTTP状态码测试"""

            def setUp(self):
                """设置测试数据"""
                self.user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )
                self.token = Token.objects.create(user=self.user)
                self.auth_header = {'HTTP_AUTHORIZATION': f'Token {self.token.key}'}

                self.post = Post.objects.create(
                    title='测试文章',
                    content='测试内容',
                    author=self.user
                )

            def test_200_ok_responses(self):
                """测试200 OK响应"""
                cases = [
                    # (URL, 预期状态码, 描述)
                    (reverse('post-list'), 200, '获取文章列表'),
                    (reverse('post-detail', kwargs={'pk': self.post.pk}), 200, '获取文章详情'),
                    (reverse('user-detail', kwargs={'pk': self.user.id}), 200, '获取用户详情'),
                    (reverse('category-list'), 200, '获取分类列表'),
                ]

                for url, expected_status, description in cases:
                    with self.subTest(url=url, description=description):
                        response = self.client.get(url, **self.auth_header)
                        self.assertEqual(response.status_code, expected_status)

            def test_201_created_responses(self):
                """测试201 Created响应"""
                cases = [
                    # (URL, 数据, 预期状态码, 描述)
                    (reverse('post-list'), {
                        'title': '新文章',
                        'content': '新文章内容'
                    }, 201, '创建文章'),
                    (reverse('comment-list'), {
                        'post': self.post.id,
                        'content': '新评论'
                    }, 201, '创建评论'),
                    (reverse('category-list'), {
                        'name': '新分类',
                        'slug': 'new-category'
                    }, 201, '创建分类'),
                ]

                for url, data, expected_status, description in cases:
                    with self.subTest(url=url, description=description):
                        response = self.client.post(url, data, **self.auth_header, format='json')
                        self.assertEqual(response.status_code, expected_status)

            def test_204_no_content_responses(self):
                """测试204 No Content响应"""
                cases = [
                    (reverse('post-detail', kwargs={'pk': self.post.pk}), '删除文章'),
                    # 假设有这些端点
                    (reverse('post-like', kwargs={'pk': self.post.pk}), '取消点赞文章'),
                    (reverse('user-logout'), '用户登出'),
                ]

                for url, description in cases:
                    with self.subTest(url=url, description=description):
                        response = self.client.delete(url, **self.auth_header)
                        self.assertEqual(response.status_code, 204)

            def test_400_bad_request_responses(self):
                """测试400 Bad Request响应"""
                cases = [
                    # (URL, 数据, 描述)
                    (reverse('post-list'), {}, '创建文章时缺少必要字段'),
                    (reverse('post-list'), {
                        'title': '',  # 空标题
                        'content': '内容'
                    }, '创建文章时标题为空'),
                    (reverse('user-register'), {
                        'username': 'newuser',
                        'password': 'pass123'
                        # 缺少email
                    }, '用户注册时缺少email字段'),
                    (reverse('user-register'), {
                        'username': 'newuser',
                        'email': '[email protected]',
                        'password': '123',  # 密码太短
                        'password_confirm': '123'
                    }, '用户注册时密码不符合要求'),
                ]

                for url, data, description in cases:
                    with self.subTest(url=url, description=description):
                        response = self.client.post(url, data, format='json')
                        self.assertEqual(response.status_code, 400)
                        # 验证错误响应格式
                        self.assertIsInstance(response.data, dict)

            def test_401_unauthorized_responses(self):
                """测试401 Unauthorized响应"""
                cases = [
                    # (URL, 方法, 描述)
                    (reverse('post-list'), 'post', '未认证用户创建文章'),
                    (reverse('post-detail', kwargs={'pk': self.post.pk}), 'patch', '未认证用户更新文章'),
                    (reverse('post-detail', kwargs={'pk': self.post.pk}), 'delete', '未认证用户删除文章'),
                    (reverse('user-posts', kwargs={'user_id': self.user.id}), 'get', '未认证用户访问私有内容'),
                ]

                for url, method, description in cases:
                    with self.subTest(url=url, method=method, description=description):
                        if method == 'get':
                            response = self.client.get(url)
                        elif method == 'post':
                            response = self.client.post(url, {}, format='json')
                        elif method == 'patch':
                            response = self.client.patch(url, {}, format='json')
                        elif method == 'delete':
                            response = self.client.delete(url)

                        self.assertEqual(response.status_code, 401)

            def test_403_forbidden_responses(self):
                """测试403 Forbidden响应"""
                # 创建另一个用户
                other_user = User.objects.create_user(
                    username='otheruser',
                    password='otherpass123'
                )
                other_token = Token.objects.create(user=other_user)
                other_auth_header = {'HTTP_AUTHORIZATION': f'Token {other_token.key}'}

                cases = [
                    # (URL, auth_header, 描述)
                    (reverse('post-detail', kwargs={'pk': self.post.pk}), other_auth_header,
                     '其他用户更新文章'),
                    (reverse('post-detail', kwargs={'pk': self.post.pk}), other_auth_header,
                     '其他用户删除文章'),
                    (reverse('admin-only-endpoint'), self.auth_header, '普通用户访问管理员端点'),
                ]

                for url, auth_header, description in cases:
                    with self.subTest(url=url, description=description):
                        response = self.client.patch(url, {}, **auth_header, format='json')
                        self.assertEqual(response.status_code, 403)

            def test_404_not_found_responses(self):
                """测试404 Not Found响应"""
                cases = [
                    # (URL, 描述)
                    (reverse('post-detail', kwargs={'pk': 99999}), '获取不存在的文章'),
                    (reverse('user-detail', kwargs={'pk': 99999}), '获取不存在的用户'),
                    (reverse('comment-detail', kwargs={'pk': 99999}), '获取不存在的评论'),
                    (reverse('category-detail', kwargs={'pk': 99999}), '获取不存在的分类'),
                ]

                for url, description in cases:
                    with self.subTest(url=url, description=description):
                        response = self.client.get(url, **self.auth_header)
                        self.assertEqual(response.status_code, 404)

            def test_405_method_not_allowed_responses(self):
                """测试405 Method Not Allowed响应"""
                cases = [
                    # (URL, 方法, 描述)
                    (reverse('post-list'), 'put', 'PUT请求到列表端点'),
                    (reverse('post-list'), 'delete', 'DELETE请求到列表端点'),
                    (reverse('post-detail', kwargs={'pk': self.post.pk}), 'post', 'POST请求到详情端点'),
                ]

                for url, method, description in cases:
                    with self.subTest(url=url, method=method, description=description):
                        if method == 'get':
                            response = self.client.get(url)
                        elif method == 'post':
                            response = self.client.post(url, {}, format='json')
                        elif method == 'put':
                            response = self.client.put(url, {}, format='json')
                        elif method == 'patch':
                            response = self.client.patch(url, {}, format='json')
                        elif method == 'delete':
                            response = self.client.delete(url)

                        self.assertEqual(response.status_code, 405)

            def test_409_conflict_responses(self):
                """测试409 Conflict响应"""
                # 尝试创建重复资源
        duplicate_data = {
                    'username': 'testuser',  # 已存在的用户名
                    'email': '[email protected]',
                    'password': 'testpass123'
                }

                url = reverse('user-register')
                response = self.client.post(url, duplicate_data, format='json')
                self.assertEqual(response.status_code, 409)

                # 尝试重复点赞
                url = reverse('post-like', kwargs={'pk': self.post.pk})
                response = self.client.post(url, **self.auth_header)
                self.assertEqual(response.status_code, 201)

                # 第二次点赞
                response = self.client.post(url, **self.auth_header)
                self.assertEqual(response.status_code, 409)

            def test_422_unprocessable_entity_responses(self):
                """测试422 Unprocessable Entity响应"""
                # 发送格式正确但语义错误的请求
                url = reverse('post-publish', kwargs={'pk': self.post.pk})
                response = self.client.post(url, **self.auth_header)
                # 假设文章已经是发布状态
                self.assertEqual(response.status_code, 422)

            def test_429_too_many_requests_responses(self):
                """测试429 Too Many Requests响应"""
                url = reverse('rate-limited-endpoint')

                # 快速发送大量请求
                responses = []
                for i in range(100):
                    response = self.client.get(url, **self.auth_header)
                    responses.append(response)
                    if response.status_code == 429:
                        break

                # 验证至少有一个响应是429
                self.assertTrue(any(r.status_code == 429 for r in responses))

            def test_500_internal_server_error_responses(self):
                """测试500 Internal Server Error响应"""
                with patch('myapp.views.PostDetailView.get') as mock_get:
                    mock_get.side_effect = Exception('Test exception')

                    url = reverse('post-detail', kwargs={'pk': self.post.pk})
                    response = self.client.get(url, **self.auth_header)

                    # 在开发环境中可能返回500,在生产环境中可能返回更友好的错误
                    self.assertIn(response.status_code, [500, 503])

            def test_503_service_unavailable_responses(self):
                """测试503 Service Unavailable响应"""
                # 模拟服务维护
                with patch('myapp.views.is_maintenance_mode', return_value=True):
                    url = reverse('post-list')
                    response = self.client.get(url, **self.auth_header)
                    self.assertEqual(response.status_code, 503)

        class ResponseFormatTestCase(APITestCase):
            """响应格式测试"""

            def setUp(self):
                """设置测试数据"""
                self.user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )
                self.token = Token.objects.create(user=self.user)
                self.auth_header = {'HTTP_AUTHORIZATION': f'Token {self.token.key}'}

            def test_error_response_format(self):
                """测试错误响应格式"""
                url = reverse('post-list')
                response = self.client.post(url, {}, **self.auth_header, format='json')

                self.assertEqual(response.status_code, 400)

                # 验证错误响应包含必要字段
                error_data = response.json()
                if isinstance(error_data, dict):
                    # 字段级错误
                    self.assertTrue(any(field in error_data for field in ['title', 'content', 'non_field_errors']))
                elif isinstance(error_data, list):
                    # 非字段错误
                    self.assertTrue(len(error_data) > 0)

            def test_success_response_format(self):
                """测试成功响应格式"""
                # 创建文章
                url = reverse('post-list')
                data = {
                    'title': '测试文章',
                    'content': '测试内容'
                }
                response = self.client.post(url, data, **self.auth_header, format='json')

                self.assertEqual(response.status_code, 201)

                # 验证成功响应格式
                success_data = response.json()
                self.assertIn('id', success_data)
                self.assertIn('title', success_data)
                self.assertIn('content', success_data)
                self.assertIn('created_at', success_data)

            def test_pagination_response_format(self):
                """测试分页响应格式"""
                # 创建多篇文章
                for i in range(25):
                    Post.objects.create(
                        title=f'文章{i}',
                        content=f'内容{i}',
                        author=self.user
                    )

                url = reverse('post-list')
                response = self.client.get(url, **self.auth_header)

                self.assertEqual(response.status_code, 200)

                # 验证分页响应格式
                paginated_data = response.json()
                self.assertIn('count', paginated_data)
                self.assertIn('next', paginated_data)
                self.assertIn('previous', paginated_data)
                self.assertIn('results', paginated_data)

        # 使用示例:
        # 1. 运行所有状态码测试:
        # python manage.py test tests.test_status_codes

        # 2. 测试特定的状态码类型:
        # python manage.py test tests.test_status_codes.StatusCodeTestCase.test_400_bad_request_responses

        # 3. 测试响应格式:
        # python manage.py test tests.test_status_codes.ResponseFormatTestCase

        # 4. 使用特定标记运行测试:
        # python manage.py test tests.test_status_codes --tag=status_codes

        # 5. 生成状态码测试报告:
        # python manage.py test tests.test_status_codes --verbosity=2
        ---

8.3 单元测试实践

01.序列化器测试
    a.基础序列化器验证
        序列化器测试确保数据验证、序列化和反序列化逻辑的正确性。
        ---
        # tests/test_serializers.py - 序列化器测试
        from django.test import TestCase
        from rest_framework import status
        from django.contrib.auth import get_user_model
        from django.core.exceptions import ValidationError
        from datetime import datetime, timezone
        from unittest.mock import patch, MagicMock

        from myapp.serializers import (
            PostSerializer, UserRegistrationSerializer,
            CommentSerializer, CategorySerializer
        )
        from myapp.models import Post, Comment, Category

        User = get_user_model()

        class PostSerializerTestCase(TestCase):
            """文章序列化器测试"""

            def setUp(self):
                """设置测试数据"""
                self.user = User.objects.create_user(
                    username='testuser',
                    email='[email protected]',
                    password='testpass123'
                )

                self.category = Category.objects.create(
                    name='Python',
                    slug='python'
                )

                self.valid_data = {
                    'title': 'Django REST Framework入门',
                    'content': '这是一篇关于DRF的文章内容。',
                    'category': self.category.id,
                    'status': 'draft'
                }

            def test_valid_serialization(self):
                """测试有效数据序列化"""
                post = Post.objects.create(
                    title='测试文章',
                    content='测试内容',
                    author=self.user,
                    category=self.category
                )

                serializer = PostSerializer(post)
                data = serializer.data

                self.assertEqual(data['title'], '测试文章')
                self.assertEqual(data['content'], '测试内容')
                self.assertEqual(data['author'], self.user.id)
                self.assertEqual(data['category'], self.category.id)
                self.assertIn('created_at', data)

            def test_valid_deserialization(self):
                """测试有效数据反序列化"""
                serializer = PostSerializer(data=self.valid_data)

                self.assertTrue(serializer.is_valid())
                self.assertEqual(serializer.validated_data['title'], self.valid_data['title'])
                self.assertEqual(serializer.validated_data['content'], self.valid_data['content'])

            def test_missing_required_fields(self):
                """测试缺少必填字段"""
                invalid_data = {
                    'content': '只有内容,缺少标题'
                }

                serializer = PostSerializer(data=invalid_data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('title', serializer.errors)

            def test_title_length_validation(self):
                """测试标题长度验证"""
                # 标题太短
                invalid_data = self.valid_data.copy()
                invalid_data['title'] = '短'  # 假设最小长度为5

                serializer = PostSerializer(data=invalid_data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('title', serializer.errors)

                # 标题太长
                invalid_data['title'] = 'x' * 300  # 假设最大长度为200

                serializer = PostSerializer(data=invalid_data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('title', serializer.errors)

            def test_status_choice_validation(self):
                """测试状态选择验证"""
                invalid_data = self.valid_data.copy()
                invalid_data['status'] = 'invalid_status'

                serializer = PostSerializer(data=invalid_data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('status', serializer.errors)

            def test_unique_title_validation(self):
                """测试标题唯一性验证"""
                # 先创建一篇文章
                Post.objects.create(
                    title='唯一标题',
                    content='内容',
                    author=self.user
                )

                # 尝试创建相同标题的文章
                invalid_data = self.valid_data.copy()
                invalid_data['title'] = '唯一标题'

                serializer = PostSerializer(data=invalid_data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('title', serializer.errors)

            def test_category_validation(self):
                """测试分类验证"""
                # 无效的分类ID
                invalid_data = self.valid_data.copy()
                invalid_data['category'] = 9999

                serializer = PostSerializer(data=invalid_data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('category', serializer.errors)

            def test_custom_validation_method(self):
                """测试自定义验证方法"""
                # 假设有自定义验证:标题不能包含禁用词
                invalid_data = self.valid_data.copy()
                invalid_data['title'] = '包含禁用词的内容'

                serializer = PostSerializer(data=invalid_data)
                self.assertFalse(serializer.is_valid())

                # 检查自定义错误消息
                self.assertIn('non_field_errors', serializer.errors)

            def test_create_method(self):
                """测试创建方法"""
                serializer = PostSerializer(data=self.valid_data)
                self.assertTrue(serializer.is_valid())

                # 设置作者(通常在视图中设置)
                serializer.validated_data['author'] = self.user

                post = serializer.save()
                self.assertIsInstance(post, Post)
                self.assertEqual(post.title, self.valid_data['title'])
                self.assertEqual(post.author, self.user)

            def test_update_method(self):
                """测试更新方法"""
                post = Post.objects.create(
                    title='原始标题',
                    content='原始内容',
                    author=self.user
                )

                update_data = {
                    'title': '更新后的标题',
                    'content': '更新后的内容'
                }

                serializer = PostSerializer(post, data=update_data, partial=True)
                self.assertTrue(serializer.is_valid())

                updated_post = serializer.save()
                self.assertEqual(updated_post.title, '更新后的标题')
                self.assertEqual(updated_post.content, '更新后的内容')

            def test_read_only_fields(self):
                """测试只读字段"""
                post = Post.objects.create(
                    title='测试文章',
                    content='测试内容',
                    author=self.user
                )

                # 尝试更新只读字段
                update_data = {
                    'created_at': '2023-01-01T00:00:00Z'
                }

                serializer = PostSerializer(post, data=update_data, partial=True)
                self.assertTrue(serializer.is_valid())

                updated_post = serializer.save()
                # created_at应该保持不变
                self.assertNotEqual(
                    updated_post.created_at.isoformat(),
                    '2023-01-01T00:00:00Z'
                )

            def test_write_only_fields(self):
                """测试只写字段"""
                data = self.valid_data.copy()
                data['password'] = 'secret_password'  # 假设有password字段

                serializer = PostSerializer(data=data)
                self.assertTrue(serializer.is_valid())

                # 密码字段不应出现在序列化数据中
                serialized_data = serializer.data
                self.assertNotIn('password', serialized_data)

            def test_nested_serialization(self):
                """测试嵌套序列化"""
                # 创建带标签的文章
                post = Post.objects.create(
                    title='测试文章',
                    content='测试内容',
                    author=self.user,
                    category=self.category
                )

                # 添加标签
                from myapp.models import Tag
                tag1 = Tag.objects.create(name='Django', slug='django')
                tag2 = Tag.objects.create(name='Python', slug='python')
                post.tags.add(tag1, tag2)

                serializer = PostSerializer(post)
                data = serializer.data

                self.assertIn('tags', data)
                self.assertEqual(len(data['tags']), 2)

        class UserRegistrationSerializerTestCase(TestCase):
            """用户注册序列化器测试"""

            def test_valid_registration(self):
                """测试有效注册数据"""
                data = {
                    'username': 'newuser',
                    'email': '[email protected]',
                    'password': 'securepass123',
                    'password_confirm': 'securepass123'
                }

                serializer = UserRegistrationSerializer(data=data)
                self.assertTrue(serializer.is_valid())

            def test_password_confirmation_mismatch(self):
                """测试密码确认不匹配"""
                data = {
                    'username': 'newuser',
                    'email': '[email protected]',
                    'password': 'securepass123',
                    'password_confirm': 'differentpass'
                }

                serializer = UserRegistrationSerializer(data=data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('password_confirm', serializer.errors)

            def test_duplicate_username(self):
                """测试重复用户名"""
                User.objects.create_user(username='existinguser', password='pass123')

                data = {
                    'username': 'existinguser',
                    'email': '[email protected]',
                    'password': 'securepass123',
                    'password_confirm': 'securepass123'
                }

                serializer = UserRegistrationSerializer(data=data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('username', serializer.errors)

            def test_email_format_validation(self):
                """测试邮箱格式验证"""
                data = {
                    'username': 'newuser',
                    'email': 'invalid-email-format',
                    'password': 'securepass123',
                    'password_confirm': 'securepass123'
                }

                serializer = UserRegistrationSerializer(data=data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('email', serializer.errors)

            def test_create_method_creates_user(self):
                """测试创建方法创建用户"""
                data = {
                    'username': 'newuser',
                    'email': '[email protected]',
                    'password': 'securepass123',
                    'password_confirm': 'securepass123'
                }

                serializer = UserRegistrationSerializer(data=data)
                self.assertTrue(serializer.is_valid())

                user = serializer.save()
                self.assertIsInstance(user, User)
                self.assertEqual(user.username, 'newuser')
                self.assertTrue(user.check_password('securepass123'))

        class CommentSerializerTestCase(TestCase):
            """评论序列化器测试"""

            def setUp(self):
                """设置测试数据"""
                self.user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

                self.post = Post.objects.create(
                    title='测试文章',
                    content='测试内容',
                    author=self.user
                )

            def test_valid_comment_creation(self):
                """测试有效评论创建"""
                data = {
                    'post': self.post.id,
                    'content': '这是一条测试评论'
                }

                serializer = CommentSerializer(data=data)
                self.assertTrue(serializer.is_valid())

            def test_content_length_validation(self):
                """测试内容长度验证"""
                # 内容太短
                data = {
                    'post': self.post.id,
                    'content': '短'
                }

                serializer = CommentSerializer(data=data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('content', serializer.errors)

                # 内容太长
                data['content'] = 'x' * 2000  # 假设最大长度为1000

                serializer = CommentSerializer(data=data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('content', serializer.errors)

            def test_post_existence_validation(self):
                """测试文章存在性验证"""
                data = {
                    'post': 9999,  # 不存在的文章ID
                    'content': '这是一条测试评论'
                }

                serializer = CommentSerializer(data=data)
                self.assertFalse(serializer.is_valid())
                self.assertIn('post', serializer.errors)

            def test_custom_validation_methods(self):
                """测试自定义验证方法"""
                with patch('myapp.models.Post.comments.count') as mock_count:
                    mock_count.return_value = 100  # 假设最大评论数为50

                    data = {
                        'post': self.post.id,
                        'content': '这是一条测试评论'
                    }

                    serializer = CommentSerializer(data=data)
                    self.assertFalse(serializer.is_valid())
                    self.assertIn('non_field_errors', serializer.errors)

        # tests/test_serializer_performance.py - 序列化器性能测试
        import time
        from django.test.utils import override_settings

        class SerializerPerformanceTestCase(TestCase):
            """序列化器性能测试"""

            def setUp(self):
                """设置测试数据"""
                self.user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

                # 创建大量数据用于性能测试
                self.posts = []
                for i in range(100):
                    post = Post.objects.create(
                        title=f'文章{i}',
                        content=f'文章内容{i}',
                        author=self.user
                    )
                    self.posts.append(post)

            def test_serialization_performance(self):
                """测试序列化性能"""
                posts = Post.objects.all()

                start_time = time.time()
                serializer = PostSerializer(posts, many=True)
                data = serializer.data
                end_time = time.time()

                duration = end_time - start_time

                # 验证性能基准(例如应该在1秒内完成)
                self.assertLess(duration, 1.0, f"序列化100篇文章耗时 {duration:.2f} 秒")
                self.assertEqual(len(data), 100)

            def test_deserialization_performance(self):
                """测试反序列化性能"""
                data = [
                    {
                        'title': f'新文章{i}',
                        'content': f'新文章内容{i}'
                    }
                    for i in range(100)
                ]

                start_time = time.time()
                serializers = [PostSerializer(data=item) for item in data]
                valid_count = sum(1 for s in serializers if s.is_valid())
                end_time = time.time()

                duration = end_time - start_time

                self.assertLess(duration, 1.0, f"反序列化100条数据耗时 {duration:.2f} 秒")
                self.assertEqual(valid_count, 100)

            @override_settings(DEBUG=True)
            def test_query_optimization(self):
                """测试查询优化"""
                from django.db import connection
                from django.test.utils import override_settings

                # 未优化的序列化
                with override_settings(REST_FRAMEWORK={
                    'DEFAULT_SERIALIZER_CLASS': 'myapp.serializers.PostWithoutOptimizationSerializer'
                }):
                    start_time = time.time()
                    serializer = PostSerializer(self.posts, many=True)
                    data = serializer.data
                    end_time = time.time()
                    unoptimized_time = end_time - start_time
                    unoptimized_queries = len(connection.queries)

                    connection.queries_log.clear()

                # 优化的序列化
                with override_settings(REST_FRAMEWORK={
                    'DEFAULT_SERIALIZER_CLASS': 'myapp.serializers.OptimizedPostSerializer'
                }):
                    start_time = time.time()
                    serializer = PostSerializer(self.posts, many=True)
                    data = serializer.data
                    end_time = time.time()
                    optimized_time = end_time - start_time
                    optimized_queries = len(connection.queries)

                # 优化版本应该更快且查询次数更少
                self.assertLess(optimized_time, unoptimized_time)
                self.assertLess(optimized_queries, unoptimized_queries)

        # tests/test_serializer_edge_cases.py - 序列化器边缘情况测试
        class SerializerEdgeCasesTestCase(TestCase):
            """序列化器边缘情况测试"""

            def test_unicode_handling(self):
                """测试Unicode处理"""
                user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

                # 包含各种Unicode字符的数据
                unicode_data = {
                    'title': '🎉 Django REST Framework 中文测试 🚀',
                    'content': 'Emojis: 😊🎈\n中文内容\nالعربية\n한국어\n日本語',
                    'author': user.id
                }

                serializer = PostSerializer(data=unicode_data)
                self.assertTrue(serializer.is_valid())

                post = serializer.save(author=user)
                self.assertEqual(post.title, unicode_data['title'])
                self.assertEqual(post.content, unicode_data['content'])

            def test_large_data_handling(self):
                """测试大数据处理"""
                user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

                # 大内容数据
                large_content = 'x' * 1000000  # 1MB内容
                large_data = {
                    'title': '大内容文章',
                    'content': large_content,
                    'author': user.id
                }

                serializer = PostSerializer(data=large_data)
                self.assertTrue(serializer.is_valid())

            def test_null_and_empty_values(self):
                """测试null和空值处理"""
                user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

                # 测试可选字段的null值
                data_with_nulls = {
                    'title': '测试文章',
                    'content': '测试内容',
                    'author': user.id,
                    'category': None,  # 可选字段为null
                    'tags': []  # 空列表
                }

                serializer = PostSerializer(data=data_with_nulls)
                self.assertTrue(serializer.is_valid())

            def test_datetime_handling(self):
                """测试日期时间处理"""
                user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

                # 不同格式的日期时间
                datetime_cases = [
                    '2023-01-01T00:00:00Z',  # ISO格式
                    '2023-01-01 00:00:00',   # 本地时间格式
                    '2023-01-01T12:34:56.789',  # 带毫秒
                ]

                for datetime_str in datetime_cases:
                    with self.subTest(datetime_str=datetime_str):
                        data = {
                            'title': '定时发布文章',
                            'content': '内容',
                            'author': user.id,
                            'publish_at': datetime_str
                        }

                        serializer = PostSerializer(data=data)
                        self.assertTrue(serializer.is_valid())

            def test_recursive_data_handling(self):
                """测试递归数据处理"""
                # 测试嵌套评论
        user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

                post = Post.objects.create(
                    title='测试文章',
                    content='测试内容',
                    author=user
                )

                parent_comment = Comment.objects.create(
                    post=post,
                    content='父评论',
                    author=user
                )

                # 创建回复评论
                reply_data = {
                    'post': post.id,
                    'content': '回复评论',
                    'parent': parent_comment.id
                }

                serializer = CommentSerializer(data=reply_data)
                self.assertTrue(serializer.is_valid())

                reply = serializer.save()
                self.assertEqual(reply.parent, parent_comment)

            def test_concurrent_access(self):
                """测试并发访问"""
                import threading
                import time

                user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

                results = []
                errors = []

                def serialize_post():
                    try:
                        post = Post.objects.create(
                            title='并发测试文章',
                            content='内容',
                            author=user
                        )
                        serializer = PostSerializer(post)
                        results.append(serializer.data)
                    except Exception as e:
                        errors.append(str(e))

                # 创建多个线程同时序列化
                threads = []
                for _ in range(10):
                    thread = threading.Thread(target=serialize_post)
                    threads.append(thread)
                    thread.start()

                for thread in threads:
                    thread.join()

                # 验证没有错误
                self.assertEqual(len(errors), 0)
                self.assertEqual(len(results), 10)

        # 使用示例:
        # 1. 运行所有序列化器测试:
        # python manage.py test tests.test_serializers

        # 2. 运行特定测试类:
        # python manage.py test tests.test_serializers.PostSerializerTestCase

        # 3. 运行性能测试:
        # python manage.py test tests.test_serializer_performance

        # 4. 运行边缘情况测试:
        # python manage.py test tests.test_serializer_edge_cases

        # 5. 生成测试覆盖率报告:
        # coverage run --source='myapp.serializers' manage.py test tests.test_serializers
        # coverage report
        ---
    b.视图测试
        视图测试确保API端点的行为正确,包括HTTP方法、权限和数据流。
        ---
        # tests/test_views.py - 视图测试
        from django.test import TestCase
        from django.urls import reverse
        from rest_framework import status
        from rest_framework.test import APIRequestFactory, APITestCase
        from rest_framework.request import Request
        from django.contrib.auth import get_user_model
        from unittest.mock import patch, MagicMock
        import json

        from myapp.views import PostViewSet, UserRegistrationView
        from myapp.models import Post, Comment
        from myapp.serializers import PostSerializer

        User = get_user_model()

        class PostViewSetTestCase(APITestCase):
            """文章视图集测试"""

            def setUp(self):
                """设置测试数据"""
                self.factory = APIRequestFactory()
                self.user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )
                self.admin_user = User.objects.create_user(
                    username='admin',
                    password='adminpass123',
                    is_staff=True,
                    is_superuser=True
                )

                self.post = Post.objects.create(
                    title='测试文章',
                    content='测试内容',
                    author=self.user
                )

            def test_list_view_unauthenticated(self):
                """测试未认证的列表视图"""
                view = PostViewSet.as_view({'get': 'list'})
                request = self.factory.get('/api/posts/')
                response = view(request)

                self.assertEqual(response.status_code, status.HTTP_200_OK)

            def test_list_view_authenticated(self):
                """测试认证的列表视图"""
                view = PostViewSet.as_view({'get': 'list'})
                request = self.factory.get('/api/posts/')
                request.user = self.user
                response = view(request)

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertIsInstance(response.data, dict)

            def test_create_view_unauthorized(self):
                """测试未授权的创建视图"""
                view = PostViewSet.as_view({'post': 'create'})
                data = {
                    'title': '新文章',
                    'content': '新内容'
                }
                request = self.factory.post('/api/posts/', data, format='json')
                response = view(request)

                self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

            def test_create_view_authorized(self):
                """测试授权的创建视图"""
                view = PostViewSet.as_view({'post': 'create'})
                data = {
                    'title': '新文章',
                    'content': '新内容'
                }
                request = self.factory.post('/api/posts/', data, format='json')
                request.user = self.user
                response = view(request)

                self.assertEqual(response.status_code, status.HTTP_201_CREATED)
                self.assertEqual(response.data['title'], '新文章')

            def test_retrieve_view_existing_post(self):
                """测试获取存在的文章"""
                view = PostViewSet.as_view({'get': 'retrieve'})
                request = self.factory.get(f'/api/posts/{self.post.id}/')
                response = view(request, pk=self.post.id)

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(response.data['id'], self.post.id)

            def test_retrieve_view_nonexistent_post(self):
                """测试获取不存在的文章"""
                view = PostViewSet.as_view({'get': 'retrieve'})
                request = self.factory.get('/api/posts/99999/')
                response = view(request, pk=99999)

                self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

            def test_update_view_as_author(self):
                """测试作者更新文章"""
                view = PostViewSet.as_view({'patch': 'partial_update'})
                data = {'title': '更新后的标题'}
                request = self.factory.patch(f'/api/posts/{self.post.id}/', data, format='json')
                request.user = self.user
                response = view(request, pk=self.post.id)

                self.assertEqual(response.status_code, status.HTTP_200_OK)
                self.assertEqual(response.data['title'], '更新后的标题')

            def test_update_view_not_author(self):
                """测试非作者更新文章"""
                other_user = User.objects.create_user(
                    username='otheruser',
                    password='otherpass123'
                )

                view = PostViewSet.as_view({'patch': 'partial_update'})
                data = {'title': '更新后的标题'}
                request = self.factory.patch(f'/api/posts/{self.post.id}/', data, format='json')
                request.user = other_user
                response = view(request, pk=self.post.id)

                self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

            def test_delete_view_as_author(self):
                """测试作者删除文章"""
                view = PostViewSet.as_view({'delete': 'destroy'})
                request = self.factory.delete(f'/api/posts/{self.post.id}/')
                request.user = self.user
                response = view(request, pk=self.post.id)

                self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
                self.assertFalse(Post.objects.filter(id=self.post.id).exists())

            def test_delete_view_as_admin(self):
                """测试管理员删除文章"""
                view = PostViewSet.as_view({'delete': 'destroy'})
                request = self.factory.delete(f'/api/posts/{self.post.id}/')
                request.user = self.admin_user
                response = view(request, pk=self.post.id)

                self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

            def test_get_queryset_method(self):
                """测试get_queryset方法"""
                # 创建不同状态的文章
                Post.objects.create(title='草稿', content='内容', author=self.user, status='draft')
                Post.objects.create(title='已发布', content='内容', author=self.user, status='published')

                view = PostViewSet()
                view.request = self.factory.get('/api/posts/')
                view.request.user = self.user

                queryset = view.get_queryset()
                self.assertEqual(queryset.count(), 3)  # 包括setUp中的文章

            def test_get_object_method(self):
                """测试get_object方法"""
                view = PostViewSet()
                view.kwargs = {'pk': self.post.id}

                obj = view.get_object()
                self.assertEqual(obj, self.post)

            def test_permissions_enforced(self):
                """测试权限强制执行"""
                # 创建一个需要管理员权限的自定义action
        view = PostViewSet.as_view({'get': 'admin_only_action'})
                request = self.factory.get('/api/posts/admin-only/')
                request.user = self.user  # 普通用户

                with patch.object(view, 'admin_only_action') as mock_action:
                    mock_action.return_value = Response({'message': 'success'})
                    response = view(request)

                    # 普通用户应该被拒绝访问
                    self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

            def test_serializer_selection(self):
                """测试序列化器选择"""
                view = PostViewSet()
                view.action = 'list'
                view.request = self.factory.get('/api/posts/')

                serializer_class = view.get_serializer_class()
                self.assertEqual(serializer_class, PostSerializer)

                view.action = 'create'
                serializer_class = view.get_serializer_class()
                # 可能返回不同的序列化器用于创建

            def test_custom_action_methods(self):
                """测试自定义action方法"""
                # 测试自定义action路由
        view = PostViewSet.as_view({'get': 'custom_action'})
                request = self.factory.get('/api/posts/custom-action/')
                request.user = self.user

                with patch.object(view, 'custom_action') as mock_action:
                    mock_action.return_value = Response({'message': 'custom action executed'})
                    response = view(request)

                    mock_action.assert_called_once()
                    self.assertEqual(response.status_code, status.HTTP_200_OK)

        class UserRegistrationViewTestCase(APITestCase):
            """用户注册视图测试"""

            def setUp(self):
                """设置测试"""
                self.factory = APIRequestFactory()
                self.valid_data = {
                    'username': 'newuser',
                    'email': '[email protected]',
                    'password': 'securepass123',
                    'password_confirm': 'securepass123'
                }

            def test_registration_success(self):
                """测试注册成功"""
                view = UserRegistrationView.as_view()
                request = self.factory.post('/api/register/', self.valid_data, format='json')
                response = view(request)

                self.assertEqual(response.status_code, status.HTTP_201_CREATED)
                self.assertIn('token', response.data)
                self.assertIn('user', response.data)

            def test_registration_invalid_data(self):
                """测试无效数据注册"""
                invalid_data = self.valid_data.copy()
                invalid_data['email'] = 'invalid-email'

                view = UserRegistrationView.as_view()
                request = self.factory.post('/api/register/', invalid_data, format='json')
                response = view(request)

                self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

            def test_registration_rate_limiting(self):
                """测试注册频率限制"""
                view = UserRegistrationView.as_view()

                # 模拟多次快速注册尝试
                responses = []
                for i in range(5):
                    data = self.valid_data.copy()
                    data['username'] = f'user{i}'
                    data['email'] = f'user{i}@example.com'

                    request = self.factory.post('/api/register/', data, format='json')
                    response = view(request)
                    responses.append(response)

                # 检查是否有请求被限制
                rate_limited = any(r.status_code == 429 for r in responses)
                self.assertTrue(rate_limited, "Expected rate limiting to be enforced")

        class ViewMethodTestCase(TestCase):
            """视图方法测试"""

            def setUp(self):
                """设置测试"""
                self.factory = APIRequestFactory()
                self.user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

            def test_perform_create_method(self):
                """测试perform_create方法"""
                view = PostViewSet()
                view.request = self.factory.post('/api/posts/', {}, format='json')
                view.request.user = self.user

                serializer = MagicMock()
                serializer.save = MagicMock()

                view.perform_create(serializer)
                serializer.save.assert_called_once_with(author=self.user)

            def test_perform_update_method(self):
                """测试perform_update方法"""
                view = PostViewSet()
                view.request = self.factory.patch('/api/posts/1/', {}, format='json')
                view.request.user = self.user

                instance = MagicMock()
                serializer = MagicMock()
                serializer.save = MagicMock()

                view.perform_update(serializer)
                serializer.save.assert_called_once()

            def test_get_serializer_context(self):
                """测试get_serializer_context方法"""
                view = PostViewSet()
                view.request = self.factory.get('/api/posts/')
                view.request.user = self.user

                context = view.get_serializer_context()
                self.assertIn('request', context)
                self.assertIn('format', context)
                self.assertEqual(context['request'], view.request)

        class ViewIntegrationTestCase(APITestCase):
            """视图集成测试"""

            def setUp(self):
                """设置测试"""
                self.user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )
                self.token = Token.objects.create(user=self.user)
                self.auth_header = {'HTTP_AUTHORIZATION': f'Token {self.token.key}'}

            def test_full_workflow(self):
                """测试完整的API工作流程"""
                # 1. 创建文章
                create_data = {
                    'title': '新文章',
                    'content': '文章内容'
                }
                create_response = self.client.post('/api/posts/', create_data, **self.auth_header, format='json')
                self.assertEqual(create_response.status_code, status.HTTP_201_CREATED)
                post_id = create_response.data['id']

                # 2. 获取文章列表
                list_response = self.client.get('/api/posts/', **self.auth_header)
                self.assertEqual(list_response.status_code, status.HTTP_200_OK)
                self.assertTrue(any(p['id'] == post_id for p in list_response.data['results']))

                # 3. 获取文章详情
                detail_response = self.client.get(f'/api/posts/{post_id}/', **self.auth_header)
                self.assertEqual(detail_response.status_code, status.HTTP_200_OK)
                self.assertEqual(detail_response.data['id'], post_id)

                # 4. 更新文章
                update_data = {'title': '更新后的标题'}
                update_response = self.client.patch(f'/api/posts/{post_id}/', update_data, **self.auth_header, format='json')
                self.assertEqual(update_response.status_code, status.HTTP_200_OK)
                self.assertEqual(update_response.data['title'], '更新后的标题')

                # 5. 添加评论
                comment_data = {
                    'post': post_id,
                    'content': '这是一条评论'
                }
                comment_response = self.client.post('/api/comments/', comment_data, **self.auth_header, format='json')
                self.assertEqual(comment_response.status_code, status.HTTP_201_CREATED)

                # 6. 删除文章
                delete_response = self.client.delete(f'/api/posts/{post_id}/', **self.auth_header)
                self.assertEqual(delete_response.status_code, status.HTTP_204_NO_CONTENT)

        # tests/test_view_permissions.py - 视图权限测试
        class ViewPermissionTestCase(APITestCase):
            """视图权限测试"""

            def setUp(self):
                """设置测试用户"""
                self.regular_user = User.objects.create_user(
                    username='regular',
                    password='regularpass'
                )
                self.staff_user = User.objects.create_user(
                    username='staff',
                    password='staffpass',
                    is_staff=True
                )
                self.superuser = User.objects.create_user(
                    username='super',
                    password='superpass',
                    is_superuser=True
                )

                self.regular_token = Token.objects.create(user=self.regular_user)
                self.staff_token = Token.objects.create(user=self.staff_user)
                self.super_token = Token.objects.create(user=self.superuser)

            def test_regular_user_permissions(self):
                """测试普通用户权限"""
                auth_header = {'HTTP_AUTHORIZATION': f'Token {self.regular_token.key}'}

                # 可以访问公开端点
                response = self.client.get('/api/posts/', **auth_header)
                self.assertIn(response.status_code, [200, 401])

                # 不能访问管理员端点
                response = self.client.get('/api/admin/stats/', **auth_header)
                self.assertEqual(response.status_code, 403)

            def test_staff_user_permissions(self):
                """测试员工用户权限"""
                auth_header = {'HTTP_AUTHORIZATION': f'Token {self.staff_token.key}'}

                # 可以访问员工端点
                response = self.client.get('/api/staff/review-queue/', **auth_header)
                self.assertEqual(response.status_code, 200)

                # 不能访问超级用户端点
                response = self.client.get('/api/super/system-config/', **auth_header)
                self.assertEqual(response.status_code, 403)

            def test_superuser_permissions(self):
                """测试超级用户权限"""
                auth_header = {'HTTP_AUTHORIZATION': f'Token {self.super_token.key}'}

                # 可以访问所有端点
                response = self.client.get('/api/super/system-config/', **auth_header)
                self.assertEqual(response.status_code, 200)

        # 使用示例:
        # 1. 运行所有视图测试:
        # python manage.py test tests.test_views

        # 2. 运行特定测试类:
        # python manage.py test tests.test_views.PostViewSetTestCase

        # 3. 运行权限测试:
        # python manage.py test tests.test_view_permissions

        # 4. 运行集成测试:
        # python manage.py test tests.test_views.ViewIntegrationTestCase

        # 5. 测试特定HTTP方法:
        # python manage.py test tests.test_views.PostViewSetTestCase.test_create_view_authorized
        ---
    c.权限测试
        权限测试确保API端点的访问控制正确实现,保护敏感数据和操作。
        ---
        # tests/test_permissions.py - 权限系统测试
        from django.test import TestCase
        from django.contrib.auth import get_user_model
        from rest_framework.test import APITestCase
        from rest_framework import status
        from rest_framework.permissions import IsAuthenticated, IsAdminUser
        from unittest.mock import patch, MagicMock
        from myapp.permissions import IsAuthorOrReadOnly, IsOwnerOrStaff
        from myapp.models import Post, Comment

        User = get_user_model()

        class PermissionClassTestCase(TestCase):
            """权限类测试"""

            def test_is_authenticated_permission(self):
                """测试IsAuthenticated权限"""
                permission = IsAuthenticated()

                # 未认证用户
                request = MagicMock()
                request.user.is_authenticated = False
                self.assertFalse(permission.has_permission(request, None))

                # 已认证用户
                request.user.is_authenticated = True
                self.assertTrue(permission.has_permission(request, None))

            def test_is_admin_user_permission(self):
                """测试IsAdminUser权限"""
                permission = IsAdminUser()

                # 普通用户
                request = MagicMock()
                request.user.is_staff = False
                request.user.is_superuser = False
                self.assertFalse(permission.has_permission(request, None))

                # 员工用户
                request.user.is_staff = True
                request.user.is_superuser = False
                self.assertTrue(permission.has_permission(request, None))

                # 超级用户
                request.user.is_staff = True
                request.user.is_superuser = True
                self.assertTrue(permission.has_permission(request, None))

            def test_is_author_or_read_only_permission(self):
                """测试IsAuthorOrReadOnly权限"""
                permission = IsAuthorOrReadOnly()

                # 创建用户和文章
                author = User.objects.create_user(username='author', password='pass123')
                other_user = User.objects.create_user(username='other', password='pass123')
                post = Post.objects.create(title='Test', content='Content', author=author)

                # 安全方法(GET, HEAD, OPTIONS)
                safe_request = MagicMock()
                safe_request.method = 'GET'
                self.assertTrue(permission.has_permission(safe_request, None))

                # 非安全方法,但用户是作者
                author_request = MagicMock()
                author_request.method = 'POST'
                author_request.user = author
                self.assertTrue(permission.has_object_permission(author_request, None, post))

                # 非安全方法,用户不是作者
                other_request = MagicMock()
                other_request.method = 'POST'
                other_request.user = other_user
                self.assertFalse(permission.has_object_permission(other_request, None, post))

            def test_is_owner_or_staff_permission(self):
                """测试IsOwnerOrStaff权限"""
                permission = IsOwnerOrStaff()

                # 创建用户
                owner = User.objects.create_user(username='owner', password='pass123')
                staff_user = User.objects.create_user(username='staff', password='pass123', is_staff=True)
                regular_user = User.objects.create_user(username='regular', password='pass123')

                # 创建对象(假设有owner字段)
                obj = MagicMock()
                obj.owner = owner

                # 所有者访问
                owner_request = MagicMock()
                owner_request.user = owner
                self.assertTrue(permission.has_object_permission(owner_request, None, obj))

                # 员工访问
                staff_request = MagicMock()
                staff_request.user = staff_user
                self.assertTrue(permission.has_object_permission(staff_request, None, obj))

                # 普通用户访问
                regular_request = MagicMock()
                regular_request.user = regular_user
                self.assertFalse(permission.has_object_permission(regular_request, None, obj))

        class PermissionIntegrationTestCase(APITestCase):
            """权限集成测试"""

            def setUp(self):
                """设置测试数据"""
                # 创建不同权限的用户
                self.owner = User.objects.create_user(
                    username='owner',
                    password='ownerpass123'
                )
                self.staff = User.objects.create_user(
                    username='staff',
                    password='staffpass123',
                    is_staff=True
                )
                self.superuser = User.objects.create_user(
                    username='superuser',
                    password='superpass123',
                    is_superuser=True
                )
                self.regular_user = User.objects.create_user(
                    username='regular',
                    password='regularpass123'
                )

                # 创建Token
                from rest_framework.authtoken.models import Token
                self.owner_token = Token.objects.create(user=self.owner)
                self.staff_token = Token.objects.create(user=self.staff)
                self.super_token = Token.objects.create(user=self.superuser)
                self.regular_token = Token.objects.create(user=self.regular_user)

                # 创建测试文章
                self.post = Post.objects.create(
                    title='测试文章',
                    content='测试内容',
                    author=self.owner
                )

            def test_post_permissions(self):
                """测试文章权限"""
                owner_auth = {'HTTP_AUTHORIZATION': f'Token {self.owner_token.key}'}
                regular_auth = {'HTTP_AUTHORIZATION': f'Token {self.regular_token.key}'}
                staff_auth = {'HTTP_AUTHIZATION': f'Token {self.staff_token.key}'}

                # 作者可以更新自己的文章
                response = self.client.patch(
                    f'/api/posts/{self.post.id}/',
                    {'title': '更新标题'},
                    **owner_auth,
                    format='json'
                )
                self.assertEqual(response.status_code, 200)

                # 普通用户不能更新他人的文章
                response = self.client.patch(
                    f'/api/posts/{self.post.id}/',
                    {'title': '更新标题'},
                    **regular_auth,
                    format='json'
                )
                self.assertEqual(response.status_code, 403)

                # 员工可以更新他人的文章(根据权限设置)
                response = self.client.patch(
                    f'/api/posts/{self.post.id}/',
                    {'title': '更新标题'},
                    **staff_auth,
                    format='json'
                )
                # 这个结果取决于具体的权限设置
                self.assertIn(response.status_code, [200, 403])

            def test_comment_permissions(self):
                """测试评论权限"""
                owner_auth = {'HTTP_AUTHORIZATION': f'Token {self.owner_token.key}'}
                regular_auth = {'HTTP_AUTHORIZATION': f'Token {self.regular_token.key}'}

                # 任何人都可以创建评论(如果允许)
                comment_data = {
                    'post': self.post.id,
                    'content': '这是一条评论'
                }

                response = self.client.post(
                    '/api/comments/',
                    comment_data,
                    **regular_auth,
                    format='json'
                )
                self.assertIn(response.status_code, [201, 403])  # 取决于权限设置

                # 作者可以删除文章的评论
                comment = Comment.objects.create(
                    post=self.post,
                    content='测试评论',
                    author=self.regular_user
                )

                response = self.client.delete(
                    f'/api/comments/{comment.id}/',
                    **owner_auth
                )
                self.assertIn(response.status_code, [204, 403])  # 取决于权限设置

            def test_admin_only_endpoints(self):
                """测试仅管理员端点"""
                regular_auth = {'HTTP_AUTHORIZATION': f'Token {self.regular_token.key}'}
                staff_auth = {'HTTP_AUTHORIZATION': f'Token {self.staff_token.key}'}
                super_auth = {'HTTP_AUTHORIZATION': f'Token {self.super_token.key}'}

                # 普通用户无法访问管理员端点
                response = self.client.get('/api/admin/dashboard/', **regular_auth)
                self.assertEqual(response.status_code, 403)

                # 员工可以访问管理员端点
                response = self.client.get('/api/admin/dashboard/', **staff_auth)
                self.assertEqual(response.status_code, 200)

                # 超级用户可以访问所有端点
                response = self.client.get('/api/super/system/', **super_auth)
                self.assertEqual(response.status_code, 200)

            def test_object_level_permissions(self):
                """测试对象级权限"""
                owner_auth = {'HTTP_AUTHORIZATION': f'Token {self.owner_token.key}'}
                regular_auth = {'HTTP_AUTHORIZATION': f'Token {self.regular_token.key}'}

                # 创建属于不同用户的私有对象
                other_post = Post.objects.create(
                    title='其他用户的文章',
                    content='内容',
                    author=self.regular_user
                )

                # 用户无法访问其他用户的私有文章
                response = self.client.get(
                    f'/api/posts/{other_post.id}/private/',
                    **owner_auth
                )
                self.assertEqual(response.status_code, 403)

                # 用户可以访问自己的私有文章
                response = self.client.get(
                    f'/api/posts/{self.post.id}/private/',
                    **owner_auth
                )
                self.assertEqual(response.status_code, 200)

        class PermissionEdgeCasesTestCase(APITestCase):
            """权限边缘情况测试"""

            def setUp(self):
                """设置测试"""
                self.user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )
                self.token = Token.objects.create(user=self.user)
                self.auth = {'HTTP_AUTHORIZATION': f'Token {self.token.key}'}

            def test_permission_with_deleted_user(self):
                """测试删除用户的权限"""
                # 创建文章
                post = Post.objects.create(
                    title='测试文章',
                    content='内容',
                    author=self.user
                )

                # 删除用户
                self.user.delete()

                # 尝试访问应该失败
                response = self.client.get(f'/api/posts/{post.id}/', **self.auth)
                self.assertEqual(response.status_code, 401)

            def test_permission_with_inactive_user(self):
                """测试非活跃用户的权限"""
                # 禁用用户
                self.user.is_active = False
                self.user.save()

                # 尝试访问应该失败
                response = self.client.get('/api/posts/', **self.auth)
                self.assertEqual(response.status_code, 401)

            def test_permission_with_expired_token(self):
                """测试过期Token的权限"""
                # 模拟过期Token
                from django.utils import timezone
                from datetime import timedelta

                self.token.created = timezone.now() - timedelta(days=365)
                self.token.save()

                # 根据Token过期策略,可能返回401或仍然有效
                response = self.client.get('/api/posts/', **self.auth)
                self.assertIn(response.status_code, [200, 401])

            def test_permission_with_malformed_token(self):
                """测试畸形Token的权限"""
                malformed_auth = {'HTTP_AUTHORIZATION': 'Token malformed_token'}

                response = self.client.get('/api/posts/', **malformed_auth)
                self.assertEqual(response.status_code, 401)

            def test_permission_with_concurrent_access(self):
                """测试并发访问的权限"""
                import threading
        import time

                results = []

                def make_request():
                    response = self.client.get('/api/posts/', **self.auth)
                    results.append(response.status_code)

                # 创建多个线程同时访问
                threads = []
                for _ in range(10):
                    thread = threading.Thread(target=make_request)
                    threads.append(thread)
                    thread.start()

                for thread in threads:
                    thread.join()

                # 所有请求都应该返回相同的状态码
                self.assertTrue(all(code == results[0] for code in results))

        class CustomPermissionTestCase(TestCase):
            """自定义权限测试"""

            def test_custom_permission_class(self):
                """测试自定义权限类"""
                from myapp.permissions import CustomPermission

                permission = CustomPermission()
                request = MagicMock()

                # 测试自定义逻辑
                request.user = MagicMock()
                request.user.has_custom_permission = MagicMock(return_value=True)

                result = permission.has_permission(request, None)
                self.assertTrue(result)

                request.user.has_custom_permission.return_value = False
                result = permission.has_permission(request, None)
                self.assertFalse(result)

            def test_permission_inheritance(self):
                """测试权限类继承"""
                from myapp.permissions import ExtendedPermission

                permission = ExtendedPermission()
                request = MagicMock()
                request.user.is_authenticated = True

                # 测试继承的基础权限
                self.assertTrue(permission.has_permission(request, None))

                # 测试扩展的权限检查
                request.user.has_extended_permission = MagicMock(return_value=True)
                self.assertTrue(permission.has_object_permission(request, None, MagicMock()))

        # tests/test_permission_performance.py - 权限性能测试
        class PermissionPerformanceTestCase(TestCase):
            """权限性能测试"""

            def setUp(self):
                """设置测试"""
                self.user = User.objects.create_user(
                    username='testuser',
                    password='testpass123'
                )

            def test_permission_check_performance(self):
                """测试权限检查性能"""
                import time
                from myapp.permissions import IsAuthorOrReadOnly

                permission = IsAuthorOrReadOnly()
                post = Post.objects.create(
                    title='测试',
                    content='内容',
                    author=self.user
                )

                # 创建请求
                request = MagicMock()
                request.user = self.user
                request.method = 'GET'

                # 测试多次权限检查
                start_time = time.time()
                for _ in range(1000):
                    permission.has_object_permission(request, None, post)
                end_time = time.time()

                duration = end_time - start_time
                self.assertLess(duration, 1.0, "权限检查性能不达标")

        # 使用示例:
        # 1. 运行所有权限测试:
        # python manage.py test tests.test_permissions

        # 2. 运行权限类测试:
        # python manage.py test tests.test_permissions.PermissionClassTestCase

        # 3. 运行权限集成测试:
        # python manage.py test tests.test_permissions.PermissionIntegrationTestCase

        # 4. 运行边缘情况测试:
        # python manage.py test tests.test_permissions.PermissionEdgeCasesTestCase

        # 5. 运行自定义权限测试:
        # python manage.py test tests.test_permissions.CustomPermissionTestCase
        ---