DRF的使用

06-01 1300阅读

1. DRF概述

DRF即django rest framework,是一个基于Django的Web API框架,专门用于构建RESTful API接口。DRF的核心特点包括:

  • 序列化:通过序列化工具,DRF能够轻松地将Django模型转换为JSON格式,也可以将JSON格式的数据反序列化为Python对象。
  • 认证与权限管理:DRF提供了多种认证方式,如Token认证、OAuth等以及权限控制,确保API的访问是安全的。
  • 视图与路由优化:DRF简化了基于类的视图,允许快速构建RESTful API端点,减少重复代码。ViewSet是它的一大特色,使得定义和处理API端点变得更加清晰。并且可以结合Router自动生成URL路由,简化API端点配置。
  • 分页与过滤:支持分页、搜索和过滤功能,可以轻松地管理大规模的数据集。
  • 强大的文档生成:DRF可以与Swagger等工具集成,自动生成API文档,帮助前端开发者和其他开发人员理解API接口。

    总体来说,DRF让Django项目中的API开发更加高效、灵活、且可维护,非常适合构建前后端分离的项目。推荐使用Django 4.2 + DRF 3.15.2进行新项目的开发。包管理工具推荐使用pip,如果涉及机器学习、数据分析等才使用conda,不建议两种混用。原因如下:

    • 🐍 官方标准包管理器:Django 官方文档、教程都基于 pip。
    • 📦 丰富的第三方库:绝大多数 Django 插件、中间件都通过 PyPI 发布。
    • ⚙️ 轻量灵活:适合现代开发流程,如结合 poetry、pipenv 使用。
    • 🚀 部署友好:与 requirements.txt 配合良好,便于 CI/CD 和容器化部署。

      要在现有的Django 项目中集成DRF,需要安装 djangorestframework 包,如下:

      pip install django==4.2 djangorestframework==3.15.2
      

      安装之后在settings.py中注册rest_framework,如下:

      INSTALLED_APPS = [
          # 其他Django应用
          'rest_framework',
      ]
      

      如果想要修改drf的配置文件,只需要在settings.py中添加配置项REST_FRAMeWORK,如下:

      REST_FRAMEWORK = {
          # 关闭未认证用户的默认行为, 如自定义了身份认证机制[JWT、Token、Oauth2]
      	"UNAUTHENTICATED_USER": None
      }
      

      2. 用户模型

      Django的用户模型是用户认证系统的核心部分,用于表示网站的用户,包括用户名、密码、邮箱、权限等信息。Django默认的用户模型是django.contrib.auth.models.User模型,它继承自django.contrib.auth.models.AbstractUser,代码如下:

      class User(AbstractUser):
          class Meta(AbstractUser.Meta):
              # 标识这个模型是可以被交换的, 允许开发者通过AUTH_USER_MODEL指定一个自定义用户模型来替代默认的User模型
              swappable = "AUTH_USER_MODEL"
      

      2.1 AbstractUser

      AbstractUser继承自django.contrib.auth.base_user.AbstractBaseUser,有以下默认字段:该用户模型适合功能不复杂的后台管理系统,且无需扩展用户字段的项目。

      字段名称类型描述默认值/约束条件
      usernameCharField唯一用户名,用于登录系统,允许字母、数字和特定符号max_length=150,必填且唯一
      passwordCharField加密存储的密码,使用 PBKDF2 或 Argon2 算法max_length=128,必填
      emailEmailField用户邮箱地址,非必填项可选,默认空字符串
      first_nameCharField用户名字段,用于存储用户的名max_length=150,可选
      last_nameCharField用户姓氏字段,用于存储用户的姓max_length=150,可选
      is_staffBooleanField标识用户是否有权限访问 Django Admin 后台默认False
      is_activeBooleanField标识用户账号是否激活默认True
      is_superuserBooleanField超级管理员标识,拥有所有权限默认False
      date_joinedDateTimeField用户注册时间默认当前时间 【timezone.now】
      last_loginDateTimeField用户最后一次登录时间可为空,初始值为None

      2.2 自定义用户模型

      由于AbstractUser的局限性,我们可以自定义用户模型,只需要继承AbstractUser 【推荐】或 AbstractBaseUser即可,如下:

      # apps/users/models.py
      from django.contrib.auth.models import AbstractUser
      from django.db import models
      class CustomUser(AbstractUser):
          # 扩展字段
          phone = models.CharField(max_length=11, unique=True, null=True, blank=True)
          nickname = models.CharField(max_length=20, blank=True, null=True)
          
          # 覆盖不想要的AbstractUser模型的字段
          first_name = None
          last_name = None
      	
          class Meta:
              db_table = "custom_user"
              verbose_name = "用户"
              verbose_name_plural = "用户"
      
      # 指定用户模型为自定义用户模型
      AUTH_USER_MODEL = 'users.CustomUser'
      

      操作用户模型的相关API如下:

      from django.contrib.auth import get_user_model
      # 获取当前项目正在使用的用户模型
      User = get_user_model()
      # 创建用户
      user = User.objects.create_user(username='aaa', email='aaa@qq.com', password='123456')
      # 查询用户
      user = User.objects.get(email='aaa@qq.com')
      # 检查密码
      user.check_password('123456')
      

      如果是多用户角色,有以下两种常见做法:

      • 单一用户模型 + 角色字段:该方法的局限性在于所有用户数据存储在一张表中,且由于不同角色字段的不同,会导致很多空字段。

        class User(AbstractUser):
            ROLE_CHOICES = (
                ('student', '学生'),
                ('teacher', '教师'),
                ('admin', '管理员'),
            )
            role = models.CharField(max_length=10, choices=ROLE_CHOICES)
            # 公共字段, 如手机号、头像等
            ...
            # 学生专属字段
            ...
            # 教师专属字段
            ...
        
      • User + OneToOne角色扩展表【推荐】:一个Django项目只能有一个全局的用户模型,因此多用户角色可以使用角色扩展表实现。该设计将不相同的字段存储在用户表中,将通用字段存储在了主表中。

        class User(AbstractUser):
            role = models.CharField(max_length=10, choices=[('student', '学生'), ('teacher', '教师'), ('admin', '管理员')])
            class Meta:
                verbose_name = '用户信息'
                verbose_name_plural = '用户信息'
                db_table = 'users'
        class StudentProfile(models.Model):
            user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='student_profile')
            student_id = models.CharField(max_length=20)
            grade = models.CharField(max_length=10)
        class TeacherProfile(models.Model):
            user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='teacher_profile')
            title = models.CharField(max_length=50)
            department = models.CharField(max_length=100)
        class AdminProfile(models.Model):
            user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='admin_profile')
            admin_code = models.CharField(max_length=50)
        
        # 创建超级管理员, 用于登录Django后台, 信息保存在users表中
        python manage.py createsuperuser
        
        # 创建其它用户角色
        class CreateStudentView(APIView):
            def post(self, request):
                username = request.data.get('username')
                password = request.data.get('password')
                role = request.data.get('role')
                # 创建学生用户
                user = User.objects.create_user(username=username, password=password, role=role)
                StudentProfile.objects.create(user=user, student_id="S123456", grade="2023")
                return Response({'message': 'ok'})
        
        # 将用户模型注册到管理后台中
        # users/admin.py
        # Register your models here.
        from django.contrib import admin
        from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
        from .models import User, StudentProfile, TeacherProfile, AdminProfile
        @admin.register(User)
        class UserAdmin(BaseUserAdmin):
            # 控制页面中要展示的字段
            list_display = ('username', 'email', 'role', 'is_active', 'is_staff', 'is_superuser')
            # 添加侧边栏过滤器,快速筛选数据
            list_filter = ('role', 'is_active', 'is_staff')
            # 启用后台搜索框,支持模糊查找,允许通过哪些字段查询
            search_fields = ('username', 'email')
            # 控制用户编辑页中字段的分组和顺序
            fieldsets = BaseUserAdmin.fieldsets + (
                ('角色信息', {'fields': ('role',)}),
            )
        @admin.register(StudentProfile)
        class StudentProfileAdmin(admin.ModelAdmin):
            list_display = ('user', 'student_id', 'grade')
            search_fields = ('student_id', 'user__username')
        @admin.register(TeacherProfile)
        class TeacherProfileAdmin(admin.ModelAdmin):
            list_display = ('user', 'title', 'department')
            # 注意双下划线
            search_fields = ('user__username', 'department')
        @admin.register(AdminProfile)
        class AdminProfileAdmin(admin.ModelAdmin):
            list_display = ('user', 'admin_code')
            search_fields = ('user__username', 'admin_code')
        

        3. 认证后端

        认证后端是Django中处理登录身份验证的核心机制,用来判断账户提供的用户名和密码【或其它凭证】是否合法,并返回合法的用户对象User或None。当调用authenticate方法时会触发认证后端,如下:

        from django.contrib.auth import authenticate, login
        user = authenticate(request, username="admin", password="123456")
        # 返回成功后可以保存session
        from django.contrib.auth import login
        login(request, user)
        # 当然也可以销毁session
        logout(request)
        

        Django会遍历AUTHENTICATION_BACKENDS 中配置的每一个认证后端,按顺序执行它们的authenticate方法,直到有一个返回User对象,否则返回None。

        Django中默认的认证后端是django.contrib.auth.backends.ModelBackend,它的行为是查找用户username,比对密码password。如果想要实现邮箱登录或其他登录,则需要自定义认证后端:

        # apps/users/backends.py
        from django.contrib.auth.backends import ModelBackend
        from django.contrib.auth import get_user_model
        User = get_user_model()
        class EmailBackend(ModelBackend):
            def authenticate(self, request, username=None, password=None, **kwargs):
                try:
                    user = User.objects.get(email=username)  # username 实际是 email
                    if user.check_password(password):
                        return user
                except User.DoesNotExist:
                    return None
        
        # settings.py
        AUTHENTICATION_BACKENDS = [
            'apps.users.backends.EmailBackend',
        ]
        
        # 在登录视图中调用
        @api_view(['POST'])
        def login_view(request):
            email = request.data.get('email')
            password = request.data.get('password')
            # 仍然传入username=email
            user = authenticate(request, username=email, password=password)
            if user:
                login(request, user)
                return Response({'msg': '登录成功'})
            return Response({'msg': '登录失败'}, status=401)
        

        认证后端与认证组件的区别与联系如下:

        项目认证后端认证组件
        属于Django核心DRF
        什么时候用登录视图中调用authenticate每个API请求识别用户
        作用识别账号 + 密码的是否合法识别这个请求是谁发的
        输入用户名/密码或其它凭证token、cookie、JWT
        举例邮箱登录、自定义账号逻辑自定义 token、cookie、JWT
        是否每次请求执行❌ 只在登录时用一次✅ 每个受保护 API 请求都会调用
        是否与authenticate有关无关

        它们的配合流程如下:

        [ 用户请求登录 ]
              ↓
        [ 调用 authenticate(username=xxx, password=xxx) ]
              ↓
        [ 遍历 Authentication Backend → 返回 user ]
              ↓
        [ 调用 login(request, user) → 设置 session或token ]
        =========================
        [ 之后每个 API 请求 ]
              ↓
        [ 调用 Authentication Class → 从 token/session 中识别用户 ]
              ↓
        [ 返回 (user, auth) → 自动赋值 request.user ]
        

        ✅ 正确理解整个 Token 登录流程:

        阶段你应该做的事使用什么
        登录视图中✅ 调用 authenticate(username=..., password=...) 验证账号密码调用 Django 的认证后端(AUTHENTICATION_BACKENDS)
        登录成功后✅ 创建或获取 token使用 Token.objects.get_or_create(user=user)
        返回给前端✅ 返回 {"token": "xxx"}用 JsonResponse / Response 返回即可
        后续请求验证❌ 不用你手动做,DRF 自动用 TokenAuthentication 来解析请求它会从请求头中找 token,并识别用户

        4. 认证组件

        在DRF中,认证组件用于验证用户的身份【你是谁】,判断用户是否登录,确保只有经过授权的用户才能访问特定的API视图。DRF提供了多种认证类,每种认证类都实现了特定的认证逻辑。常见的认证类包括:

        • BasicAuthentication:基于HTTP基本认证,通过用户名和密码进行认证。
        • SessionAuthentication:基于Django的会话机制进行认证,适用于基于浏览器的交互。
        • TokenAuthentication:基于令牌Token进行认证,适用于前后端分离的应用。
        • JSONWebTokenAuthentication:需要安装 djangorestframework-jwt 库,基于JSON Web Token即JWT进行认证,是一种无状态的认证方式。
        • Custom Authentication:自定义认证类,有时内置的认证类无法满足特定需求,这时可以自定义认证类。DRF允许我们通过继承 BaseAuthentication 类来实现自己的认证逻辑。

          认证组件必须配合权限组件一起使用,不然认证就没有意义,因为默认权限是AllowAny,表示所有人都可以访问。就好比你进入一栋写字楼,authentication是你出示身份证,确认你是谁?permission是保安根据规则判断你能不能进入大门。如果你只配置了认证,但没有规定保安不让陌生人进,那默认每个人都可以进 —— 所以你访问不需要登录也能成功。

          4.1 BasicAuthentication

          BasicAuthentication 是一种简单的 HTTP 认证方式,通常用于 REST API 接口,在开发和测试环境中使用频繁,它也是Django中默认的认证组件,需配合isAuthenticated使用。客户端发送请求时,如果没有认证头时则返回401 Unauthorized响应,并且包含www-Authenticate头。当包含认证头时,则会解析认证头,提取用户民和密码,并验证其合法性。BasicAuthentication的优缺点如下:

          • 优点:简单易用,兼容性好,不依赖Cookie和Session,适用于无状态的API服务。适合用于开发和测试环境,但是不能用于生产环境。

          • 缺点:安全性低,用户名及密码暴露在认证头中;无会话机制,每次请求都要携带账号密码,效率低且不适合长期登录;不支持登出机制,除非客户端不再发送请求头;用户体验差,需要频繁的输入用户名和密码等。

            REST_FRAMEWORK = {
                # 'UNAUTHENTICATED_USERNAME': None
                'DEFAULT_AUTHENTICATION_CLASSES': (
                    'rest_framework.authentication.BasicAuthentication',
                ),
                'DEFAULT_PERMISSION_CLASSES': (
                    'rest_framework.permissions.IsAuthenticated',
                ),
            }
            
            class HelloView(APIView):
                def get(self, request):
                    return Response({"message": f"Hello, {request.user.username}!"})
            

            DRF的使用

            DRF的使用

            4.2 SessionAuthentication

            SessionAuthentication 是基于 Django 自带的会话Session机制的认证方式。它的原理是:

            • 用户通过 Django 的登录视图登录成功,Django 创建一个 Session,服务器保存 session 数据,同时发给客户端一个 sessionid 。

            • 客户端在后续请求中自动携带 sessionid,DRF 的 SessionAuthentication 会从这个 session 中获取用户身份。

              REST_FRAMEWORK = {
                  'DEFAULT_AUTHENTICATION_CLASSES': [
                      'rest_framework.authentication.SessionAuthentication',
                  ],
                  'DEFAULT_PERMISSION_CLASSES': [
                      'rest_framework.permissions.IsAuthenticated',
                  ],
              }
              
              # 自定义登录视图
              def custom_login_view(request):
                  if request.method == "POST":
                      try:
                          data = json.loads(request.body)
                          username = data.get("username")
                          password = data.get("password")
                      except Exception:
                          return JsonResponse({"error": "Invalid input"}, status=400)
                      user = authenticate(request, username=username, password=password)
                      if user:
                          login(request, user)  # 使用Django的session login
                          return JsonResponse({"message": "Login success", "username": user.username})
                      else:
                          return JsonResponse({"error": "Invalid credentials"}, status=401)
                  return JsonResponse({"error": "Only POST allowed"}, status=405)
              

              登录成功后会返会Session ID,在浏览器中,后续请求会自动携带Session ID。需要注意的是,SessionAuthentication需要依赖Cookie和CSRF,不适合前后端分离项目,有状态认证机制【需在服务端存储Session】。

              DRF的使用

              SessionAuthentication支持登出,如下:

              from django.contrib.auth import logout
              from rest_framework.decorators import api_view, permission_classes
              from rest_framework.permissions import IsAuthenticated
              from rest_framework.response import Response
              @api_view(['POST'])
              @permission_classes([IsAuthenticated])
              def logout_view(request):
                  logout(request)  # 销毁 session
                  return Response({'message': 'Logout successful'})
              

              4.3 TokenAuthentication

              TokenAuthentication是一种无状态的认证机制,其核心原理是用户登录后,服务端返回一个Token给客户端,客户端之后访问所有接口时都在请求头携带这个Token【Authorization: Token 】,服务端通过它识别用户身份,返回给前端的token也会保存在authen_token数据表中,后续认证也就是查看数据表中是否有携带的token。其使用如下:

              INSTALLED_APPS = [
                  ...
                  'rest_framework',
                  'rest_framework.authtoken',
              ]
              # 迁移数据库创建token表
              python manage.py migrate
              REST_FRAMEWORK = {
                  'DEFAULT_AUTHENTICATION_CLASSES': [
                      'rest_framework.authentication.TokenAuthentication',
                  ],
                  'DEFAULT_PERMISSION_CLASSES': [
                      'rest_framework.permissions.IsAuthenticated',
                  ]
              }
              
              from rest_framework.authtoken.views import obtain_auth_token
              urlpatterns = [
                  # 内置的登录视图只支持 username + password
                  # 通过认证后端的用户会返回一个token
                  path('login/', obtain_auth_token),
              ]
              

              值得注意的是,使用Djano的TokenAuthentication只接受如下的Header,这也就意味着在postman中只能手动添加请求头:并且浏览器是不会自动携带Token的。

              Authorization: Bearer 
              

              DRF的使用

              Django的TokenAuthentication虽然使用很方便,但是有着很多局限性和缺点,因为生产环境中并不推荐使用TokenAuthentication。

              • Token永不过期:一旦泄露,别人可永久使用,除非手动删除。

              • 不支持刷新:无法像JWT刷新token,有效期无法动态延长。

              • 不支持多端登录:默认用户只有一个token,不能区分Web、手机或其他设备。

              • 安全性低:Token是明文存储在数据库中的,盗用风险高,依赖HTTPS保证安全。

              • 无签名校验:服务端要查数据库,效率低且无法校验 token 真伪。

              • 不支持登出:没有标准的注销机制,只能手动删除 token 或换一个。

              • 不易扩展:不支持加自定义字段、角色、权限等高级内容。

                4.4 JSONWebTokenAuthentication

                JSONWebTokenAuthentication即JWT认证,是一种无状态的、签名验证的令牌认证机制,服务端签发token后,不再保存用户登录状态,客户端每次请求时携带token,服务端验证签名即可。在前后端分离、移动端或小程序场景中,JWT 是非常主流的认证方式。

                pip install djangorestframework-simplejwt
                
                INSTALLED_APPS = [
                    ...
                    'rest_framework',
                    'rest_framework_simplejwt',
                ]
                REST_FRAMEWORK = {
                    'DEFAULT_AUTHENTICATION_CLASSES': (
                        'rest_framework_simplejwt.authentication.JWTAuthentication',
                    ),
                    'DEFAULT_PERMISSION_CLASSES': [
                        'rest_framework.permissions.IsAuthenticated',
                    ]
                }
                from datetime import timedelta
                SIMPLE_JWT = {
                    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
                    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
                    # 每次刷新AccessToken时是否刷新RefreshToken
                    'ROTATE_REFRESH_TOKENS': True,
                    # 是否将旧Token加入黑名单,配合ROTATE_REFRESH_TOKENS=True使用
                    'BLACKLIST_AFTER_ROTATION': True,
                    
                    # 自定义盐, 默认使用django的盐
                    'SIGNING_KEY': 'my-very-secret-salt-used-to-sign-tokens-123456',
                }
                
                from django.urls import path
                from rest_framework_simplejwt.views import (
                    TokenObtainPairView,      # 登录
                    TokenRefreshView,         # 刷新 token
                    TokenVerifyView           # 校验 token 是否有效
                )
                urlpatterns = [
                    # 通过认证后端时才返回token
                    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),  # 登录
                    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),  # 刷新
                    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),    # 验证
                ]
                

                4.5 Custom Authentication

                首先是需要清楚为什么需要自定义认证组件,对于上述的四种认证组件,如果对它们的默认行为不满意,比如token在请求体中、实现多端控制登录、支持企业微信、微信小程序登录等等,这时就需要使用自定义认证组件才能完成需求。

                对于自定义的认证类,需要继承BaseAuthentication 类并重写authenticate方法,其中authenticate方法在认证成功时必须有返回值,其返回值为(requset.user, request.auth):

                • user:表示经过认证的用户对象,通常是 User 模型的实例,或者其他表示用户身份的对象。
                • auth:表示与认证相关的附加信息,通常是 None,除非你需要传递其他信息,如Token、API Key等认证数据。

                  对于BaseAuthentication,有以下两个方法:

                  • authenticate:执行具体的认证逻辑,验证请求的合法性。若认证成功则返回(user, auth),返回None则表示允许其它认证类继续尝试,抛出异常则明确表示认证失败,终止后续认证流程。

                  • authenticate_header:用于在认证失败时,为客户端返回一个 WWW - Authenticate 响应头,响应状态码为401,用于指导客户端如何重新认证。如果不重写此方法,认证失败时默认返回**403 Forbidden** 而非401 Unauthorized,可能导致前端无法正确处理认证流程。

                    from rest_framework.authentication import BaseAuthentication
                    from rest_framework.exceptions import AuthenticationFailed
                    class MyCustomAuthentication(BaseAuthentication):
                        def authenticate(self, request):
                            # 模拟认证失败
                            raise AuthenticationFailed('Invalid authentication credentials.')
                        def authenticate_header(self, request):
                            # 返回 WWW - Authenticate 头信息
                            return 'Bearer realm="api"'
                    
                    HTTP/1.1 401 Unauthorized
                    WWW - Authenticate: MyCustomAuth realm="api"
                    Content - Type: application/json
                    {
                        "detail": "Invalid authentication credentials."
                    }
                    

                    下面是【局部认证】的简单使用,一般使用CBV方式为某些视图指定认证类:

                    from rest_framework.authentication import BaseAuthentication
                    from django.contrib.auth import get_user_model
                    from rest_framework.exceptions import AuthenticationFailed
                    User = get_user_model()
                    class CustomHeaderTokenAuthentication(BaseAuthentication):
                        def authenticate(self, request):
                            token = request.META.get("HTTP_X_AUTH_TOKEN")  # 自定义header
                            if not token:
                                return None  # 返回 None 表示此认证组件不处理该请求
                            try:
                                user = User.objects.get(auth_token=token)
                            except User.DoesNotExist:
                                raise AuthenticationFailed("Invalid token")
                            return (user, token)
                    
                    REST_FRAMEWORK = {
                        'DEFAULT_AUTHENTICATION_CLASSES': [
                            'yourapp.auth.CustomHeaderTokenAuthentication',
                        ]
                    }
                    

                    如果不使用【全局认证】,就需要单独给每个视图单独指定认证类,难以管理。下面是全局认证的简单使用:

                    REST_FRAMEWORK = {
                        "UNAUTHENTICATED_USER": None,
                        "DEFAULT_AUTHENTICATION_CLASSES": [
                            "api.views.MyAuthentication",
                        ],
                    }
                    

                    如果使用全局认证,认证类就一定不能写在视图文件中了,其原因就是出现了循环引用。如果同时使用了全局认证和局部认证,则使用局部认证,本质上就是一个覆盖的问题。

                    4.6 DRF中的request

                    dispatch 方法是视图处理请求的核心调度器,它负责接收请求,进行一系列的初始化、权限检查、异常处理等操作,最后将响应返回给客户端。其源代码如下:

                    def dispatch(self, request, *args, **kwargs):
                        """
                        `.dispatch()` is pretty much the same as Django's regular dispatch,
                        but with extra hooks for startup, finalize, and exception handling.
                        """
                        self.args = args
                        self.kwargs = kwargs
                        request = self.initialize_request(request, *args, **kwargs)
                        self.request = request
                        self.headers = self.default_response_headers  # deprecate?
                        try:
                            self.initial(request, *args, **kwargs)
                            # Get the appropriate handler method
                            if request.method.lower() in self.http_method_names:
                                handler = getattr(self, request.method.lower(),
                                                  self.http_method_not_allowed)
                            else:
                                handler = self.http_method_not_allowed
                            response = handler(request, *args, **kwargs)
                        except Exception as exc:
                            response = self.handle_exception(exc)
                        self.response = self.finalize_response(request, response, *args, **kwargs)
                        return self.response
                    

                    其中request = self.initialize_request(request, *args, **kwargs)是对原始的Django请求对象进行封装,将其转换为 DRF 的Request对象,该对象包含了更多与RESTful API相关的属性和方法,例如请求的认证信息、解析后的请求数据等。drf中的request是对原始请求对象的封装,下面是几个常用的扩展方法:

                    • request.query_params:用于获取URL中的查询参数,等同于Django原生 HttpRequest 对象的 request.GET。
                    • request.user:用于获取经过认证的用户对象。如果请求经过了认证,request.user 将是一个有效的用户实例,如果未认证,默认是 AnonymousUser。
                    • request.auth:用于获取认证后得到的认证信息,例如令牌Token。具体的值取决于使用的认证类。
                    • request.is_authenticated:检查请求是否经过了认证。

                      4.7 多个认证类

                      在DRF中,多个认证类的队列是非常重要的。DRF会按照在 settings.py 中配置的顺序依次尝试每个认证类,直到某个认证类成功认证为止,或者所有认证类都失败为止。每个认证类的 authenticate 方法会返回不同的结果,DRF 会根据这些结果进行相应处理:

                      • 认证成功:如果某个认证类的 authenticate 方法返回一个包含用户对象和认证信息的元组 (user, auth),则认为认证成功。此时,DRF会停止继续尝试后续的认证类,并将认证成功的用户对象和认证信息存储在 request.user 和 request.auth 中,供后续的权限检查和视图处理使用。
                      • 无法认证:如果某个认证类的 authenticate 方法返回 None,表示该认证类无法对当前请求进行认证。DRF会忽略这个结果,并继续尝试下一个认证类。
                      • 认证失败:如果某个认证类的 authenticate 方法抛出 AuthenticationFailed 异常,则表示认证失败。DRF 会立即停止认证过程,并将该异常传递给异常处理机制,最终返回一个包含错误信息的响应给客户端,通常状态码为 401未授权。

                        如果所有配置的认证类的 authenticate 方法最终都返回 None,即没有任何一个认证类能够成功认证请求,这时候就会涉及到是否认证失败的判断,而这取决于权限类的配置:

                        • 权限类允许未认证访问:若权限类允许未认证的用户访问视图,例如使用 AllowAny 权限类,即使所有认证类都返回 None,请求也不会被判定为认证失败,而是可以正常访问视图。示例代码如下:

                          from rest_framework.views import APIView
                          from rest_framework.permissions import AllowAny
                          from rest_framework.response import Response
                          class MyView(APIView):
                              authentication_classes = [MyAuthentication, MyAuthentication2]
                              permission_classes = [AllowAny]
                              def get(self, request):
                                  return Response({'message': 'This view allows unauthenticated access.'})
                          
                        • 权限类要求认证:如果权限类要求用户必须经过认证才能访问视图,例如使用 IsAuthenticated 权限类,当所有认证类都返回None 时,就会被判定为认证失败。DRF会抛出 AuthenticationFailed 异常,并返回一个状态码为401的错误响应给客户端。示例代码如下:

                          from rest_framework.views import APIView
                          from rest_framework.permissions import IsAuthenticated
                          from rest_framework.response import Response
                          class MyProtectedView(APIView):
                              authentication_classes = [MyAuthentication, MyAuthentication2]
                              permission_classes = [IsAuthenticated]
                              def get(self, request):
                                  return Response({'message': 'This is a protected view.'})
                          

                          总而言之,当返回(user, auth)时表示认证成功,如果所有认证类均返回None则表示认证未通过但不是认证失败,将返回匿名用户(AnonymousUser, None),如果不想匿名用户的user为Anonymous,可以通过设置'UNAUTHENTICATED_USER': None。当用户端提供了无效的凭证,在认证组件中会明确的判断为认证失败即抛出AuthenticationFailed异常,DRF 会立即停止遍历其他认证组件。

                          5. 权限组件

                          在DRF中,权限组件用于控制对API视图的访问,确保只有满足特定条件的用户才能访问相应的资源。DRF提供了多个内置的权限类,如下:

                          • AllowAny:允许任何用户访问,不管用户是否经过认证。使用该组件时不需要配置认证组件。
                          • IsAuthenticated:只允许经过认证的用户访问。
                          • IsAdminUser:只允许管理员用户,即 is_staff 为 True 的用户访问。
                          • IsAuthenticatedOrReadOnly:允许经过认证的用户进行任何操作,对于未认证的用户只允许进行只读操作,如 GET 请求。
                          • 自定义权限组件:如果内置的权限类无法满足业务需求,你可以自定义权限类。自定义权限类需要继承 BasePermission 类,并实现 has_permission 或 has_object_permission 方法。

                            对于权限组件,其配置方式和认证组件的配置方式一致。权限组件有以下注意点:

                            • 权限类的顺序:当配置多个权限类时,DRF会按照它们在列表中的顺序依次检查权限。如果前面的权限类检查不通过,后面的权限类将不再检查。因此,需要合理安排权限类的顺序,通常将最宽松的权限类放在前面,最严格的放在后面。

                            • 与认证组件协同工作:权限组件依赖于认证组件,只有在认证成功后才能进行权限检查。因此,需要确保在使用权限组件之前已经正确配置了认证组件。如果认证失败,权限检查将无法正常进行。

                            • 异常处理:当权限检查不通过时,DRF会抛出 PermissionDenied 异常,并返回一个状态码为403禁止访问的响应。可以通过自定义异常处理函数来改变默认的错误响应,以满足特定的业务需求。

                              from rest_framework.views import exception_handler
                              from rest_framework.response import Response
                              def custom_exception_handler(exc, context):
                                  response = exception_handler(exc, context)
                                  if response is not None and isinstance(exc, rest_framework.exceptions.PermissionDenied):
                                      response.data = {'error': 'You do not have permission to access this resource.'}
                                  return response
                              
                              REST_FRAMEWORK = {
                                  'EXCEPTION_HANDLER': 'your_app.utils.custom_exception_handler'
                              }
                              

                              对于自定义的权限组件,BasePermission中的has_permission和has_object_permission默认返回True,表示权限通过,反之不通过。这两个方法有以下区别:

                              • has_permission:决定是否允许整个视图类的访问。

                                • 调用时机:在调用视图的get()、post()方法之前执行。它主要针对视图级别的权限控制,不涉及具体的对象实例。例如,当客户端发起一个请求访问某个视图时,DRF 会首先调用视图所配置的权限类的 has_permission 方法来决定是否允许该用户继续访问这个视图。
                                • 应用场景:适用于对整个视图的访问进行权限控制,比如限制只有认证用户才能访问某个视图,或者只有特定角色的用户才能访问某个视图集。例如一个博客系统的文章列表视图,可能只允许已登录用户访问,就可以在 has_permission 方法中进行判断。
                                • has_object_permission:针对单个对象如详情页、更新或删除操作的权限验证。

                                  • 调用时机:该方法在视图处理涉及具体对象的操作时被调用,前提是 has_permission 方法已经返回 True,即用户已经通过了视图级别的权限检查。has_object_permission 用于判断用户是否有权限对特定的对象实例进行操作,如对某个数据库记录进行更新或删除。

                                  • 应用场景:适用于对具体对象的操作进行权限控制,例如只有文章的作者才能对文章进行修改或删除操作。在这种情况下,需要根据具体的文章对象来判断用户是否有权限进行操作,就可以使用 has_object_permission 方法。

                                    class BasePermission(metaclass=BasePermissionMetaclass):
                                        """
                                        A base class from which all permission classes should inherit.
                                        """
                                        # 权限拒绝时的提示信息
                                        message = {"code": 1001, "msg": "无权访问"}
                                        def has_permission(self, request, view):
                                            """
                                            Return `True` if permission is granted, `False` otherwise.
                                            """
                                            return True
                                        def has_object_permission(self, request, view, obj):
                                            """
                                            Return `True` if permission is granted, `False` otherwise.
                                            """
                                            return True
                                    

                                    上述两个方法中有两个参数,view和obj,它们都是由django自动传入到你的权限类方法中的,使用如下:

                                    • view:当前请求的视图实例,可以访问视图的 queryset、serializer_class、action,以及视图类中的自定义属性。

                                      def has_permission(self, request, view):
                                          # 若视图类中定义了scope属性
                                          if view.scope == 'admin':
                                              return request.user.is_staff
                                          return True
                                      
                                    • obj:表示当前操作的具体对象的模型。

                                      def has_object_permission(self, request, view, obj):
                                          # 允许读操作,写操作需验证所有者
                                          if request.method in permissions.SAFE_METHODS:
                                              return True
                                          return obj.owner == request.user
                                      

                                      5.1 单个权限类

                                      from rest_framework import permissions
                                      class IsAuthorOrAdminOrReadOnly(permissions.BasePermission):
                                          """
                                          允许任何人进行只读操作
                                          只允许文章作者或管理员进行写操作
                                          """
                                          def has_permission(self, request, view):
                                              """
                                              负责视图级别的权限检查
                                              """
                                              # SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
                                              if request.method in permissions.SAFE_METHODS:
                                                  return True
                                              # 如果是写操作如POST则要求用户必须登录
                                              return request.user and request.user.is_authenticated
                                          def has_object_permission(self, request, view, obj):
                                              """
                                              负责对象级别的权限检查
                                              """
                                              if request.method in permissions.SAFE_METHODS:
                                                  return True
                                              # 如果是写操作如PUT、PATCH、DELETE则检查当前用户是否是该文章的作者或者是否是管理员
                                              return obj.author == request.user or request.user.is_staff
                                      
                                      from rest_framework import viewsets
                                      from .models import Post
                                      from .serializers import PostSerializer
                                      from .permissions import IsAuthorOrAdminOrReadOnly # 导入自定义权限类
                                      class PostViewSet(viewsets.ModelViewSet):
                                          queryset = Post.objects.all()
                                          serializer_class = PostSerializer
                                          # 在这里应用我们的权限类
                                          authentication_classes = [SessionAuthentication, TokenAuthentication]
                                          permission_classes = [IsAuthorOrAdminOrReadOnly]
                                          def perform_create(self, serializer):
                                              # 在创建文章时,自动将作者设置为当前登录用户
                                              serializer.save(author=self.request.user)
                                      

                                      5.2 多个权限类

                                      使用方式与单个权限类一致,需要注意的是当某个权限类未通过,之后的权限类将不再执行。但是在实际开发中,有时并不需要所有权限类都通过才能访问对应的资源,符合某一个条件就可以访问该资源。为了实现这一需求,可以使用如下:

                                      from rest_framework.permissions import IsAuthenticated, IsAdminUser
                                      from rest_framework.combinators import Or
                                      # 满足任一权限即可
                                      permission_classes = [Or(IsAuthenticated, IsAdminUser)]
                                      

                                      6. 限流组件

                                      在DRF 中,限流是一个非常有用的功能,能够限制某个用户在一定时间内能发送多少次请求。限流通常用于防止恶意攻击、避免服务过载、保护 API 资源,或者简单地控制流量。限流通常是通过对请求进行计数,并且每个用户或 IP 在一定时间内只能发起有限次数的请求来实现的,例如每个用户每分钟最多能发起100个请求。DRF提供了几种常用的限流类,我们可以在 全局配置或 视图级别设置限流。

                                      • AnonRateThrottle:AnonRateThrottle 用于限制未认证的用户的请求次数。

                                      • UserRateThrottle:UserRateThrottle 用于限制已认证的用户的请求次数。

                                        # settings.py
                                        REST_FRAMEWORK = {
                                            # 限流类
                                            'DEFAULT_THROTTLE_CLASSES': [
                                                'rest_framework.throttling.UserRateThrottle',  # 针对认证用户的限流
                                                'rest_framework.throttling.AnonRateThrottle',  # 针对匿名用户的限流
                                            ],
                                            
                                            # 限流速率
                                            'DEFAULT_THROTTLE_RATES': {
                                                'user': '100/hour',  # 认证用户每小时最多 100 次请求
                                                'anon': '10/minute',  # 匿名用户每分钟最多 10 次请求
                                            },
                                        }
                                        
                                      • ScopedRateThrottle:针对不同视图或端点的限流。ScopedRateThrottle 可以为不同的视图或视图集设置不同的限流策略。

                                        class ContactListView(APIView):
                                            throttle_scope = 'contacts'  # 关联限流作用域
                                            ...
                                        # settings.py 配置
                                        REST_FRAMEWORK = {
                                            'DEFAULT_THROTTLE_RATES': {'contacts': '50/day'}
                                        }
                                        
                                      • CustomThrottle:如果内置的限流类不满足需求,DRF允许自定义限流类。你可以继承 BaseThrottle 类来实现自定义的限流逻辑。对于BaseThrottle类,里面限流的逻辑都需要自己实现,较为麻烦。这时可以用到BaseThrottle类的子类SimpleRateThrottle。

                                        from rest_framework.throttling import SimpleRateThrottle
                                        from django.core.cache import cache as default_cache
                                        class MyThrottle(SimpleRateThrottle):
                                            scope = 'user'
                                            # 配置节流
                                            THROTTLE_RATES = {scope: '3/m'}
                                            cache = default_cache
                                            def get_cache_key(self, request, view):
                                                if request.user:
                                                    ident = request.user.pk
                                                else:
                                                    # 获取用户的IP地址
                                                    ident = self.get_ident(request)
                                                return self.cache_format % {
                                                    'scope':  self.scope,
                                                    'ident': ident
                                                }
                                        

                                        DRF 限流依赖 Django 缓存系统,默认使用 LocMemCache,若需使用其他缓存如 Redis,需在 settings.py 中配置。

                                        # settings.py
                                        CACHES = {
                                            "default": {  # 限流默认使用此配置
                                                "BACKEND": "django_redis.cache.RedisCache",
                                                "LOCATION": "redis://127.0.0.1:6379/0",  # 使用 Redis 数据库 0
                                            },
                                            "rate_limit_cache": {  # 其他缓存配置
                                                "BACKEND": "django_redis.cache.RedisCache",
                                                "LOCATION": "redis://127.0.0.1:6379/1",  # 使用 Redis 数据库 1
                                            }
                                        }
                                        
                                        from rest_framework.throttling import SimpleRateThrottle
                                        from django.core.cache import caches  # 导入多缓存管理器
                                        class CustomRedisThrottle(SimpleRateThrottle):
                                            scope = "custom"  # 限流作用域
                                            cache = caches["rate_limit_cache"]
                                            rate = "100/day"  # 限流频率
                                            def get_cache_key(self, request, view):
                                                # 自定义逻辑生成唯一标识(如用户 ID 或 IP)
                                                return f"custom_{request.user.id if request.user else self.get_ident(request)}"
                                        

                                        7. 版本控制

                                        在 DRF里,版本控制是一项重要的功能,它允许你在同一个 API 端点上提供不同版本的服务,以满足不同客户端的需求,同时确保系统的兼容性和可维护性。版本控制的作用就是随着业务的发展,API 可能会发生变化。通过版本控制,旧版本的客户端可以继续使用旧版本的 API,而新版本的客户端可以使用新功能。DRF 提供了多种版本控制的实现方式,下面分别介绍:

                                        • 基于查询参数QueryParameterVersion:通过在 URL 的查询参数中指定版本号,例如 http://example.com/api/resource/?version=v1。

                                          from rest_framework.versioning import QueryParameterVersioning
                                          class OrderView(APIView):
                                              authentication_classes = []
                                              versioning_class = QueryParameterVersioning
                                              def get(self, request):
                                                  print(request.version)
                                                  return Response('订单列表')
                                          
                                          # 控制版本的名称
                                          REST_FRAMEWORK = {
                                              'DEFAULT_VERSION': 'v1',
                                              'ALLOWED_VERSIONS': ['v1', 'v2'],
                                              'VERSION_PARAM': 'version'
                                          }
                                          
                                        • 基于URL的版本控制URLPathVersioning:在 URL 路径中包含版本号,例如 http://example.com/api/v1/resource/。

                                          REST_FRAMEWORK = {
                                              'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning'
                                          }
                                          
                                          from django.urls import path
                                          from rest_framework.views import APIView
                                          from rest_framework.response import Response
                                          class MyView(APIView):
                                              def get(self, request, version):
                                                  return Response({'version': version})
                                          urlpatterns = [
                                              path('api//resource/', MyView.as_view()),
                                          ]
                                          
                                        • 基于请求头AcceptHeaderVersioning:在请求头的 Accept 字段中指定版本号,例如 Accept: application/json; version=v1。

                                          REST_FRAMEWORK = {
                                              'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning'
                                          }
                                          
                                          from rest_framework.views import APIView
                                          from rest_framework.response import Response
                                          class MyView(APIView):
                                              def get(self, request):
                                                  version = request.version
                                                  return Response({'version': version})
                                          

                                          在 DRF的版本控制机制里,version 和 versioning_scheme 是两个关键的概念,下面为你详细介绍它们的含义以及 versioning_scheme 的使用方法。

                                          • version :当前请求所使用的 API 版本号。在处理请求时,DRF 会根据所配置的版本控制方案从请求中提取版本信息,并将其存储在 request.version 属性中,方便在视图里获取和使用该版本号,从而依据不同的版本返回不同的响应。

                                            class UserView(APIView):
                                                def get(self, request):
                                                    if request.version == 'v1':
                                                        return Response({"name": "Alice"})
                                                    elif request.version == 'v2':
                                                        return Response({"name": "Alice", "email": "alice@example.com"})
                                            
                                          • versioning_scheme :负责实现版本解析逻辑和反向生成 URL,反向生成url如下:

                                            class OrderView(APIView):
                                                authentication_classes = []
                                                versioning_class = QueryParameterVersioning
                                                def get(self, request):
                                                    url = request.versioning_scheme.reverse('order-list', request=request)
                                                    print(url)
                                            

                                            8. 解析器

                                            在DRF中,解析器Parsers用于将传入的请求数据解析为 Python 对象,以便在视图中进行处理。DRF 提供了多种内置的解析器,其中下面的前三章解析器是drf默认的解析器:解析器仅用于格式转换,不执行任何数据验证。

                                            • JSONParser:用于解析JSON格式的数据。这是最常用的解析器之一,适用于处理以 JSON 格式发送的 API 请求,例如前端通过AJAX发送JSON数据到后端。

                                            • FormParser:用于解析表单数据,通常用于处理 application/x-www-form-urlencoded 格式的数据,这种格式是传统的 HTML 表单提交数据的方式。

                                            • MultiPartParser:用于处理多部分表单数据,特别是包含文件上传的情况,格式为 multipart/form-data。当用户通过表单上传文件时,就需要使用这个解析器。

                                            • 自定义解析器:如果内置的解析器无法满足项目需求,还可以自定义解析器。自定义解析器需要继承 BaseParser 类,并实现 parse 方法。开发中使用默认的解析器足够使用。

                                              # 全局配置
                                              REST_FRAMEWORK = {
                                                  'DEFAULT_PARSER_CLASSES': [
                                                      'rest_framework.parsers.JSONParser',        # 解析 JSON 数据
                                                      'rest_framework.parsers.FormParser',        # 解析表单数据x-www-form-urlencoded
                                                      'rest_framework.parsers.MultiPartParser'     # 解析文件上传multipart/form-data
                                                  ]
                                              }
                                              
                                              from rest_framework.versioning import QueryParameterVersioning
                                              class OrderView(APIView):
                                                  parser_classes = [JSONParser, FormParser]
                                                  def get(self, request):
                                                      # 访问解析后的数据
                                                      print(request.data)
                                              

                                              9. 序列化器

                                              在DRF中,序列化Serialization是一个非常重要的概念,它主要用于将复杂的数据类型如 Django 模型实例、查询集转换为Python原生数据类型,以便可以轻松地将其渲染为 JSON、XML 等格式的响应数据。同时,也能将传入的原始数据反序列化为 Django 模型实例或其他复杂数据类型,方便进行数据验证和保存到数据库。

                                              如果没有序列化器,通过model.Depart.objects.all().first()获得的模型实例是一个复杂的python对象,又或是model.Depart.objects.all()返回的一个QuerySet对象,是不能直接通过Response转换为JSON格式的,应该将其转换为python中的原生对象再传入Response,这时就需要用到序列化器。

                                              对于创建序列化器实例时的参数问题,如下:

                                              • 序列化:传入instance参数,如ArticleSerializer(instance=article),通常发生在Get请求中。
                                              • 反序列化:传入data参数,如ArticleSerializer(data=article),通常发生在Post、Put、Patch请求中。通常会涉及serializer.save() 方法的使用,用于修改或保存数据,对于参数方面也有以下两种使用方式:
                                                • serializer = MySerializer(data=request.data):用于创建,会触发create方法。
                                                • serializer = MySerializer(instance=obj, data=request.data):用于更新,会触发update方法。

                                                  9.1 Serializer

                                                  Serializer:这是最基础的序列化器,需要手动定义每个字段。适用于需要自定义序列化和反序列化逻辑的复杂场景,如当你的数据源不涉及 Django 模型、需要高度定制序列化行为即默认行为create、update不满足需求时才使用。

                                                  # reports/serializers.py
                                                  from rest_framework import serializers
                                                  from datetime import datetime, timedelta
                                                  class EventSerializer(serializers.Serializer):
                                                      # 字段定义
                                                      -----------------------------------------------------------------------------------------------
                                                      # 不涉及django模型时,序列化器字段必须与处理的数据字典的键名一致才能正确获取到值,因为Serializer不具有自动推断的功能
                                                      -----------------------------------------------------------------------------------------------
                                                      event_id = serializers.UUIDField(format='hex_verbose', read_only=True)  # UUID格式
                                                      event_type = serializers.CharField(max_length=50)
                                                      source_system = serializers.CharField(max_length=100, required=False, allow_blank=True, default="UNKNOWN")
                                                      payload = serializers.JSONField()  # 任意JSON数据
                                                      timestamp = serializers.DateTimeField(read_only=True)  # 表示处理时间
                                                      # --- 字段级别验证示例 ---
                                                      def validate_event_type(self, value):
                                                          """
                                                          验证事件类型是否为预定义类型之一
                                                          """
                                                          allowed_types = ['login', 'logout', 'purchase', 'error', 'page_view']
                                                          if value.lower() not in allowed_types:
                                                              raise serializers.ValidationError(
                                                                  f"无效的事件类型 允许的类型是: {', '.join(allowed_types)}"
                                                              )
                                                          return value
                                                      # --- 对象级别验证示例 ---
                                                      def validate(self, data):
                                                          """
                                                          验证特定事件类型下的数据规则。
                                                          例如,如果是 'purchase' 事件,payload 必须包含 'amount' 字段。
                                                          """
                                                          if data['event_type'].lower() == 'purchase':
                                                              if 'amount' not in data['payload'] or not isinstance(data['payload']['amount'], (int, float)):
                                                                  raise serializers.ValidationError(
                                                                      {"payload": "Purchase事件的payload必须包含有效的'amount'字段"}
                                                                  )
                                                              if data['payload']['amount'] "payload": "Purchase事件的'amount'必须大于零"}
                                                                  )
                                                          return data
                                                      # --- create 方法示例 反序列化时使用 ---
                                                      def create(self, validated_data):
                                                          # 模拟生成一个事件ID
                                                          validated_data['event_id'] = f"evt-{datetime.now().timestamp()}"
                                                          validated_data['timestamp'] = datetime.now()
                                                          # 模拟将事件数据发送到日志系统或消息队列
                                                          print(f"--- 接收到并处理了新事件 ---")
                                                          print(f"  Event Type: {validated_data['event_type']}")
                                                          print(f"  Source: {validated_data['source_system']}")
                                                          print(f"  Payload: {validated_data['payload']}")
                                                          print(f"  Processed At: {validated_data['timestamp']}")
                                                          print(f"---------------------------\n")
                                                          # 返回处理后的数据,通常包含新生成的ID和时间戳
                                                          return validated_data
                                                      # --- update 方法示例 反序列化时使用 ---
                                                      def update(self, instance, validated_data):
                                                          """
                                                          对于像事件日志这样的数据,通常是只创建不更新
                                                          所以这里不实现 update 方法,或者直接抛出 NotImplementedError
                                                          如果需要更新,例如更新一个临时事件的状态,你可以修改这里
                                                          """
                                                          # 简单模拟更新 instance 的部分字段
                                                          instance['event_type'] = validated_data.get('event_type', instance['event_type'])
                                                          instance['source_system'] = validated_data.get('source_system', instance['source_system'])
                                                          instance['payload'] = validated_data.get('payload', instance['payload'])
                                                          instance['timestamp'] = datetime.now()  # 更新时间戳
                                                          print(f"--- 更新了事件 ---")
                                                          print(f"  Event ID: {instance.get('event_id', 'N/A')}")
                                                          print(f"  New Type: {instance['event_type']}")
                                                          print(f"  Updated At: {instance['timestamp']}")
                                                          print(f"-------------------\n")
                                                          return instance
                                                  
                                                          "event_id": uuid.UUID("a1b2c3d4-e5f6-7890-1234-567890abcdef"),
                                                          "event_type": "login",
                                                          "source_system": "web_app",
                                                          "payload": {"user_id": 101, "ip_address": "192.168.1.1"},
                                                          "timestamp": datetime.now() - timedelta(hours=2)
                                                      },
                                                      {
                                                          "event_id": uuid.UUID("f0e9d8c7-b6a5-4321-fedc-ba9876543210"),
                                                          "event_type": "page_view",
                                                          "source_system": "mobile_app",
                                                          "payload": {"page_url": "/dashboard", "user_id": 102},
                                                          "timestamp": datetime.now() - timedelta(minutes=30)
                                                      }
                                                  ]
                                                  class EventListAPIView(APIView):
                                                      permission_classes = [AllowAny]
                                                      def get(self, request, format=None):
                                                          """
                                                          序列化操作:将Python列表转换为可供API响应的格式
                                                          """
                                                          serializer = EventSerializer(instance=_mock_events_data, many=True)
                                                          # serializer.data 包含序列化后的 Python 字典或列表
                                                          print("GET Request - Serialized data:", serializer.data) # 如{'id': 1, 'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}
                                                          return Response(serializer.data, status=status.HTTP_200_OK)
                                                      def post(self, request, format=None):
                                                          """
                                                          反序列化操作:接收客户端数据,进行验证,并模拟处理
                                                          """
                                                          # 实例化序列化器,传入客户端发送的数据
                                                          serializer = EventSerializer(data=request.data)
                                                          # 调用is_valid()进行数据验证
                                                          if serializer.is_valid():
                                                              # serializer.validated_data包含了验证通过并转换后的Python字典
                                                              print("POST Request - Validated data type:", type(serializer.validated_data))
                                                              print("POST Request - Validated data:", serializer.validated_data)
                                                              processed_event = serializer.save()
                                                              # 将处理后的数据作为响应返回
                                                              return Response(processed_event, status=status.HTTP_201_CREATED)
                                                          else:
                                                              # 如果验证失败,serializer.errors 包含错误信息
                                                              print("POST Request - Validation Errors:", serializer.errors)
                                                              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
                                                  class EventDetailAPIView(APIView):
                                                      permission_classes = [AllowAny]
                                                      def get_object(self, event_id):
                                                          # 模拟从 _mock_events_data 中查找一个事件
                                                          for event in _mock_events_data:
                                                              print("GET Request - Event ID:", event['event_id'])
                                                              if event['event_id'] == event_id:
                                                                  return event
                                                          raise status.HTTP_404_NOT_FOUND
                                                      def get(self, request, event_id, format=None):
                                                          print("GET Request - Event IDaaaaa:", event_id)
                                                          """
                                                          单个对象的序列化
                                                          """
                                                          try:
                                                              event = self.get_object(event_id)
                                                          except Exception:
                                                              return Response({"detail": "Event not found."}, status=status.HTTP_404_NOT_FOUND)
                                                          serializer = EventSerializer(event)  # 传入单个Python字典进行序列化
                                                          return Response(serializer.data, status=status.HTTP_200_OK)
                                                      def put(self, request, event_id, format=None):
                                                          """
                                                          单个对象的反序列化 更新操作
                                                          """
                                                          try:
                                                              event = self.get_object(event_id)
                                                          except Exception:
                                                              return Response({"detail": "Event not found."}, status=status.HTTP_404_NOT_FOUND)
                                                          # 实例化序列化器,传入现有实例 event 和新数据request.data
                                                          # 传入一个instance调用的就是update而不是create
                                                          serializer = EventSerializer(instance=event, data=request.data, partial=True)  # partial=True 允许部分更新
                                                          if serializer.is_valid():
                                                              # 调用 serializer.save(), 会触发 EventSerializer的update()方法
                                                              updated_event = serializer.save()
                                                              return Response(updated_event, status=status.HTTP_200_OK)
                                                          return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
                                                  
                                                      "id": 1,
                                                      "title": "My Article",
                                                      "categories": [1, 2] // 关联的Tag对象的ID列表
                                                  }
                                                  
                                                              'id': author.id,
                                                              'name': author.name
                                                          }
                                                      def get_category_info(self, obj):
                                                          categories = obj.categories.all()
                                                          return [
                                                              {
                                                                  'id': category.id,
                                                                  'name': category.name
                                                              } for category in categories
                                                          ]
                                                      class Meta:
                                                          model = Book
                                                          fields = ['id', 'title', 'author_info', 'category_info']
                                                  
                                                      'DEFAULT_PERMISSION_CLASSES': [
                                                          'rest_framework.permissions.AllowAny',
                                                      ],
                                                      'DEFAULT_RENDERER_CLASSES': [
                                                          # 
                                                          'rest_framework.renderers.JSONRenderer',
                                                          'rest_framework.renderers.BrowsableAPIRenderer',
                                                      ],
                                                  }
                                                  
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码