django rest framework:从零开始搭建RESTful API
django rest framework:从零开始搭建RESTful API
- 一、安装DRF
- 二、DRF通用视图
- 三、DRF视图集
- 四、统一请求响应结果格式
- 五、JWT认证配置
- 六、生成API文档
UV教程:Python多版本管理神器
Django教程:Django项目开发全链路:数据库操作、多环境配置、windows/linux项目部署一站式指南
Django数据库配置:Django多数据库配置:mysql、mongo、redis、达梦
DRF官方:https://www.django-rest-framework.org/
GitHub:https://github.com/encode/django-rest-framework
一、安装DRF
1.运行uv add djangorestframework安装djangorestframework,未使用uv管理工具可使用pip install djangorestframework
2.进入项目目录,运行 uv run python manage.py startapp drfDemo新建应用,未使用uv管理工具可使用python manage.py startapp drfDemo
3.修改settings.py下的INSTALLED_APPS
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','rest_framework','drfDemo'
]
4.修改drfDemo>view.py
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from django.views.decorators.http import require_http_methods@method_decorator(csrf_exempt, name='dispatch')
@method_decorator(require_http_methods(['GET']), name='dispatch')
class DrfDemo(APIView):def get(self, request):return Response({'code': status.HTTP_200_OK,'message': '请求成功'}, status=status.HTTP_201_CREATED)
5.新建drfDemo>urls.py
from .views import DrfDemo
from django.urls import pathurlpatterns = [path('api', DrfDemo.as_view(), name='DrfDemo'),
]
6.修改settings.py同级urls.py
from django.contrib import admin
from django.urls import include, pathurlpatterns = [path('drfDemo/', include('drfDemo.urls')),path('', admin.site.urls)
]
7.进入项目目录,运行uv run python manage.py runserver 6001启动服务,未使用uv管理工具可使用python manage.py runserver 6001
8.访问http://127.0.0.1:6001/drfDemo/api,如下图所示,说明DRF安装成功
二、DRF通用视图
1.DRF通用视图相关类,官网文档:https://www.django-rest-framework.org/api-guide/generic-views/
类别 | 类名称 | 说明 |
---|---|---|
功能类 | ListModelMixin | 提供列表查询功能(get 方法实现) |
功能类 | CreateModelMixin | 提供创建资源功能(post 方法实现) |
功能类 | RetrieveModelMixin | 提供单资源查询功能(get 方法实现,需指定主键) |
功能类 | UpdateModelMixin | 提供更新资源功能(put/patch 方法实现) |
功能类 | DestroyModelMixin | 提供删除资源功能(delete 方法实现) |
基础通用视图 | GenericAPIView | 所有通用视图的基类,提供核心功能(如查询集、序列化器管理) |
具体视图类 | CreateAPIView | 仅支持创建资源(GenericAPIView + CreateModelMixin) |
具体视图类 | ListAPIView | 仅支持列表查询(GenericAPIView + ListModelMixin) |
具体视图类 | RetrieveAPIView | 仅支持单资源查询(GenericAPIView + RetrieveModelMixin) |
具体视图类 | DestroyAPIView | 仅支持删除资源(GenericAPIView + DestroyModelMixin) |
具体视图类 | UpdateAPIView | 仅支持更新资源(GenericAPIView + UpdateModelMixin) |
具体视图类 | ListCreateAPIView | 支持列表查询和创建资源(ListModelMixin + CreateModelMixin) |
具体视图类 | RetrieveUpdateAPIView | 支持单资源查询和更新(RetrieveModelMixin + UpdateModelMixin) |
具体视图类 | RetrieveDestroyAPIView | 支持单资源查询和删除(RetrieveModelMixin + DestroyModelMixin) |
具体视图类 | RetrieveUpdateDestroyAPIView | 支持单资源查询、更新和删除(Retrieve+Update+DestroyModelMixin) |
2.修改drfDemo>models.py
from django.db import models
from django.utils import timezoneclass Student(models.Model):# 性别选择GENDER_CHOICES = (('M', '男'),('F', '女'),('O', '其他'),)# 基本信息name = models.CharField(max_length=100, verbose_name='姓名')student_id = models.CharField(max_length=20, unique=True, verbose_name='学号')gender = models.CharField(max_length=1, choices=GENDER_CHOICES, verbose_name='性别')birth_date = models.DateField(verbose_name='出生日期')email = models.EmailField(blank=True, null=True, verbose_name='邮箱')phone = models.CharField(max_length=15, blank=True, null=True, verbose_name='电话')# 学业信息major = models.CharField(max_length=100, verbose_name='专业')grade = models.PositiveIntegerField(verbose_name='年级')admission_date = models.DateField(default=timezone.now, verbose_name='入学日期')# 状态信息is_active = models.BooleanField(default=True, verbose_name='是否在读')created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')class Meta:db_table = 'student'verbose_name = '学生'verbose_name_plural = '学生'ordering = ['student_id'] # 按学号排序def __str__(self):return f'{self.name}({self.student_id})'def get_age(self):"""计算学生年龄"""today = timezone.now().date()age = today.year - self.birth_date.year# 考虑生日是否已过if (today.month, today.day) < (self.birth_date.month, self.birth_date.day):age -= 1return age
3.依次运行下面命令同步模型,需配置数据库
uv run python manage.py makemigrations drfDemo #根据模型变更创建新的数据库迁移文件,未使用uv管理工具可使用pthon manage.py makemigrations drfDemouv run python manage.py migrate #同步模型与数据库,未使用uv管理工具可使用python manage.py migrate
4.新建drfDemo>serializers.py,序列化文件将查询集和模型实例等复杂数据转换为Python数据类型,官网文档:https://www.django-rest-framework.org/api-guide/serializers/
from .models import Student
from rest_framework import serializersclass StudentSerializer(serializers.ModelSerializer):"""学生信息序列化器,用于学生信息的序列化和反序列化支持所有字段的增删改查操作"""# 只读字段,计算年龄age = serializers.IntegerField(source='get_age', read_only=True)created_at = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)updated_at = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)class Meta:model = Student# 包含模型所有字段fields = '__all__'# 可以根据需要指定只读字段,如自动生成的时间戳read_only_fields = ['created_at', 'updated_at']def validate_phone(self, value):"""验证电话号码格式"""if value and not (value.isdigit() and len(value) in [11]):raise serializers.ValidationError('请输入有效的11位手机号码')return valuedef validate_grade(self, value):"""验证年级范围(1-6年级)"""if value < 1 or value > 6:raise serializers.ValidationError('年级必须在1-6之间')return value
5.修改drfDemo>views.py
from .models import Student
from .serializers import StudentSerializer
from rest_framework import generics, status
from rest_framework.response import Responseclass DrfDemoAddView(generics.GenericAPIView):""" 提供学生列表查询和创建新学生的API接口 """queryset = Student.objects.all()serializer_class = StudentSerializerdef post(self, request):request_data = request.data # 将请求数据存储在一个变量中serializer = self.get_serializer(data=request_data)if serializer.is_valid():serializer.save()return Response({'code': status.HTTP_200_OK,'message': '学生添加成功'}, status=status.HTTP_200_OK)return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '请求失败','errors': serializer.errors}, status=status.HTTP_400_BAD_REQUEST)class DrfDemoGetView(generics.GenericAPIView):""" 提供学生列表查询和创建新学生的API接口 """queryset = Student.objects.all()serializer_class = StudentSerializerdef get(self, request):students = self.get_queryset()serializer = self.get_serializer(students, many=True)return Response({'code': status.HTTP_200_OK,'message': '请求成功','data': serializer.data}, status=status.HTTP_200_OK)class DrfDemoUpdateView(generics.GenericAPIView):""" 提供学生列表查询和创建新学生的API接口 """queryset = Student.objects.all()serializer_class = StudentSerializerdef post(self, request):"""通过POST方法修改学生信息请求数据中需包含id字段指定要修改的学生"""# 从请求数据中获取学生IDstudent_id = request.data.get('student_id')if not student_id:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '缺少student_id字段'}, status=status.HTTP_400_BAD_REQUEST)# 获取要修改的学生对象try:# 这里假设id是学生表的主键,如果是其他字段(如student_id)可修改查询条件student = self.queryset.get(student_id=student_id)except Student.DoesNotExist:return Response({'code': status.HTTP_404_NOT_FOUND,'message': f'ID为{student_id}的学生不存在'}, status=status.HTTP_404_NOT_FOUND)# 初始化序列化器(传入要更新的对象和新数据)serializer = self.get_serializer(instance=student,data=request.data,partial=True # 允许部分字段更新)if serializer.is_valid():serializer.save() # 执行更新操作return Response({'code': status.HTTP_200_OK,'message': '学生信息修改成功'}, status=status.HTTP_200_OK)return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '请求失败'}, status=status.HTTP_400_BAD_REQUEST)class DrfDemoDeleteView(generics.GenericAPIView):"""提供学生信息删除的API接口(使用GET方法)"""queryset = Student.objects.all()serializer_class = StudentSerializerdef get(self, request):"""通过GET方法删除学生信息"""student_id = request.GET.get('student_id')if not student_id:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '缺少student_id字段'}, status=status.HTTP_400_BAD_REQUEST)try:student = self.queryset.get(student_id=str(student_id))student_name = student.name # 记录姓名用于返回提示student.delete() # 执行删除操作(物理删除,若需逻辑删除可改为student.is_active=False + save())# 4. 返回删除成功响应return Response({'code': status.HTTP_200_OK,'message': f'学生「{student_name}」(student_id:{student_id})删除成功'}, status=status.HTTP_200_OK)# 处理学生不存在的情况except Student.DoesNotExist:return Response({'code': status.HTTP_404_NOT_FOUND,'message': f'student_id为{student_id}的学生不存在,无法删除'}, status=status.HTTP_404_NOT_FOUND)
6.修改drfDemo>urls.py
from .views import DrfDemoAddView, DrfDemoDeleteView, DrfDemoGetView, DrfDemoUpdateView
from django.urls import pathurlpatterns = [path('drfDemo/add', DrfDemoAddView.as_view(), name='添加数据'),path('drfDemo/query', DrfDemoGetView.as_view(), name='查询数据'),path('drfDemo/update', DrfDemoUpdateView.as_view(), name='修改数据'),path('drfDemo/delete', DrfDemoDeleteView.as_view(), name='删除数据'),
]
7.修改修改settings.py同级urls.py
from django.contrib import admin
from django.urls import include, pathurlpatterns = [path('api/', include('drfDemo.urls')),path('', admin.site.urls)
]
8.测试增删改查请求,如下图
9.修改drfDemo>views.py,使用功能类优化请求,如:ListModelMixin、
CreateModelMixin、UpdateModelMixin、DestroyModelMixin
from .models import Student
from .serializers import StudentSerializer
from rest_framework import generics, status
from rest_framework.exceptions import ValidationError
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, UpdateModelMixin
from rest_framework.response import Responseclass DrfDemoAddView(generics.GenericAPIView,CreateModelMixin):""" 提供学生列表查询和创建新学生的API接口 """queryset = Student.objects.all()serializer_class = StudentSerializerdef post(self, request, *args, **kwargs):try:# 调用create()方法的异常self.create(request, *args, **kwargs)return Response({'code': status.HTTP_200_OK,'message': '学生添加成功'}, status=status.HTTP_200_OK)except ValidationError as e:# 捕获序列化器验证错误(如字段格式错误)return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '请求失败','errors': e.detail # 详细错误信息}, status=status.HTTP_400_BAD_REQUEST)class DrfDemoGetView(generics.GenericAPIView, ListModelMixin):""" 提供学生列表查询和创建新学生的API接口 """queryset = Student.objects.all()serializer_class = StudentSerializerdef get(self, request, *args, **kwargs):response = self.list(request, *args, **kwargs)return Response({'code': status.HTTP_200_OK,'message': '请求成功','data': response.data}, status=status.HTTP_200_OK)class DrfDemoUpdateView(generics.GenericAPIView, UpdateModelMixin):""" 提供学生列表查询和创建新学生的API接口 """queryset = Student.objects.all()serializer_class = StudentSerializerdef get_object(self):"""重写此方法,从请求数据中获取学生ID"""student_id = self.request.data.get('student_id')if not student_id:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '缺少student_id字段'}, status=status.HTTP_400_BAD_REQUEST)obj = self.queryset.get(student_id=student_id)# 检查对象权限(如果需要)self.check_object_permissions(self.request, obj)return objdef post(self, request, *args, **kwargs):"""通过POST方法修改学生信息请求数据中需包含id字段指定要修改的学生"""# 从请求数据中获取学生IDkwargs['partial'] = Truetry:# 调用create()方法的异常self.update(request, *args, **kwargs)return Response({'code': status.HTTP_200_OK,'message': '修改成功'}, status=status.HTTP_200_OK)except ValidationError as e:# 捕获序列化器验证错误(如字段格式错误)return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '请求失败','errors': e.detail # 详细错误信息}, status=status.HTTP_400_BAD_REQUEST)class DrfDemoDeleteView(generics.GenericAPIView, DestroyModelMixin):"""提供学生信息删除的API接口(使用GET方法)"""queryset = Student.objects.all()serializer_class = StudentSerializerdef get_object(self):"""重写此方法,从请求数据中获取学生ID"""student_id = self.request.GET.get('student_id')if not student_id:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '缺少student_id字段'}, status=status.HTTP_400_BAD_REQUEST)obj = self.queryset.get(student_id=student_id)# 检查对象权限(如果需要)self.check_object_permissions(self.request, obj)return objdef get(self, request, *args, **kwargs):"""通过GET方法删除学生信息"""student_id = request.GET.get('student_id')try:self.destroy(request, *args, **kwargs)# 4. 返回删除成功响应return Response({'code': status.HTTP_200_OK,'message': f'学生(student_id:{student_id})删除成功'}, status=status.HTTP_200_OK)except Student.DoesNotExist:return Response({'code': status.HTTP_404_NOT_FOUND,'message': f'student_id为{student_id}的学生不存在,无法删除'}, status=status.HTTP_404_NOT_FOUND)
10.修改drfDemo>views.py,使用视图类优化请求,如:CreateAPIView、ListAPIView、DestroyAPIView、UpdateAPIView
from .models import Student
from .serializers import StudentSerializer
from rest_framework import generics, status
from rest_framework.exceptions import ValidationError
from rest_framework.response import Responseclass DrfDemoAddView(generics.CreateAPIView):""" 提供学生列表查询和创建新学生的API接口 """queryset = Student.objects.all()serializer_class = StudentSerializerdef post(self, request, *args, **kwargs):try:# 调用create()方法的异常self.create(request, *args, **kwargs)return Response({'code': status.HTTP_200_OK,'message': '学生添加成功'}, status=status.HTTP_200_OK)except ValidationError as e:# 捕获序列化器验证错误(如字段格式错误)return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '请求失败','errors': e.detail # 详细错误信息}, status=status.HTTP_400_BAD_REQUEST)class DrfDemoGetView(generics.ListAPIView):""" 提供学生列表查询和创建新学生的API接口 """queryset = Student.objects.all()serializer_class = StudentSerializerdef get(self, request, *args, **kwargs):response = self.list(request, *args, **kwargs)return Response({'code': status.HTTP_200_OK,'message': '请求成功','data': response.data}, status=status.HTTP_200_OK)class DrfDemoUpdateView(generics.UpdateAPIView):""" 提供学生列表查询和创建新学生的API接口 """queryset = Student.objects.all()serializer_class = StudentSerializerdef get_object(self):"""重写此方法,从请求数据中获取学生ID"""student_id = self.request.data.get('student_id')if not student_id:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '缺少student_id字段'}, status=status.HTTP_400_BAD_REQUEST)obj = self.queryset.get(student_id=student_id)# 检查对象权限(如果需要)self.check_object_permissions(self.request, obj)return objdef post(self, request, *args, **kwargs):"""通过POST方法修改学生信息请求数据中需包含id字段指定要修改的学生"""# 从请求数据中获取学生IDkwargs['partial'] = Truetry:# 调用create()方法的异常self.update(request, *args, **kwargs)return Response({'code': status.HTTP_200_OK,'message': '修改成功'}, status=status.HTTP_200_OK)except ValidationError as e:# 捕获序列化器验证错误(如字段格式错误)return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '请求失败','errors': e.detail # 详细错误信息}, status=status.HTTP_400_BAD_REQUEST)class DrfDemoDeleteView(generics.DestroyAPIView):"""提供学生信息删除的API接口(使用GET方法)"""queryset = Student.objects.all()serializer_class = StudentSerializerdef get_object(self):"""重写此方法,从请求数据中获取学生ID"""student_id = self.request.GET.get('student_id')if not student_id:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': '缺少student_id字段'}, status=status.HTTP_400_BAD_REQUEST)obj = self.queryset.get(student_id=student_id)# 检查对象权限(如果需要)self.check_object_permissions(self.request, obj)return objdef get(self, request, *args, **kwargs):"""通过GET方法删除学生信息"""student_id = request.GET.get('student_id')try:self.destroy(request, *args, **kwargs)# 4. 返回删除成功响应return Response({'code': status.HTTP_200_OK,'message': f'学生(student_id:{student_id})删除成功'}, status=status.HTTP_200_OK)except Student.DoesNotExist:return Response({'code': status.HTTP_404_NOT_FOUND,'message': f'student_id为{student_id}的学生不存在,无法删除'}, status=status.HTTP_404_NOT_FOUND)
三、DRF视图集
1.DRF视图集相关,官网文档:https://www.django-rest-framework.org/api-guide/viewsets/
视图集类名 | 说明 |
---|---|
ViewSet | 基础ViewSet类,提供action映射机制但无具体实现,需手动实现list、retrieve等方法 |
GenericViewSet | 继承GenericAPIView,提供通用视图功能但无默认action实现,需手动实现具体方法 |
ModelViewSet | 完整实现类,继承GenericViewSet并包含所有标准CRUD操作,自动提供全部action |
ReadOnlyModelViewSet | 只读版本,仅提供list和retrieve两个只读操作,不包含创建更新删除功能 |
视图集内置方法 | HTTP 方法 | URL 路径 | 描述 |
---|---|---|---|
list | GET | /path/ | 获取资源列表 |
retrieve | GET | /path/{id}/ | 获取单个资源详情 |
create | POST | /path/ | 创建新资源 |
update | PUT | /path/{id}/ | 全量更新资源 |
partial_update | PATCH | /path/{id}/ | 部分更新资源 |
destroy | DELETE | /path/{id}/ | 删除资源 |
2.修改drfDemo>views.py,使用ViewSet优化Student请求 |
from .models import Student
from .serializers import StudentSerializer
from rest_framework import status, viewsets
from rest_framework.response import Responseclass DrfDemoView(viewsets.ViewSet):@staticmethoddef get_queryset():"""获取所有学生数据"""return Student.objects.all()@staticmethoddef get_serializer(*args, **kwargs):"""获取序列化器"""return StudentSerializer(*args, **kwargs)def list(self, request, *args, **kwargs):"""获取所有学生GET /api/students/"""queryset = self.get_queryset()serializer = self.get_serializer(queryset, many=True)return Response({'code': status.HTTP_200_OK,'message': '请求成功','data': serializer.data}, status=status.HTTP_200_OK)def create(self, request, *args, **kwargs):"""创建新学生POST /api/students/"""try:serializer = self.get_serializer(data=request.data)serializer.is_valid(raise_exception=True)serializer.save()return Response({'code': status.HTTP_200_OK,'message': '学生添加成功'}, status=status.HTTP_200_OK)except Exception as e:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': f'创建学生失败: {str(e)}'}, status=status.HTTP_400_BAD_REQUEST)def update(self, request, pk=None):"""更新学生信息PUT /api/students/{id}/"""try:student = self.get_queryset().get(student_id=pk)serializer = self.get_serializer(student, data=request.data,partial=True)serializer.is_valid(raise_exception=True)serializer.save()return Response({'code': status.HTTP_200_OK,'message': '学生信息更新成功','data': serializer.data})except Student.DoesNotExist:return Response({'code': status.HTTP_404_NOT_FOUND,'message': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)except Exception as e:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': f'更新学生失败: {str(e)}','errors': serializer.errors if 'serializer' in locals() else None}, status=status.HTTP_400_BAD_REQUEST)def destroy(self, request, pk=None):"""删除学生信息DELETE /api/students/{id}/"""try:student = self.get_queryset().get(student_id=pk)student.delete()return Response({'code': status.HTTP_200_OK,'message': '学生信息删除成功'})except Student.DoesNotExist:return Response({'code': status.HTTP_404_NOT_FOUND,'message': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)except Exception as e:return Response({'code': status.HTTP_500_INTERNAL_SERVER_ERROR,'message': f'删除学生失败: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
3.修改drfDemo>urls.py,官网文档:https://www.django-rest-framework.org/api-guide/routers/
'''
GET http://127.0.0.1:6001/api/drfDemo/ 获取
POST http://127.0.0.1:6001/api/drfDemo/ 添加
PUT http://127.0.0.1:6001/api/drfDemo/1/ 更新,1为学生ID
DELETE http://127.0.0.1:6001/api/drfDemo/1/ 删除,1为学生ID
'''from .views import DrfDemoView
from rest_framework import routers# 创建路由器
router = routers.SimpleRouter()
router.register(r'drfDemo', DrfDemoView, basename='drfDemo')
urlpatterns = router.urls
4.修改drfDemo>views.py,使用GenericViewSet优化Student请求,内置get_queryset、get_serializer等方法,但是需要配置queryset、serializer_class
from .models import Student
from .serializers import StudentSerializer
from rest_framework import status, viewsets
from rest_framework.response import Responseclass DrfDemoView(viewsets.GenericViewSet):queryset = Student.objects.all()serializer_class = StudentSerializerdef list(self, request, *args, **kwargs):"""获取所有学生GET /api/students/"""queryset = self.get_queryset()serializer = self.get_serializer(queryset, many=True)return Response({'code': status.HTTP_200_OK,'message': '请求成功','data': serializer.data}, status=status.HTTP_200_OK)def create(self, request, *args, **kwargs):"""创建新学生POST /api/students/"""try:serializer = self.get_serializer(data=request.data)serializer.is_valid(raise_exception=True)serializer.save()return Response({'code': status.HTTP_200_OK,'message': '学生添加成功'}, status=status.HTTP_200_OK)except Exception as e:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': f'创建学生失败: {str(e)}'}, status=status.HTTP_400_BAD_REQUEST)def update(self, request, pk=None):"""更新学生信息PUT /api/students/{id}/"""try:student = self.get_queryset().get(student_id=pk)serializer = self.get_serializer(student, data=request.data, partial=True)serializer.is_valid(raise_exception=True)serializer.save()return Response({'code': status.HTTP_200_OK,'message': '学生信息更新成功','data': serializer.data})except Student.DoesNotExist:return Response({'code': status.HTTP_404_NOT_FOUND,'message': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)except Exception as e:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': f'更新学生失败: {str(e)}','errors': serializer.errors if 'serializer' in locals() else None}, status=status.HTTP_400_BAD_REQUEST)def destroy(self, request, pk=None):"""删除学生信息DELETE /api/students/{id}/"""try:student = self.get_queryset().get(student_id=pk)student.delete()return Response({'code': status.HTTP_200_OK,'message': '学生信息删除成功'})except Student.DoesNotExist:return Response({'code': status.HTTP_404_NOT_FOUND,'message': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)except Exception as e:return Response({'code': status.HTTP_500_INTERNAL_SERVER_ERROR,'message': f'删除学生失败: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
5.修改drfDemo>views.py,使用ModelViewSet优化Student请求,ModelViewSet内置list、create、update、delete等请求,如需自定义响应结果结构可参考GenericViewSet
from .models import Student
from .serializers import StudentSerializer
from rest_framework import viewsetsclass DrfDemoView(viewsets.ModelViewSet):queryset = Student.objects.all()serializer_class = StudentSerializer
6.由于视图集中内置请求都是的path都是固定的,如需达到图二的效果,需自定义action
from .models import Student
from .serializers import StudentSerializer
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Responseclass DrfDemoView(viewsets.GenericViewSet):serializer_class = StudentSerializer@action(detail=False, methods=['get'])def query(self, request, *args, **kwargs):"""获取所有学生"""queryset = Student.objects.all()serializer = self.get_serializer(queryset, many=True)return Response({'code': status.HTTP_200_OK,'message': '请求成功','data': serializer.data}, status=status.HTTP_200_OK)@action(detail=False, methods=['post'])def add(self, request, *args, **kwargs):"""获取所有学生"""try:serializer = self.get_serializer(data=request.data)serializer.is_valid(raise_exception=True)serializer.save()return Response({'code': status.HTTP_200_OK,'message': '学生添加成功'}, status=status.HTTP_200_OK)except Exception as e:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': f'创建学生失败: {str(e)}'}, status=status.HTTP_400_BAD_REQUEST)@action(detail=False, url_path='update',methods=['post'])def new_update(self, request, *args, **kwargs):"""获取所有学生"""try:queryset = Student.objects.all()student = queryset.get(student_id=request.data['student_id'])serializer = self.get_serializer(student, data=request.data, partial=True)serializer.is_valid(raise_exception=True)serializer.save()return Response({'code': status.HTTP_200_OK,'message': '学生信息更新成功'})except Student.DoesNotExist:return Response({'code': status.HTTP_404_NOT_FOUND,'message': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)except Exception as e:return Response({'code': status.HTTP_400_BAD_REQUEST,'message': f'更新学生失败: {str(e)}'}, status=status.HTTP_400_BAD_REQUEST)@action(detail=False, methods=['post'])def delete(self, request, *args, **kwargs):try:queryset = Student.objects.all()student = queryset.get(student_id=request.data['student_id'])student.delete()return Response({'code': status.HTTP_200_OK,'message': '学生信息删除成功'})except Student.DoesNotExist:return Response({'code': status.HTTP_404_NOT_FOUND,'message': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)except Exception as e:return Response({'code': status.HTTP_500_INTERNAL_SERVER_ERROR,'message': f'删除学生失败: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
四、统一请求响应结果格式
1.setting.py同目录新建renderers.py渲染器,统一请求响应结果格式,官网文档:https://www.django-rest-framework.org/api-guide/renderers/
from rest_framework import status
from rest_framework.renderers import JSONRenderer as DjangoJSONRendererclass JSONRenderer(DjangoJSONRenderer):"""自定义JSON渲染器,统一所有状态码的响应格式响应格式:- 成功 (2xx): {"status": "success", "code": 200, "message": "操作成功", "data": {...}}- 重定向 (3xx): {"status": "redirect", "code": 301, "message": "重定向", "data": {...}}- 客户端错误 (4xx): {"status": "error", "code": 400, "message": "错误信息", "errors": [...]}- 服务器错误 (5xx): {"status": "error", "code": 500, "message": "服务器错误", "errors": []}"""def render(self, data, accepted_media_type=None, renderer_context=None):if renderer_context is None:renderer_context = {}response = renderer_context.get('response')status_code = getattr(response, 'status_code', status.HTTP_200_OK)data = data or {}# 定义状态码到状态文本的映射status_mapping = {'success': range(200, 300), # 2xx'redirect': range(300, 400), # 3xx'error': range(400, 600), # 4xx和5xx}# 确定状态文本response_status = 'success'for status_text, code_range in status_mapping.items():if status_code in code_range:response_status = status_textbreak# 默认消息映射default_messages = {# 2xx 成功status.HTTP_200_OK: '请求成功',status.HTTP_201_CREATED: '创建成功',status.HTTP_202_ACCEPTED: '请求已接受',status.HTTP_204_NO_CONTENT: '删除成功',# 3xx 重定向status.HTTP_301_MOVED_PERMANENTLY: '永久重定向',status.HTTP_302_FOUND: '临时重定向',status.HTTP_304_NOT_MODIFIED: '资源未修改',# 4xx 客户端错误status.HTTP_400_BAD_REQUEST: '请求错误',status.HTTP_401_UNAUTHORIZED: '未授权',status.HTTP_403_FORBIDDEN: '禁止访问',status.HTTP_404_NOT_FOUND: '资源不存在',status.HTTP_405_METHOD_NOT_ALLOWED: '方法不允许',status.HTTP_409_CONFLICT: '资源冲突',status.HTTP_422_UNPROCESSABLE_ENTITY: '参数验证失败',# 5xx 服务器错误status.HTTP_500_INTERNAL_SERVER_ERROR: '服务器内部错误',status.HTTP_502_BAD_GATEWAY: '网关错误',status.HTTP_503_SERVICE_UNAVAILABLE: '服务不可用',}# 获取消息message = default_messages.get(status_code, '操作完成')# 构建统一的响应格式if 200 <= status_code < 300:# 成功响应if isinstance(data, dict) and 'status' in data and 'data' in data:# 已经是自定义格式,保持原样final_data = dataelse:final_data = {'status': response_status,'code': status_code,'message': message,'data': data}elif 300 <= status_code < 400:# 重定向响应final_data = {'status': response_status,'code': status_code,'message': message,'data': {'redirect_url': data.get('url', data.get('redirect_url', '')) if isinstance(data, dict) else '','original_data': data}}else:# 错误响应 (4xx和5xx)final_data = {'status': response_status,'code': status_code,'message': message,'data': None}if 'errors' in data:errors = data['errors']final_data['errors'] = errorsreturn super().render(final_data, accepted_media_type, renderer_context)
2.修改setting.py,增加REST_FRAMEWORK配置
REST_FRAMEWORK = {'DEFAULT_RENDERER_CLASSES': ['demo.renderers.JSONRenderer','rest_framework.renderers.BrowsableAPIRenderer', # 保留可浏览API]
}
3.修改drfDemo>views.py
from .models import Student
from .serializers import StudentSerializer
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.response import Responseclass DrfDemoView(viewsets.GenericViewSet):queryset = Student.objects.all()serializer_class = StudentSerializer@action(detail=False, methods=['get'])def query(self, request, *args, **kwargs):"""获取所有学生"""queryset = Student.objects.all()serializer = self.get_serializer(queryset, many=True)return Response(serializer.data, status=status.HTTP_200_OK)@action(detail=False, methods=['post'])def add(self, request, *args, **kwargs):"""获取所有学生"""serializer = self.get_serializer(data=request.data)try:serializer.is_valid(raise_exception=True)serializer.save()return Response(request.data, status=status.HTTP_200_OK)except ValidationError:return Response({'errors': serializer.errors},status=status.HTTP_400_BAD_REQUEST)@action(detail=False, url_path='update',methods=['post'])def new_update(self, request, *args, **kwargs):"""获取所有学生"""try:queryset = self.get_queryset()student = queryset.get(student_id=request.data['student_id'])serializer = self.get_serializer(student, data=request.data, partial=True)try:serializer.is_valid(raise_exception=True)except ValidationError:return Response({'errors': serializer.errors},status=status.HTTP_400_BAD_REQUEST)serializer.save()return Response(serializer.data, status=status.HTTP_200_OK)except KeyError:return Response({'errors': '缺少student_id参数'}, status=status.HTTP_400_BAD_REQUEST)except Student.DoesNotExist:return Response({'errors': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)@action(detail=False, methods=['post'])def delete(self, request, *args, **kwargs):try:queryset = self.get_queryset()student = queryset.get(student_id=request.data['student_id'])student.delete()return Response(status=status.HTTP_200_OK)except KeyError:return Response({'errors': '缺少student_id参数'}, status=status.HTTP_400_BAD_REQUEST)except Student.DoesNotExist:return Response({'message': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)
4.效果图如下
五、JWT认证配置
1.运行uv add djangorestframework-simplejwt --dev安装djangorestframework-simplejwt,未使用uv管理工具可使用pip install djangorestframework-simplejwt,官网文档:https://django-rest-framework-simplejwt.readthedocs.io/en/latest/
2.进入项目目录,运行 uv run python manage.py startapp users新建应用,未使用uv管理工具可使用python manage.py startapp users
3.修改setting.py
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','rest_framework','rest_framework_simplejwt','drfDemo','users',
]
REST_FRAMEWORK = {'DEFAULT_RENDERER_CLASSES': ['demo.renderers.JSONRenderer','rest_framework.renderers.BrowsableAPIRenderer',],'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework_simplejwt.authentication.JWTAuthentication',],'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',],
}SIMPLE_JWT = {# Token有效时间'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),# Refresh Token有效时间'REFRESH_TOKEN_LIFETIME': timedelta(days=1),# 是否允许刷新Token'ROTATE_REFRESH_TOKENS': False,# 是否在刷新Token时将旧Token加入黑名单'BLACKLIST_AFTER_ROTATION': True,# Token验证设置'ALGORITHM': 'HS256','SIGNING_KEY': SECRET_KEY,'VERIFYING_KEY': None,'AUDIENCE': None,'ISSUER': None,# Token验证字段'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',# 刷新Token的Sliding Token设置'JTI_CLAIM': 'jti','SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp','SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
4.修改users>view.py
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshTokenclass UserView(viewsets.GenericViewSet):"""登录视图集,使用GenericViewSet实现提供登录功能,返回JWT令牌和用户信息"""permission_classes = [AllowAny]@action(detail=False, methods=['post'])def login2(self, request, *args, **kwargs):"""处理登录请求POST /api/login/"""user_info = {'user_id': '001','username': request.data.get('username'),'password': request.data.get('password')}# 验证输入if not user_info['username'] or not user_info['password']:return Response({'detail': '用户名和密码不能为空'},status=status.HTTP_400_BAD_REQUEST)# 生成JWT令牌refresh = RefreshToken()refresh.payload.update(user_info)return Response({**user_info,'token': str(refresh.access_token)})
5.创建users>urls.py
from .views import UserView
from rest_framework import routers# 创建路由器
router = routers.SimpleRouter()
router.register(r'users', UserView, basename='user')
urlpatterns = router.urls
6.修改settings.py同级urls.py
from django.contrib import admin
from django.urls import include, pathurlpatterns = [path('api/', include('users.urls')),path('api/', include('drfDemo.urls')),path('', admin.site.urls)
]
7.访问/api/drfDemo/add/请求会发现请求报未授权,说明JWT已配置
8.验证JWT配置成功:访问/api/users/login/,拿到token,这里的账户密码是django后台管理的账户
9.验证JWT配置成功:使用/api/users/login/请求拿到的token,再次请求/api/drfDemo/add/,会发现请求成功,说明JWT配置成功
六、生成API文档
1.运行 uv add drf-spectacular安装drf-spectacular,未使用uv管理工具可使用pip install drf-spectacular,官网文档:https://drf-spectacular.readthedocs.io/en/latest/
2.修改setting.py
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','rest_framework','rest_framework_simplejwt','drf_spectacular','drfDemo','users',
]
REST_FRAMEWORK = {'DEFAULT_RENDERER_CLASSES': ['demo.renderers.JSONRenderer','rest_framework.renderers.BrowsableAPIRenderer',],'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework_simplejwt.authentication.JWTAuthentication',],'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',],'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema'
}
SPECTACULAR_SETTINGS = {'TITLE': 'API文档', # 标题'DESCRIPTION': 'API接口文档描述', # 描述'VERSION': '1.0.0', # 版本'SERVE_INCLUDE_SCHEMA': False, # 不包含schema本身的端点'TAGS': [{'name': '用户管理', 'description': '用户登录等相关接口'}, # 第1个显示{'name': '学生管理', 'description': '学生信息CRUD接口'}, # 第2个显示],'SWAGGER_UI_SETTINGS': {'defaultModelsExpandDepth': -1, # 不显示任何模型(包括Schemas分组)},
}
3.修改setting.py同目录urls.py
from django.contrib import admin
from django.urls import include, path
from drf_spectacular.views import (SpectacularAPIView, # 生成OpenAPI schema文件SpectacularRedocView, # ReDocSpectacularSwaggerView, # Swagger UI
)urlpatterns = [path('api/', include('users.urls')),path('api/', include('drfDemo.urls')),path('api/schema', SpectacularAPIView.as_view(), name='schema'), # schema文件path('swagger', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger'),path('redoc', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),path('', admin.site.urls)
]
4.修改drfDemo>serializers.py
from .models import Student
from rest_framework import serializers
from rest_framework.validators import UniqueValidatorclass StudentSerializer(serializers.ModelSerializer):"""学生信息序列化器,用于学生信息的序列化和反序列化支持所有字段的增删改查操作"""# 第一种字段校验方式name = serializers.CharField(max_length=100,error_messages={'max_length': '姓名必须是11位数字', 'required': '缺少name参数'},)student_id = serializers.CharField(required=True,max_length=20,error_messages={'required': '缺少student_id参数','max_length': '学号最大长度为20字符'},# 显式添加唯一校验器validators=[UniqueValidator(queryset=Student.objects.all(),message='学号已存在,请更换' # 自定义错误提示)])gender = serializers.ChoiceField(choices=Student.GENDER_CHOICES,error_messages={'required': '缺少gender参数','invalid_choice': f'性别必须是 {[choice[0] for choice in Student.GENDER_CHOICES]} 中的一个',},)birth_date = serializers.DateField(error_messages={'required': '缺少birth_date参数', 'invalid': '出生日期格式错误,请使用YYYY-MM-DD'})email = serializers.EmailField(required=False,allow_blank=True, # 允许空字符串error_messages={'invalid': '邮箱格式错误(例如:example@domain.com)' # 自定义错误提示},)major = serializers.CharField(max_length=100,error_messages={'max_length': '专业最大长度100', 'required': '缺少major参数'},)grade = serializers.IntegerField(min_value=1,max_value=6,error_messages={'required': '缺少grade参数','min_value': '年级必须大于等于1','max_value': '年级必须小于等于6','invalid': '年级必须是数字',},)admission_date = serializers.DateField(required=False,error_messages={'invalid': '入学日期格式错误,请使用YYYY-MM-DD'})is_active = serializers.BooleanField(default=True)age = serializers.IntegerField(source='get_age', read_only=True)created_at = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)updated_at = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)class Meta:model = Student# 包含模型所有字段fields = '__all__'# 可以根据需要指定只读字段,如自动生成的时间戳read_only_fields = ['created_at', 'updated_at']# 第二种字段校验方式extra_kwargs = {# 入学日期:可选 + 格式校验'admission_date': {'required': False,'error_messages': {'invalid': '入学日期格式错误,请使用YYYY-MM-DD'}}}# 第三种字段校验方式def validate_phone(self, value):"""验证电话号码格式"""if value and not (value.isdigit() and len(value) in [11]):raise serializers.ValidationError('手机号必须是11位数字')return valueclass StudentIdValidate(serializers.Serializer):"""学生信息序列化器,用于学生信息的序列化和反序列化支持所有字段的增删改查操作"""student_id = serializers.CharField(required=True,max_length=20,error_messages={'required': '缺少student_id参数','max_length': '学号最大长度为20字符'})
5.修改drfDemo>views.py
from .models import Student
from .serializers import StudentIdValidate, StudentSerializer
from drf_spectacular.utils import OpenApiResponse, extend_schema, extend_schema_view
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.response import Response@extend_schema_view(query=extend_schema(tags=['学生管理'],summary='获取所有学生',description='查询数据库中所有学生的信息列表',),add=extend_schema(tags=['学生管理'],summary='添加新学生',description='通过提交学生信息(如姓名、学号等)创建新学生记录',responses={200: OpenApiResponse(description='创建成功,返回新学生信息',response=StudentSerializer)}),new_update=extend_schema(tags=['学生管理'],summary='更新学生',description='根据student_id更新指定学生的信息(支持部分字段更新)',responses={200: OpenApiResponse(description='更新成功,返回更新后的学生信息',response=StudentSerializer)}),delete=extend_schema(tags=['学生管理'],summary='删除学生',description='根据student_id删除指定的学生记录',request=StudentIdValidate,responses={200: OpenApiResponse(description='删除成功'),400: OpenApiResponse(description='缺少student_id参数'),404: OpenApiResponse(description='学生不存在')}),
)
class DrfDemoView(viewsets.GenericViewSet):swagger_tags = ['学生管理']queryset = Student.objects.all()serializer_class = StudentSerializer@action(detail=False, methods=['get'])def query(self, request, *args, **kwargs):"""获取所有学生"""queryset = Student.objects.all()serializer = self.get_serializer(queryset, many=True)return Response(serializer.data, status=status.HTTP_200_OK)@action(detail=False, methods=['post'])def add(self, request, *args, **kwargs):"""获取所有学生"""serializer = self.get_serializer(data=request.data)try:serializer.is_valid(raise_exception=True)serializer.save()return Response(request.data, status=status.HTTP_200_OK)except ValidationError:return Response({'errors': serializer.errors}, status=status.HTTP_400_BAD_REQUEST)@action(detail=False, url_path='update',methods=['post'])def new_update(self, request, *args, **kwargs):"""获取所有学生"""student_id_validate = StudentIdValidate(data=request.data)try:student_id_validate.is_valid(raise_exception=True)queryset = self.get_queryset()student = queryset.get(student_id=request.data['student_id'])serializer = self.get_serializer(student, data=request.data, partial=True)try:serializer.is_valid(raise_exception=True)except ValidationError:return Response({'errors': serializer.errors}, status=status.HTTP_400_BAD_REQUEST)serializer.save()return Response(serializer.data, status=status.HTTP_200_OK)except ValidationError:return Response({'errors': student_id_validate.errors}, status=status.HTTP_400_BAD_REQUEST)except Student.DoesNotExist:return Response({'errors': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)@action(detail=False, methods=['post'])def delete(self, request, *args, **kwargs):student_id_validate = StudentIdValidate(data=request.data)try:student_id_validate.is_valid(raise_exception=True)queryset = self.get_queryset()student = queryset.get(student_id=request.data['student_id'])student.delete()return Response(status=status.HTTP_200_OK)except ValidationError:return Response({'errors': student_id_validate.errors}, status=status.HTTP_400_BAD_REQUEST)except Student.DoesNotExist:return Response({'message': '学生不存在'}, status=status.HTTP_404_NOT_FOUND)
6.新增users>serializers.py
from rest_framework import serializersclass LoginValidate(serializers.Serializer):username = serializers.CharField(error_messages={'required': '缺少username参数'})password = serializers.CharField(error_messages={'required': '缺少password参数'})class LoginSuccessResponse(serializers.Serializer):user_id = serializers.CharField()username = serializers.CharField()password = serializers.CharField()access_token = serializers.CharField()
7.新增users>views.py
from .serializers import LoginSuccessResponse, LoginValidate
from drf_spectacular.utils import OpenApiResponse, extend_schema, extend_schema_view
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken@extend_schema_view(login=extend_schema(tags=['用户管理'],summary='用户登录',description='通过用户名和密码登录,返回JWT令牌',request=LoginValidate,responses=OpenApiResponse(description='登录成功,返回用户信息和JWT令牌',response=LoginSuccessResponse))
)
class UserView(viewsets.GenericViewSet):"""登录视图集,使用GenericViewSet实现提供登录功能,返回JWT令牌和用户信息"""permission_classes = [AllowAny]@action(detail=False, methods=['post'])def login(self, request, *args, **kwargs):"""处理登录请求POST /api/login/"""login_validate = LoginValidate(data=request.data)try:login_validate.is_valid(raise_exception=True)user_info = {'user_id': '001','username': request.data.get('username'),'password': request.data.get('password')}# 生成JWT令牌refresh = RefreshToken()refresh.payload.update(user_info)return Response({**user_info,'access_token': str(refresh.access_token)})except ValidationError:return Response({'errors': login_validate.errors}, status=status.HTTP_400_BAD_REQUEST)
8.Swagger UI文档访问http://127.0.0.1:6001/swagger#/,官网文档:https://swagger.io/docs
9.ReDoc文档访问http://127.0.0.1:6001/redoc,官网文档:https://redocly.com/redoc
10.配置详解
SPECTACULAR_DEFAULTS = {# 一个正则表达式,用于指定所有操作路径的共同前缀。如果# SCHEMA_PATH_PREFIX 设置为 None,drf-spectacular 将尝试估算# 一个共同前缀。使用 '' 可禁用。# 主要用于标签提取,例如路径 '/api/v1/albums' 搭配# SCHEMA_PATH_PREFIX 正则 '/api/v[0-9]' 会生成标签 'albums'。'SCHEMA_PATH_PREFIX': None,# 从操作路径中移除匹配的 SCHEMA_PATH_PREFIX。通常与# SERVERS 中附加的前缀配合使用。'SCHEMA_PATH_PREFIX_TRIM': False,# 手动向操作路径插入一个前缀,例如 '/service/backend'。# 例如,当 API 作为子资源挂载在代理后面而 Django 未察觉时,# 可使用此配置对齐路径。也可通过 SERVERS 指定前缀,但此配置# 能让操作路径更明确。'SCHEMA_PATH_PREFIX_INSERT': '',# {pk} 到 {id} 的转换由 SCHEMA_COERCE_PATH_PK 控制。此外,# 某些库(如 drf-nested-routers)使用 "_pk" 后缀的路径变量。# 此设置全局将类似 "{user_pk}" 的路径变量转换为 "{user_id}"。'SCHEMA_COERCE_PATH_PK_SUFFIX': False,# 影响组件构建方式的 schema 生成参数。# 某些 schema 功能可能无法很好地适配目标场景。# 拆分/修改组件可能有助于缓解这些问题。'DEFAULT_GENERATOR_CLASS': 'drf_spectacular.generators.SchemaGenerator',# 为 PATCH 端点创建单独的组件(不含 required 列表)'COMPONENT_SPLIT_PATCH': True,# 在适当情况下将组件拆分为请求和响应部分# 强烈推荐启用此设置以获得最准确的 API 描述,# 但代价是会生成更多组件。'COMPONENT_SPLIT_REQUEST': False,# 帮助那些难以处理只读属性的客户端生成器目标。'COMPONENT_NO_READ_ONLY_REQUIRED': False,# 为不允许空字符串的字段添加 "minLength: 1"。默认禁用,# 因为序列化器不会在响应中严格执行此规则,因此# "minLength: 1" 可能并不总是准确描述 API 行为。# 当启用 COMPONENT_SPLIT_REQUEST 时会隐式启用,因为# 分离请求和响应组件后可以准确建模。'ENFORCE_NON_BLANK_FIELDS': False,# 此版本字符串将显示在 schema 头部。默认 OpenAPI 版本为 3.0.3,# 经过充分测试。现在也支持 3.1.0,包含相同功能和一些# 强制性但次要的更改。'OAS_VERSION': '3.0.3',# 用于 SpectacularAPIView 提供 schema 子集的配置'SERVE_URLCONF': None,# 完整的公共 schema 或基于请求用户的子集'SERVE_PUBLIC': True,# 将 schema 端点包含到 schema 中'SERVE_INCLUDE_SCHEMA': True,# spectacular 视图的认证/权限类列表'SERVE_PERMISSIONS': ['rest_framework.permissions.AllowAny'],# None 将默认使用 DRF 的 AUTHENTICATION_CLASSES'SERVE_AUTHENTICATION': None,# 传递给 SwaggerUI({ ... }) 的通用配置字典# https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/# 配置通过 json.dumps() 序列化。如果需要自定义 JS,可使用字符串,# 字符串必须包含有效的 JS 且会原样传递。'SWAGGER_UI_SETTINGS': {'deepLinking': True,},# 使用额外的 OAuth2 配置初始化 SwaggerUI# https://swagger.io/docs/open-source-tools/swagger-ui/usage/oauth2/'SWAGGER_UI_OAUTH2_CONFIG': {},# 传递给 Redoc.init({ ... }) 的通用配置字典# https://redocly.com/docs/redoc/config/#functional-settings# 配置通过 json.dumps() 序列化。如果需要自定义 JS,可使用字符串,# 字符串必须包含有效的 JS 且会原样传递。'REDOC_UI_SETTINGS': {},# swagger 和 redoc 的 CDN。可根据需求更改版本甚至自行托管。# 关于自行托管,参见 README 中的 sidecar 选项。'SWAGGER_UI_DIST': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest','SWAGGER_UI_FAVICON_HREF': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest/favicon-32x32.png','REDOC_DIST': 'https://cdn.jsdelivr.net/npm/redoc@latest',# 向路径和组件追加 OpenAPI 对象(附加到生成的对象)'APPEND_PATHS': {},'APPEND_COMPONENTS': {},# 强烈不推荐(除了 djangorestframework-api-key 库)# 请不要再使用此配置,因为它可能带来难以解决的问题。# 对于认证,强烈推荐使用 OpenApiAuthenticationExtension,# 因为它们更健壮且易于编写。# 但如果使用,方法列表会附加到 schema 中的每个端点!'SECURITY': [],# 在 schema 生成结束时运行的后处理函数。# 必须满足接口:result = hook(generator, request, public, result)'POSTPROCESSING_HOOKS': ['drf_spectacular.hooks.postprocess_schema_enums'],# 在 schema 生成前运行的预处理函数。# 必须满足接口:result = hook(endpoints=result),其中 result 是# 元组列表 (path, path_regex, method, callback)。# 示例:'drf_spectacular.hooks.preprocess_exclude_path_format''PREPROCESSING_HOOKS': [],# 确定操作应如何排序。如果打算通过 PREPROCESSING_HOOKS 进行排序,# 请确保禁用此设置。如果配置,排序将在 PREPROCESSING_HOOKS 之后应用。# 接受 True(drf-spectacular 的字母排序器)、False 或 sort 键的可调用对象。'SORT_OPERATIONS': True,# 枚举名称覆盖。键为 "YourEnum" 且值为 "field.choices" 的字典# 例如:{'SomeEnum': ['A', 'B'], 'OtherEnum': 'import.path.to.choices'}'ENUM_NAME_OVERRIDES': {},# 在适当情况下添加 "blank" 和 "null" 枚举选项。在客户端生成有问题时禁用'ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE': True,# 向枚举描述字符串添加/追加 (``选项值`` - 选项名称) 列表'ENUM_GENERATE_CHOICE_DESCRIPTION': True,# 生成的枚举的可选后缀# 例如:{'ENUM_SUFFIX': "Type"} 会生成枚举名称 'StatusType''ENUM_SUFFIX': 'Enum',# 返回应从文档字符串提取中排除的所有类的列表的函数'GET_LIB_DOC_EXCLUDES': 'drf_spectacular.plumbing.get_lib_doc_excludes',# 返回用于视图处理的模拟请求的函数。对于 CLI 使用,# original_request 将为 None。# 接口:request = build_mock_request(method, path, view, original_request, **kwargs)'GET_MOCK_REQUEST': 'drf_spectacular.plumbing.build_mock_request',# 将 "operationId" 和路径参数名称等驼峰化# 操作 schema 本身的驼峰化需要添加# 'drf_spectacular.contrib.djangorestframework_camel_case.camelize_serializer_fields'# 到 POSTPROCESSING_HOOKS。请注意,此钩子依赖于# ``djangorestframework_camel_case``,而 CAMELIZE_NAMES 本身不依赖。'CAMELIZE_NAMES': False,# 更改生成的 OperationId 中动作/方法的位置。例如,# "POST": "group_person_list", "group_person_create"# "PRE": "list_group_person", "create_group_person"'OPERATION_ID_METHOD_POSITION': 'POST',# 确定是否以及如何在 schema 中生成自由格式的 'additionalProperties'。# 某些代码生成器目标对此敏感。None 禁用通用的 'additionalProperties'。# 允许的值为 'dict'、'bool'、None'GENERIC_ADDITIONAL_PROPERTIES': 'dict',# 路径转换器 schema 覆盖(例如 <int:foo>)。可用于修改默认行为# 或为通过 register_converter(...) 注册的自定义转换器提供 schema。# 以转换器标签为键,以基本 Python 类型、OpenApiType 或原始 schema 为值。# 示例:{'aint': OpenApiTypes.INT, 'bint': str, 'cint': {'type': ...}}'PATH_CONVERTER_OVERRIDES': {},# 确定操作参数应按字母数字排序还是按原始顺序排列。# 接受 True、False 或 sort 键的可调用对象。'SORT_OPERATION_PARAMETERS': True,# @extend_schema 允许指定 200 以外的状态码。此功能通常用于# 描述错误响应,很少使用列表机制。因此,默认情况下我们会在# 非 2XX 状态码上抑制列表(分页和过滤)。切换此设置可启用# 无论状态码如何,都使用 ListSerializers/many=True 的列表响应。'ENABLE_LIST_MECHANICS_ON_NON_2XX': False,# 此设置允许你通过访问不同的模型属性来偏离默认管理器。# 为了兼容性,我们默认使用 "objects"。使用 "_default_manager"# 可能会解决大多数问题,但你也可以自由选择任何名称。"DEFAULT_QUERY_MANAGER": 'objects',# 控制在 schema 中暴露哪些认证方法。如果不为 None,将隐藏# 不在白名单中的认证类。使用完整的导入路径,# 例如 ['rest_framework.authentication.TokenAuthentication', ...]。# 空列表 ([]) 将隐藏所有认证方法。默认 None 将显示所有。'AUTHENTICATION_WHITELIST': None,# 控制在 schema 中暴露哪些解析器。工作方式类似于 AUTHENTICATION_WHITELIST。# 允许的解析器列表或 None(允许所有)。'PARSER_WHITELIST': None,# 控制在 schema 中暴露哪些渲染器。工作方式类似于 AUTHENTICATION_WHITELIST。# 如果白名单为 None,默认忽略 rest_framework.renderers.BrowsableAPIRenderer'RENDERER_WHITELIST': None,# 关闭错误和警告消息的选项'DISABLE_ERRORS_AND_WARNINGS': False,# 作为 "./manage.py check --deploy" 的一部分运行示例性 schema 生成并发出警告'ENABLE_DJANGO_DEPLOY_CHECK': True,# 通用 schema 元数据。参考规范获取有效输入# https://spec.openapis.org/oas/v3.0.3#openapi-object'TITLE': '','DESCRIPTION': '','TOS': None,# 可选:可以包含 "name"、"url"、"email"'CONTACT': {},# 可选:必须包含 "name",可以包含 URL'LICENSE': {},# 静态设置 schema 版本。也可以是空字符串。当与视图版本控制一起使用时,# 对于 'v2' 版本的请求,将变为 '0.0.0 (v2)'。# 如果只想渲染请求版本,将 VERSION 设置为 None。'VERSION': '0.0.0',# 可选的服务器列表。# 每个条目必须包含 "url",可以包含 "description"、"variables"# 例如:[{'url': 'https://example.com/v1', 'description': '文本'}, ...]'SERVERS': [],# 在全局范围内定义的标签'TAGS': [],# 可选:OpenAPI 3.1 webhooks 列表。每个条目应为 OpenApiWebhook 实例的导入路径。'WEBHOOKS': [],# 可选:必须包含 'url',可以包含 "description"'EXTERNAL_DOCS': {},# 附加到 schema 的 info 对象的任意规范扩展。# https://swagger.io/specification/#specification-extensions'EXTENSIONS_INFO': {},# 附加到 schema 的根对象的任意规范扩展。# https://swagger.io/specification/#specification-extensions'EXTENSIONS_ROOT': {},# 与 Oauth2 相关的设置。例如用于 django-oauth2-toolkit。# https://spec.openapis.org/oas/v3.0.3#oauth-flows-object'OAUTH2_FLOWS': [],'OAUTH2_AUTHORIZATION_URL': None,'OAUTH2_TOKEN_URL': None,'OAUTH2_REFRESH_URL': None,'OAUTH2_SCOPES': None,
}