Article

后端框架 Django REST Framework

更新于:2026-07-13

第一章:DRF 入门与环境搭建

1.1 DRF 简介与核心特性

名称语法用途代码示例注意事项
Web API无固定语法提供前后端分离的数据接口-需遵循 RESTful 设计规范
可浏览 API自动生成功能开发调试时可直接在浏览器查看接口-生产环境建议关闭以提升安全
序列化器 (Serializer)class MySerializer(serializers.Serializer):将复杂数据(如模型实例)转换为 JSON 可序列化格式-支持反序列化和数据验证
视图 (Views)继承 APIView 或使用 @api_view处理 HTTP 请求并返回响应-比 Django 原生视图更专注 API 逻辑
认证与权限authentication_classes, permission_classes控制谁可以访问哪些接口-默认允许所有人访问,需显式配置限制
内容协商自动根据请求头选择响应格式支持 JSON、HTML、XML 等多种格式输出-可通过配置自定义支持的格式
可扩展性提供插件机制支持自定义认证、权限、分页等组件-社区生态丰富,如 drf-spectacular 用于文档

1.2 安装与配置 DRF

名称语法用途代码示例注意事项
安装 DRFpip install djangorestframework安装 Django REST Framework 包-确保已安装 Django
添加应用INSTALLED_APPS += ['rest_framework']启用 DRF 功能模块INSTALLED_APPS = ['django.contrib.admin', 'django.contrib.auth', ..., 'rest_framework']必须添加,否则无法使用 DRF 组件
配置全局设置REST_FRAMEWORK = { ... }设置默认认证、分页、渲染器等REST_FRAMEWORK = {'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer'], 'DEFAULT_AUTHENTICATION_CLASSES': []}可省略,使用默认配置
配置可浏览 API 页面'rest_framework.urls'启用登录/登出和 API 浏览界面urlpatterns = [path('api-auth/', include('rest_framework.urls'))]仅用于开发,生产环境可移除
检查安装python -c "import rest_framework; print(rest_framework.__version__)"验证是否安装成功-若报错说明未正确安装

1.3 创建第一个 API 视图(APIView 与 @api_view)

名称语法用途代码示例注意事项
@api_view 装饰器@api_view(['GET', 'POST'])将函数视图转换为 APIView 支持的函数from rest_framework.decorators import api_view; @api_view(['GET']); def hello(request): return Response({'message': 'Hello'})必须指定允许的 HTTP 方法
APIView 类class MyView(APIView):面向对象方式处理 API 请求from rest_framework.views import APIView; class HelloView(APIView):; def get(self, request): return Response({'data': []})更适合复杂逻辑和复用
Response 对象Response(data, status=200)返回 JSON 响应并支持内容协商return Response({'count': 10}, status=201)替代 Django 的 JsonResponse
Request 对象request.data, request.query_params统一获取请求数据(无论 POST/PUT)和查询参数if request.method == 'POST':; print(request.data)比 Django 原生 request 更强大
URL 路由绑定path('hello/', hello_view)将视图函数或类注册到 URLfrom django.urls import path; urlpatterns = [path('hello/', hello)]类视图需加 .as_view()

第二章:序列化器(Serializers)基础

2.1 序列化与反序列化概念

概念说明注意事项
序列化将 Python 对象(如模型实例)转换为 JSON 字符串以便传输用于 API 响应输出
反序列化将 JSON 数据解析为 Python 数据结构,并进行验证用于处理客户端提交的数据
Serializer 类DRF 中用于执行序列化和反序列化的核心类是数据转换的桥梁
is_valid() 方法验证反序列化数据是否合法必须调用才能访问 validated_data
validated_data存储通过验证后的干净数据仅在 is_valid() 返回 True 后可用
数据流方向序列化:数据库 → JSON;反序列化:JSON → 数据库理解方向有助于设计接口

2.2 Serializer 类的基本使用

方法/字段语法用途代码示例注意事项
定义序列化器class UserSerializer(serializers.Serializer):显式声明字段用于数据转换class UserSerializer(serializers.Serializer): name = serializers.CharField(); age = serializers.IntegerField()字段需与数据结构匹配
序列化单个对象serializer = MySerializer(obj)将模型实例转为字典user = User.objects.get(id=1); serializer = UserSerializer(user); print(serializer.data)输出为 OrderedDict
序列化多个对象serializer = MySerializer(queryset, many=True)批量序列化查询集users = User.objects.all(); serializer = UserSerializer(users, many=True)必须设置 many=True
反序列化数据serializer = MySerializer(data=request.data)接收客户端 JSON 并验证serializer = UserSerializer(data=request.data); if serializer.is_valid(): print(serializer.validated_data)必须传 data=
save() 方法serializer.save()保存验证后的数据(需实现 create/update)if serializer.is_valid(): user = serializer.save()需重写 create() 或 update()
create() 方法def create(self, validated_data):定义如何创建新对象def create(self, validated_data): return User.objects.create(**validated_data)反序列化时调用
update() 方法def update(self, instance, validated_data):定义如何更新现有对象def update(self, instance, validated_data): instance.name = validated_data.get('name'); instance.save(); return instanceinstance 为原对象

2.3 ModelSerializer 的定义与自动字段生成

特性语法用途代码示例注意事项
ModelSerializer 类class MyModelSerializer(serializers.ModelSerializer):自动生成字段并提供默认 create/updateclass UserSerializer(serializers.ModelSerializer):; class Meta: model = User; fields = 'all'减少重复代码
Meta 类class Meta:配置模型和字段映射class Meta: model = Book; fields = ['id', 'title', 'author']必须定义
fields 属性fields = ['f1', 'f2']'__all__'指定包含的字段fields = '__all__'(推荐显式列出字段)推荐显式列出字段
exclude 属性exclude = ['field_name']排除某些字段exclude = ['created_at']与 fields 互斥
read_only_fieldsread_only_fields = ['field']设置字段只读(不参与反序列化)read_only_fields = ['id', 'created_at']常用于自增字段
自动字段映射根据模型字段类型生成对应 Serializer 字段减少手动定义CharField → CharField, DateTimeField → DateTimeField大部分类型自动支持
自动 create/update内置保存逻辑直接调用 save() 保存数据serializer = UserSerializer(data=data); if serializer.is_valid(): serializer.save()无需手动实现

2.4 字段级验证方法(validate_)

方法语法用途代码示例注意事项
validate_def validate_<field_name>(self, value):对特定字段进行额外验证def validate_age(self, value):; if value < 0:; raise serializers.ValidationError("年龄不能为负数"); return value方法名必须匹配字段名
抛出 ValidationErrorraise serializers.ValidationError("错误信息")中断验证并返回错误如上例所示必须从 serializers 导入
返回验证后值return value允许修改或原样返回字段值return value.strip() # 去空格必须返回值
执行时机在字段通用验证之后执行确保值类型正确后再做业务逻辑验证-例如:CharField 已确保是字符串
仅对存在字段调用字段存在于 data 中才执行跳过未提交字段的验证-可结合 required=False 使用

2.5 对象级验证方法(validate)

方法语法用途代码示例注意事项
validate 方法def validate(self, attrs):验证多个字段之间的逻辑关系def validate(self, attrs):; if attrs['password'] != attrs['confirm_password']:; raise serializers.ValidationError("两次密码不一致"); return attrs接收所有字段的 validated_data
参数 attrs字典类型,包含所有字段值用于跨字段比较如上例所示键为字段名,值为输入值
返回 attrsreturn attrs必须返回数据供后续使用-可修改字段值后返回
与字段验证顺序在所有 validate_ 之后执行确保每个字段已单独验证通过-避免处理无效数据
可抛出 ValidationErrorraise serializers.ValidationError({...})返回结构化错误信息raise serializers.ValidationError({'start_date': '开始时间不能晚于结束时间'})支持字段级错误定位

2.6 自定义字段与只读/必填字段控制

字段/参数语法用途代码示例注意事项
write_onlywrite_only=True字段仅用于输入(不序列化输出)password = serializers.CharField(write_only=True)常用于密码、验证码
read_onlyread_only=True字段仅用于输出(不参与反序列化)created_at = serializers.DateTimeField(read_only=True)可替代 read_only_fields
requiredrequired=False设置字段非必填email = serializers.EmailField(required=False)默认为 True
defaultdefault='value'设置默认值status = serializers.CharField(default='active')可为函数或常量
allow_nullallow_null=True允许字段值为 Noneage = serializers.IntegerField(allow_null=True)注意与 required 区分
sourcesource='field_name'指定字段对应的数据源属性full_name = serializers.CharField(source='get_full_name')支持方法调用
自定义字段继承 serializers.Field实现特殊序列化逻辑class CustomField(serializers.Field):; def to_representation(self, value): return str(value); def to_internal_value(self, data): return int(data)需实现两个核心方法

第三章:视图层(Views)

3.1 APIView 基类详解

方法/属性语法用途代码示例注意事项
继承 APIViewclass MyView(APIView):创建基于类的 API 视图from rest_framework.views import APIView; class UserView(APIView): def get(self, request): return Response({'users': []})必须从 rest_framework.views 导入
HTTP 方法处理def get(self, request):, def post(self, request):分别处理不同 HTTP 请求def post(self, request): serializer = UserSerializer(data=request.data); if serializer.is_valid(): serializer.save(); return Response(serializer.data, status=201); return Response(serializer.errors, status=400)方法名对应 HTTP 动词小写
permission_classespermission_classes = [IsAuthenticated]设置视图级权限控制class UserView(APIView): permission_classes = [IsAuthenticated]; def get(self, request): ...覆盖全局设置
authentication_classesauthentication_classes = [...]指定认证方式authentication_classes = [TokenAuthentication]可组合多个认证器
throttle_classesthrottle_classes = [UserRateThrottle]添加访问频率限制throttle_classes = [AnonRateThrottle]常用于防止滥用
自定义 dispatchdef dispatch(self, request, *args, **kwargs):拦截请求前后执行逻辑def dispatch(self, request, *args, **kwargs):; print("Request received"); return super().dispatch(request, *args, **kwargs)可用于日志、性能监控

3.2 混合类(Mixins)与通用视图(GenericAPIView)

类/方法语法用途代码示例注意事项
GenericAPIViewclass MyView(GenericAPIView):提供通用属性和方法(queryset, serializer_class)class UserListView(GenericAPIView): queryset = User.objects.all(); serializer_class = UserSerializer; def get(self, request): serializer = self.get_serializer(many=True); return Response(serializer.data)需配合 Mixin 使用
ListModelMixinListModelMixin实现 list() 方法返回列表class UserList(GenericAPIView, ListModelMixin): queryset = User.objects.all(); serializer_class = UserSerializer; def get(self, request): return self.list(request)自动生成分页响应
CreateModelMixinCreateModelMixin实现 create() 方法处理创建def post(self, request): return self.create(request)自动调用 save()
RetrieveModelMixinRetrieveModelMixin实现 retrieve() 返回单个对象def get(self, request, pk): return self.retrieve(request, pk)需提供 pk
UpdateModelMixinUpdateModelMixin实现 update() 和 partial_update()def put(self, request, pk): return self.update(request, pk); def patch(self, request, pk): return self.partial_update(request, pk)支持全量/部分更新
DestroyModelMixinDestroyModelMixin实现 destroy() 删除对象def delete(self, request, pk): return self.destroy(request, pk)默认返回 204
get_serializer()self.get_serializer()获取序列化器实例serializer = self.get_serializer(data=request.data)自动传入 context

3.3 通用视图集(GenericViewSet)与路由映射

类/方法语法用途代码示例注意事项
GenericViewSetclass MyViewSet(GenericAPIView, ViewSetMixin):结合 GenericAPIView 与 ViewSet 行为from rest_framework.viewsets import GenericViewSet; class UserViewSet(GenericViewSet, ListModelMixin, RetrieveModelMixin): queryset = User.objects.all(); serializer_class = UserSerializer必须混入 Mixin 实现功能
action 装饰器@action(methods=['get'], detail=True)添加自定义动作from rest_framework.decorators import action; @action(methods=['get'], detail=False); def recent(self, request): users = User.objects.order_by('-date_joined')[:5]; serializer = self.get_serializer(users, many=True); return Response(serializer.data)detail=True 表示操作单个对象
detail 参数detail=True/False控制 URL 是否包含 pk@action(detail=False, methods=['get']); def stats(self, request): ...False 用于集合级操作
methods 参数methods=['get', 'post']指定允许的 HTTP 方法@action(methods=['post'], detail=True); def activate(self, request, pk): ...默认为 [‘get’]
url_pathurl_path='custom-path'自定义路由路径@action(url_path='activate', detail=True); def activate_user(self, request, pk): ...URL 变为 /users/1/activate/
url_nameurl_name='activate'设置反向查找名@action(url_name='activate', ...)用于 reverse 解析

3.4 ModelViewSet 快速构建 CRUD 接口

方法语法用途代码示例注意事项
ModelViewSetclass MyViewSet(viewsets.ModelViewSet):快速实现完整的 CRUD 操作from rest_framework import viewsets; class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all(); serializer_class = UserSerializer自动生成 list, create, retrieve, update, destroy
默认方法.list(), .create(), .retrieve(), .update(), .destroy()覆盖即可自定义行为def create(self, request): response = super().create(request); # 发送欢迎邮件; return response可在原有逻辑前后扩展
querysetqueryset = Model.objects.all()定义数据源queryset = User.objects.filter(is_active=True)推荐过滤敏感数据
serializer_classserializer_class = MySerializer指定默认序列化器serializer_class = UserDetailSerializer可动态切换
lookup_fieldlookup_field = 'slug'更改查找字段(默认 ‘pk’)lookup_field = 'username'需确保唯一性
http_method_nameshttp_method_names = ['get', 'post']限制允许的 HTTP 方法http_method_names = ['get', 'head']禁用不安全操作

3.5 ViewSet 与 URL 路由绑定(SimpleRouter / DefaultRouter)

类/方法语法用途代码示例注意事项
SimpleRouterrouter = SimpleRouter()自动生成标准 REST 路由from rest_framework.routers import SimpleRouter; router = SimpleRouter(); router.register('users', UserViewSet); urlpatterns = router.urls不包含额外 HTML 页面
DefaultRouterrouter = DefaultRouter()同 SimpleRouter,但包含根视图from rest_framework.routers import DefaultRouter; router = DefaultRouter(); router.register('users', UserViewSet)推荐开发使用,提供导航页
register() 方法router.register(prefix, viewset)注册 ViewSet 到路由router.register('books', BookViewSet, basename='book')basename 用于反向解析
basename 参数basename='model-name'指定 URL 名称前缀basename='user' → url name: ‘user-list’, ‘user-detail’当 queryset 为空时必须指定
include 路由urlpatterns += router.urls将路由注入主 URL 配置from django.urls import include; urlpatterns = [path('api/', include(router.urls))]可加前缀组织 API 版本
自定义路由继承 BaseRouter实现非标准路由逻辑-高级用法,一般无需自定义

第四章:请求与响应处理

4.1 Request 对象封装与属性(data, query_params 等)

属性语法用途代码示例注意事项
request.datarequest.data获取请求体数据(兼容 POST/PUT/PATCH)if request.data.get('name'):; print(request.data['name'])替代 request.POST 和 request.body 解析
request.query_paramsrequest.query_params获取 URL 查询参数(类似 GET)page = request.query_params.get('page', 1)替代 request.GET
request.userrequest.user获取当前认证用户if request.user.is_authenticated:; print(request.user.username)未登录为 AnonymousUser
request.authrequest.auth获取认证凭据(如 Token)print(request.auth.key)仅在 TokenAuthentication 下有效
request.methodrequest.method获取 HTTP 方法(大写字符串)if request.method == 'POST': ...值为 ‘GET’, ‘POST’ 等
request.content_typerequest.content_type获取请求内容类型print(request.content_type)如 ‘application/json’
request.streamrequest.stream低级访问原始请求体raw_body = request.stream.read()一般不推荐直接使用

4.2 Response 对象构造与状态码控制

参数/方法语法用途代码示例注意事项
Response 类Response(data, status=200)返回 API 响应return Response({'ok': True}, status=201)自动进行内容协商
data 参数data={...}响应数据(字典、列表等)return Response({'count': 10})必须可 JSON 序列化
status 参数status=status.HTTP_201_CREATED设置 HTTP 状态码from rest_framework import status; return Response(data, status=status.HTTP_400_BAD_REQUEST)使用常量更清晰
template_nametemplate_name='api.html'指定可浏览 API 模板return Response(data, template_name='custom.html')仅用于调试页面
headers 参数headers={'Location': '/new-url'}添加响应头return Response(data, headers={'X-Process-Time': '100ms'})常用于重定向、自定义头
content_typecontent_type='application/xml'强制指定内容类型return Response(xml_data, content_type='application/xml')覆盖自动协商结果

4.3 内容协商(Content Negotiation)机制

概念/类说明代码示例注意事项
内容协商根据客户端请求头选择响应格式Accept: application/json / Accept: text/htmlDRF 自动判断返回 JSON 或 HTML 页面
DefaultContentNegotiation默认协商类可在 REST_FRAMEWORK 设置中替换-
renderers 设置DEFAULT_RENDERER_CLASSES 配置支持的渲染格式REST_FRAMEWORK = {'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer']}-
JSONRenderer返回纯 JSON-适合生产环境
BrowsableAPIRenderer返回可交互 HTML 页面-仅用于开发调试
format 参数?format=json 强制指定响应格式/users/?format=json-
自定义 Renderer继承 BaseRenderer 支持 XML、CSV 等格式class CSVRenderer(BaseRenderer): media_type = 'text/csv'; format = 'csv'; def render(self, data, media_type=None, renderer_context=None): ...-

第五章:认证(Authentication)

5.1 认证机制概述(SessionAuthentication, TokenAuthentication 等)

认证类语法用途代码示例注意事项
SessionAuthenticationauthentication_classes = [SessionAuthentication]基于 Django 会话的认证,适合浏览器客户端REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication']}需配合 CSRF 保护,不推荐用于纯 API
TokenAuthenticationTokenAuthentication使用静态 Token 进行认证,适合移动客户端from rest_framework.authentication import TokenAuthentication; class MyView(APIView): authentication_classes = [TokenAuthentication]每个用户一个 Token,需手动管理
BasicAuthenticationBasicAuthentication使用 HTTP Basic Auth(用户名:密码 Base64 编码)'rest_framework.authentication.BasicAuthentication'不安全,仅用于测试或 HTTPS 环境
JWTAuthenticationJWTAuthentication使用 JSON Web Token 实现无状态认证需安装 djangorestframework-simplejwt支持刷新 Token,适合前后端分离
RemoteUserAuthenticationRemoteUserAuthentication由前端代理(如 Nginx)完成认证适用于 SSO 或企业级认证系统需配置 Web 服务器
认证执行顺序按列表顺序依次尝试直到某个认证成功或全部失败authentication_classes = [TokenAuthentication, SessionAuthentication]返回 (user, auth) 表示成功,None 继续下个

5.2 配置全局与局部认证类

配置方式语法用途代码示例注意事项
全局配置REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES']设置所有视图默认认证方式REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.TokenAuthentication']}项目级统一控制
局部配置(视图类)authentication_classes = [...]覆盖全局设置,仅作用于当前视图class PublicView(APIView): authentication_classes = []; def get(self, request): return Response({'msg': '无需认证'})优先级高于全局
局部配置(函数视图)@authentication_classes([TokenAuthentication])为函数视图设置认证@api_view(['GET']); @authentication_classes([TokenAuthentication]); def profile(request): ...需导入 authentication_classes 装饰器
禁用认证authentication_classes = []明确表示该视图无需认证authentication_classes = []与 AllowAny 权限配合使用
多认证组合[TokenAuth, SessionAuth]支持多种客户端接入authentication_classes = [TokenAuthentication, SessionAuthentication]按顺序尝试,任一成功即通过
settings.py 位置在 Django 配置文件中集中管理 DRF 行为# settings.py; REST_FRAMEWORK = { ... }修改后需重启服务

5.3 自定义认证类实现

方法/属性语法用途代码示例注意事项
继承 BaseAuthenticationclass MyAuth(BaseAuthentication):创建自定义认证类from rest_framework.authentication import BaseAuthentication; class CustomTokenAuthentication(BaseAuthentication): ...必须实现 authenticate()
authenticate() 方法def authenticate(self, request):核心认证逻辑def authenticate(self, request):; token = request.META.get('HTTP_X_API_KEY'); if not token: return None; try: user = User.objects.get(api_key=token); return (user, token); except User.DoesNotExist: raise AuthenticationFailed('无效 API Key')返回 (user, auth) 成功,None 继续,抛异常拒绝
抛出 AuthenticationFailedfrom rest_framework.exceptions import AuthenticationFailed中断认证并返回 401raise AuthenticationFailed('登录失效')客户端应重新登录
authenticate_header()def authenticate_header(self, request):返回 WWW-Authenticate 头def authenticate_header(self, request): return 'API-Key'影响 401 响应头,用于提示客户端
request 参数request获取请求头、数据等auth_header = request.META.get('HTTP_AUTHORIZATION')注意 META 中的键为大写
返回值说明(user, auth)None(user, auth) 表示成功,None 表示不处理(继续下一个认证器)return (user, 'token-str')若所有认证器返回 None,用户为 AnonymousUser

第六章:权限控制(Permissions)

6.1 权限系统工作原理

概念说明注意事项
执行时机在认证之后、视图执行之前确保 request.user 已确定
权限检查流程依次检查每个权限类的 has_permission() 或 has_object_permission()所有权限必须通过
has_permission()针对整个视图的权限(如能否访问列表)用于 GET /items/ 这类集合操作
has_object_permission()针对具体对象的权限(如能否编辑某条数据)在 get_object() 时调用,用于 PUT /items/1/
权限组合使用元组或列表配置多个权限permission_classes = [IsAuthenticated, IsOwner]
短路机制任一权限返回 False,立即拒绝并返回 403不会继续检查后续权限
上下文参数传入 request, view, obj(可选)可根据请求方法、用户、对象状态判断
与认证关系认证解决”你是谁”,权限解决”你能做什么”通常先认证后授权

6.2 常用权限类(IsAuthenticated, IsAdminUser, AllowAny 等)

权限类语法用途代码示例注意事项
AllowAnypermission_classes = [AllowAny]允许所有用户访问class PublicView(APIView): permission_classes = [AllowAny]; def get(self, request): ...默认权限,无需显式设置
IsAuthenticatedIsAuthenticated仅允许已登录用户permission_classes = [IsAuthenticated]最常用,防止未登录访问
IsAdminUserIsAdminUser仅允许 is_staff=True 的用户permission_classes = [IsAdminUser]适合后台管理接口
IsAuthenticatedOrReadOnlyIsAuthenticatedOrReadOnly已登录可读写,否则仅可读permission_classes = [IsAuthenticatedOrReadOnly]适合公开内容(如博客)
DjangoModelPermissionsDjangoModelPermissions基于 Django 权限系统(add, change, delete)permission_classes = [DjangoModelPermissions]需为用户分配具体模型权限
DjangoObjectPermissionsDjangoObjectPermissions基于行级权限(django-guardian)需配合 ObjectPermission 使用实现细粒度数据访问控制
全局默认权限DEFAULT_PERMISSION_CLASSES设置项目默认权限REST_FRAMEWORK = {'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated']}推荐设为 IsAuthenticated 提升安全

6.3 自定义权限类开发

方法/类语法用途代码示例注意事项
继承 BasePermissionclass MyPerm(BasePermission):创建自定义权限from rest_framework.permissions import BasePermission; class IsOwner(BasePermission): ...必须实现 has_permission 或 has_object_permission
has_permission()def has_permission(self, request, view):集合级权限判断def has_permission(self, request, view): return request.user.is_authenticated可用于限制 HTTP 方法
has_object_permission()def has_object_permission(self, request, view, obj):对象级权限判断def has_object_permission(self, request, view, obj): return obj.owner == request.user在 get_object() 时调用
结合 request.methodif request.method in SAFE_METHODS:区分读写操作if request.method in ['GET', 'HEAD', 'OPTIONS']: return TrueSAFE_METHODS = (‘GET’, ‘HEAD’, ‘OPTIONS’)
返回布尔值return True/False允许或拒绝访问return obj.team.members.filter(id=request.user.id).exists()不可抛异常,否则返回 500
应用到视图permission_classes = [IsOwner]将自定义权限加入配置class TaskDetailView(APIView): permission_classes = [IsOwner]; ...可与其他权限组合使用
错误信息控制self.message = '自定义提示'设置拒绝时的错误消息class IsOwner(BasePermission): message = '你不是该资源的所有者'; def has_object_permission(...): ...在异常响应中返回

第七章:限流控制(Throttling)

7.1 限流机制作用与场景

概念说明注意事项
限流(Throttling)限制客户端在单位时间内可发起的请求数量防止滥用、DDoS 攻击、保护服务器资源
作用时机在权限检查之后、视图执行之前确保认证和权限通过后再进行频率控制
典型应用场景API 接口防刷、公共接口保护、免费用户配额限制如登录接口、注册接口、公开数据查询
匿名用户限流防止未登录用户高频请求常用于防止爬虫或暴力破解
认证用户限流控制登录用户的调用频率可按用户级别设置不同配额(如 VIP 用户更高限额)
返回状态码超过限制时返回 429 Too Many Requests客户端应根据 Retry-After 头等待重试
与缓存依赖限流状态通常存储在缓存中(如 Redis)需配置 cache 后端以支持分布式环境

7.2 内置限流类(AnonRateThrottle, UserRateThrottle)

限流类语法用途代码示例注意事项
AnonRateThrottlethrottle_classes = [AnonRateThrottle]限制匿名用户(未登录)的请求频率REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES': {'anon': '10/minute'}}依据 IP 地址识别用户
UserRateThrottleUserRateThrottle限制认证用户的请求频率'DEFAULT_THROTTLE_RATES': {'user': '100/hour'}依据 request.user 识别,更精准
ScopedRateThrottleclass MyThrottle(ScopedRateThrottle):按视图作用域(scope)进行限流'DEFAULT_THROTTLE_RATES': {'login': '5/hour'}; class LoginView(APIView): throttle_scope = 'login'适用于特定高风险接口
throttle_rates 配置'scope_name': '次数/时间单位'定义不同作用域的速率支持 second, minute, hour, day格式为 “5/minute”,不可空格
多限流组合[AnonRateThrottle, UserRateThrottle]同时应用多种限流策略throttle_classes = [AnonRateThrottle, UserRateThrottle]所有限流器都必须通过
响应头字段X-Throttle-Count, X-Throttle-Remaining, Retry-After提供限流状态信息客户端可据此调整请求行为Retry-After 在被限流时返回

7.3 自定义限流策略

方法/属性语法用途代码示例注意事项
继承 BaseThrottleclass MyThrottle(BaseThrottle):创建自定义限流类from rest_framework.throttling import BaseThrottle; class RateAveragerThrottle(BaseThrottle): ...必须实现 allow_request()
allow_request()def allow_request(self, request, view):核心判断逻辑:是否允许本次请求def allow_request(self, request, view): return random.randint(1, 10) > 3返回 True 允许,False 拒绝
wait() 方法def wait(self):返回还需等待的秒数def wait(self): import time; return 60 - (time.time() % 60)用于 Retry-After 头
缓存存储from django.core.cache import cache存储请求记录(如 IP + 时间戳)key = f"throttle_{request.META['REMOTE_ADDR']}"; requests = cache.get(key, []); requests = [r for r in requests if r > time.time() - 60]; if len(requests) >= 5: return False; requests.append(time.time()); cache.set(key, requests, 60)推荐使用 Redis
按用户角色限流结合 request.user 判断不同角色不同配额if request.user.is_premium: rate = 100; else: rate = 10需权限系统支持
作用域自定义throttle_scope = 'custom'与 ScopedRateThrottle 配合class UploadView(APIView): throttle_scope = 'upload'; throttle_classes = [ScopedRateThrottle]需在 DEFAULT_THROTTLE_RATES 中定义

第八章:过滤、搜索与排序

8.1 使用 DjangoFilterBackend 实现字段过滤

属性/类语法用途代码示例注意事项
DjangoFilterBackendfrom django_filters.rest_framework import DjangoFilterBackend启用字段级精确过滤class ProductListView(ListAPIView): queryset = Product.objects.all(); serializer_class = ProductSerializer; filter_backends = [DjangoFilterBackend]; filterset_fields = ['category', 'in_stock']需安装 django-filter
filterset_fieldsfilterset_fields = ['field1', 'field2']声明可过滤的字段filterset_fields = ['status', 'author__id']支持外键关联字段
查询语法?field=value客户端传参进行过滤/api/products/?category=electronics&in_stock=True多值用 , 分隔(需配置)
精确匹配默认行为等值查询?status=active → status=‘active’不支持模糊匹配
关联字段过滤foreign__field跨表过滤filterset_fields = ['author__department']路径需存在且可访问
安装依赖pip install django-filter启用过滤功能INSTALLED_APPS += ['django_filters']必须添加到 INSTALLED_APPS

8.2 SearchFilter 实现关键词搜索

属性/类语法用途代码示例注意事项
SearchFilterfrom rest_framework.filters import SearchFilter启用全文关键词搜索filter_backends = [SearchFilter]; search_fields = ['title', 'description']基于数据库 LIKE 查询
search_fieldssearch_fields = ['field1', ...]定义参与搜索的字段search_fields = ['^title', '=code', '@description']支持前缀、精确、全文索引
搜索类型前缀^, =, @, $控制匹配方式'^subject' → 以关键词开头;'=name' → 精确匹配;'@description' → 全文搜索(MySQL);'$content' → 正则匹配不同数据库支持不同
查询参数?search=关键词客户端传入搜索词/api/articles/?search=python+web多词默认为 OR 关系
多字段 OR 查询所有 search_fields 字段使用 OR 连接-搜索效率随字段增多下降
数据库性能LIKE 查询无索引时较慢大数据量建议使用 Elasticsearch可结合数据库全文索引优化

8.3 OrderingFilter 实现结果排序

属性/类语法用途代码示例注意事项
OrderingFilterfrom rest_framework.filters import OrderingFilter允许客户端对结果排序filter_backends = [OrderingFilter]; ordering_fields = ['price', 'created_at']需显式声明可排序字段
ordering_fieldsordering_fields = ['f1', 'f2']定义允许排序的字段ordering_fields = ['title', 'price', 'user__name']防止枚举所有字段造成信息泄露
ordering 参数?ordering=field?-field升序或降序?ordering=price → 升序;?ordering=-created_at → 降序支持多字段 ?ordering=price,-date
默认排序ordering = ['field']设置默认排序规则ordering = ['-created_at']可被客户端覆盖
关联字段排序foreign__field按外键字段排序ordering_fields = ['author__last_name']需注意性能,避免 N+1 查询
安全性限制 ordering_fields防止敏感字段被排序(如密码、密钥)不要包含 is_staff, api_key 等字段显式列出字段更安全

第九章:分页(Pagination)

9.1 PageNumberPagination 分页实现

属性/方法语法用途代码示例注意事项
PageNumberPagination 类class MyPagination(PageNumberPagination):基于页码的标准分页器from rest_framework.pagination import PageNumberPagination; class StandardResultsSetPagination(PageNumberPagination): page_size = 10; page_size_query_param = 'page_size'; max_page_size = 100最常用的分页方式
page_sizepage_size = 20每页默认数据条数page_size = 20可被 page_size_query_param 覆盖
page_size_query_parampage_size_query_param = 'size'允许客户端通过参数自定义每页数量page_size_query_param = 'page_size'不设置则客户端无法修改
max_page_sizemax_page_size = 100限制客户端请求的最大页大小max_page_size = 50防止 page_size=99999 导致性能问题
page_query_parampage_query_param = 'p'自定义页码参数名(默认 ‘page’)page_query_param = 'page'URL 中使用 ?page=2
get_paginated_response()def get_paginated_response(self, data):自定义分页响应结构def get_paginated_response(self, data): return Response({'links': {'next': self.get_next_link(), 'previous': self.get_previous_link()}, 'count': self.page.paginator.count, 'results': data})用于统一响应格式
应用于视图pagination_class = MyPagination在视图中指定分页类class UserListView(ListAPIView): queryset = User.objects.all(); serializer_class = UserSerializer; pagination_class = StandardResultsSetPagination也可在全局设置
全局配置DEFAULT_PAGINATION_CLASS设置项目级默认分页REST_FRAMEWORK = {'DEFAULT_PAGINATION_CLASS': 'myapp.pagination.StandardResultsSetPagination', 'PAGE_SIZE': 10}推荐用于统一风格

9.2 LimitOffsetPagination 分页模式

属性/方法语法用途代码示例注意事项
LimitOffsetPagination 类class MyLimitOffsetPagination(LimitOffsetPagination):基于偏移量的分页(类似 SQL LIMIT/OFFSET)from rest_framework.pagination import LimitOffsetPagination; class CustomLimitOffsetPagination(LimitOffsetPagination): default_limit = 20; limit_query_param = 'limit'; offset_query_param = 'offset'; max_limit = 100适合无限滚动、APP 分页
default_limitdefault_limit = 20默认每页数量(即 LIMIT)default_limit = 10客户端可覆盖
limit_query_paramlimit_query_param = 'per_page'自定义 limit 参数名limit_query_param = 'limit'对应 URL 中 ?limit=20
offset_query_paramoffset_query_param = 'start'自定义 offset 参数名offset_query_param = 'offset'对应 ?offset=40 表示跳过前40条
max_limitmax_limit = 100限制最大返回数量max_limit = 50防止一次性拉取过多数据
get_paginated_response()同 PageNumberPagination可重写以自定义响应结构def get_paginated_response(self, data): return Response({'count': self.count, 'next': self.get_next_link(), 'previous': self.get_previous_link(), 'results': data})保持 API 一致性
适用场景无页码概念的接口如 APP 列表加载更多/api/items/?limit=10&offset=20适合大数据集,但深分页性能差
性能注意OFFSET 越大查询越慢数据库需扫描前 N 行OFFSET 100000 时性能显著下降超大数据集建议使用游标分页(CursorPagination)

9.3 自定义分页样式与响应结构

方法/属性语法用途代码示例注意事项
继承 BasePaginationclass CustomPagination(BasePagination):创建完全自定义分页器from rest_framework.pagination import BasePagination; class SimplePagination(BasePagination): ...需手动实现分页逻辑
paginate_queryset()def paginate_queryset(self, queryset, request, view=None):执行分页并返回当前页数据def paginate_queryset(self, queryset, request, view=None): self.page_size = self.get_page_size(request); paginator = Paginator(queryset, self.page_size); page_number = request.query_params.get('p', 1); self.page = paginator.get_page(page_number); return self.page.object_list必须实现
get_paginated_response()def get_paginated_response(self, data):定义返回的 JSON 结构def get_paginated_response(self, data): return Response({'data': data, 'meta': {'total': self.page.paginator.count, 'current_page': self.page.number, 'last_page': self.page.paginator.num_pages, 'per_page': self.page_size}})推荐统一分页响应格式
get_page_size()def get_page_size(self, request):动态计算页大小可根据用户角色返回不同 page_size用于差异化服务
使用 DRF 内置类继承推荐继承 PageNumberPagination在现有功能上扩展class MyPagination(PageNumberPagination): def get_paginated_response(self, data): ...更稳定,减少出错
添加额外元数据在响应中加入统计信息如总页数、是否有下一页'meta': {'has_next': self.page.has_next(), 'has_previous': self.page.has_previous()}提升客户端体验
禁用分页pagination_class = None特定视图关闭分页class ExportView(ListAPIView): pagination_class = None; ...用于导出全量数据

第十章:异常处理与自定义错误响应

10.1 DRF 异常体系(APIException 及子类)

异常类说明用途代码示例注意事项
APIException基类异常所有 DRF 自定义异常的父类class CustomError(APIException):可继承创建自定义错误
status_codestatus_code = 400HTTP 状态码status_code = 429必须为有效 HTTP 状态码
default_detaildefault_detail = '错误信息'默认错误描述default_detail = '请求频率超限'客户端可见
default_codedefault_code = 'invalid'错误码标识符default_code = 'rate_limit_exceeded'用于程序判断
ParseError请求数据解析失败如 JSON 格式错误raise ParseError('Invalid JSON')自动捕获 request.data 解析异常
AuthenticationFailed认证失败Token 过期、无效凭证raise AuthenticationFailed('Token expired')返回 401
NotAuthenticated未提供认证信息未登录访问受保护接口raise NotAuthenticated()通常由认证类抛出
PermissionDenied权限不足用户无权访问资源raise PermissionDenied('No permission')返回 403
NotFound资源不存在对象查询失败(get_object)raise NotFound('User not found')返回 404
MethodNotAllowed方法不被允许如对只读视图发 PUT 请求raise MethodNotAllowed(request.method)返回 405
NotAcceptable内容协商失败客户端 Accept 头无法满足raise NotAcceptable()返回 406
Throttled请求频率超限超过限流阈值raise Throttled(wait=60)返回 429,可带 wait 秒数
ValidationError数据验证失败序列化器 .is_valid() 失败raise ValidationError({'name': 'This field is required.'})可返回字段级错误

10.2 全局异常处理器 custom_exception_handler

属性/方法语法用途代码示例注意事项
custom_exception_handlerdef custom_exception_handler(exc, context):全局捕获并格式化异常响应from rest_framework.views import exception_handler; def custom_exception_handler(exc, context): response = exception_handler(exc, context); if response is not None: response.data['status_code'] = response.status_code; return response必须返回 Response 对象
exc 参数exc当前抛出的异常实例if isinstance(exc, ValidationError): ...可判断异常类型进行特殊处理
context 参数context包含 request、view 等上下文view = context['view']; request = context['request']用于日志记录或动态响应
调用默认处理器response = exception_handler(exc, context)复用 DRF 默认处理逻辑if response: response.data['success'] = False; return response推荐先调用默认逻辑
返回自定义响应return Response(data, status=code)直接返回新响应(如500错误)if isinstance(exc, Exception): return Response({'error': 'Internal server error', 'detail': str(exc)}, status=500)生产环境避免暴露详细错误
配置位置REST_FRAMEWORK['EXCEPTION_HANDLER']注册全局处理器REST_FRAMEWORK = {'EXCEPTION_HANDLER': 'myapp.exceptions.custom_exception_handler'}只能设置一个全局处理器
日志记录import logging记录异常便于排查logger.error(f"API Error: {exc}")推荐在处理器中添加日志

10.3 返回格式统一化设计

设计要素说明推荐格式示例注意事项
成功响应结构统一成功返回格式{ "success": true, "data": { ... }, "message": "OK" }避免直接返回裸数据
错误响应结构统一错误返回格式{ "success": false, "error": "Invalid input", "code": "validation_error", "details": { "email": "Invalid format" } }包含可读错误和机器码
data 字段存放业务数据"data": { "id": 1, "name": "John" }列表数据也应包裹在 data 中
message 字段用户可读提示"message": "用户创建成功"可选,用于前端提示
error 字段错误描述"error": "User not found"仅在失败时存在
code 字段错误码(用于程序判断)"code": "user_not_found"避免使用 HTTP 状态码作为业务码
details 字段详细错误信息(如字段级验证)"details": { "password": "Too short" }适合表单验证场景
状态标识success 或 ok"success": true/false比 status 更清晰
分页响应整合将分页信息与数据合并{ "success": true, "data": [...], "pagination": { "total": 100, "page": 1, "pages": 10 } }保持一致性
避免裸数组不直接返回 […]应包裹在 data 字段中防止 JSON Hijacking,提升可扩展性
版本兼容保持字段稳定新增字段可选,避免删除或重命名有利于客户端兼容

第十一章:信号与事件驱动

11.1 利用 Django 信号解耦业务逻辑

信号类型语法/说明用途代码示例注意事项
post_save@receiver(post_save, sender=MyModel)模型保存后触发(创建或更新)from django.db.models.signals import post_save; from django.dispatch import receiver; @receiver(post_save, sender=User); def create_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(user=instance)常用于关联对象创建
pre_save@receiver(pre_save, sender=...)模型保存前触发def validate_slug(sender, instance, **kwargs): if not instance.slug: instance.slug = slugify(instance.title)可修改 instance 属性
post_delete@receiver(post_delete, sender=...)模型删除后触发@receiver(post_delete, sender=Article); def cleanup_files(sender, instance, **kwargs): if instance.image: instance.image.delete()清理文件、缓存等资源
m2m_changed@receiver(m2m_changed, sender=Model.relation.through)多对多关系变更时触发@receiver(m2m_changed, sender=Article.tags.through); def update_tag_count(sender, instance, action, **kwargs): if action == 'post_add': instance.update_tag_stats()action 可为 pre_add, post_remove 等
pre_delete@receiver(pre_delete, sender=...)删除前执行清理或备份@receiver(pre_delete, sender=Post); def backup_post(sender, instance, **kwargs): Backup.objects.create(content=instance.content)防止数据丢失
自定义信号my_signal = Signal(providing_args=["user", "action"])实现模块间松耦合from django.dispatch import Signal; user_logged_in = Signal(); # 触发:user_logged_in.send(sender=User, user=user)避免过度使用导致流程混乱

11.2 在序列化或视图中触发信号

方法/位置语法/说明用途代码示例注意事项
视图中手动发送信号my_signal.send(sender=Model, **kwargs)显式触发业务事件class CreateOrderView(APIView): def post(self, request): order = Order.objects.create(...); order_created.send(sender=Order, order=order); return Response(...)推荐用于关键业务节点
序列化器 .save() 后serializer.save() → 触发 post_save利用模型信号自动响应class OrderSerializer(serializers.ModelSerializer): def create(self, validated_data): order = super().create(validated_data); # 自动触发 post_save; return order不需在序列化器内写业务逻辑
信号处理器中调用服务在 receiver 函数中调用 service 模块解耦核心逻辑与副作用@receiver(order_created); def send_confirmation_email(**kwargs): from .services import send_order_email; send_order_email(kwargs['order'])避免阻塞主流程,可异步处理
使用 Celery 异步处理结合 @shared_task提升响应速度,避免超时@receiver(order_created); def async_process(sender, order, **kwargs): process_order.delay(order.id)推荐用于耗时操作(如邮件、推送)
避免在信号中修改 instance尤其是 post_save防止无限递归❌ 错误:在 post_save 中再次 instance.save()如需保存,检查条件或使用 update_fields
单元测试信号逻辑使用 @patch 或禁用信号确保测试独立性with mock.patch('myapp.signals.update_cache'): response = client.post(url, data)测试视图时可临时断开信号

第十二章:API 文档生成

12.1 使用 CoreAPI 或 OpenAPI(drf-spectacular)生成文档

工具/类语法/配置用途代码示例注意事项
drf-spectacularpip install drf-spectacular生成现代 OpenAPI 3.0 文档INSTALLED_APPS += ['drf_spectacular']; REST_FRAMEWORK = {'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema'}; SPECTACULAR_SETTINGS = {'TITLE': 'My API', 'DESCRIPTION': 'Awesome API docs', 'VERSION': '1.0.0'}推荐替代旧版 CoreAPI
Schema 配置入口path('schema/', SpectacularAPIView.as_view(), name='schema')生成 JSON Schemafrom drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView; urlpatterns = [path('api/schema/', SpectacularAPIView.as_view(), name='schema'), path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui')]必须添加路由
Swagger UISpectacularSwaggerView图形化 API 文档界面访问 /api/schema/swagger-ui/ 查看交互式文档支持在线测试接口
RedocSpectacularRedocView更美观的文档展示path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc')适合对外发布
标记废弃接口@extend_schema(deprecated=True)标识过期 API@extend_schema(deprecated=True); def old_endpoint(...): ...提醒客户端迁移
自定义请求体/响应@extend_schema(request=..., responses=...)精确描述复杂结构@extend_schema(request=OrderCreateSerializer, responses={201: OrderDetailSerializer})用于非标准返回格式

12.2 视图文档注释与参数描述

方法/属性语法/说明用途代码示例注意事项
docstring"""三引号注释"""自动生成接口摘要和描述def list(self, request):; """获取用户列表; 支持分页和搜索"""; ...被 drf-spectacular 自动提取
@extend_schema 装饰器@extend_schema(description="...", summary="...")手动覆盖或补充文档@extend_schema(description="批量删除文章,需管理员权限", summary="删除多篇文章"); def destroy(self, request): ...最灵活的控制方式
查询参数描述@extend_schema(parameters=[...])描述 filter/search 参数from drf_spectacular.utils import extend_schema, OpenApiParameter; @extend_schema(parameters=[OpenApiParameter(name='category', type=str, location=OpenApiParameter.QUERY), OpenApiParameter(name='active', type=bool, location=OpenApiParameter.QUERY)])提高文档清晰度
请求头参数OpenApiParameter(location='header')描述自定义 Header(如 API Key)OpenApiParameter(name='X-API-Version', type=str, location=OpenApiParameter.HEADER)用于版本控制或认证
响应状态码说明responses={400: ..., 403: ...}补充错误响应结构@extend_schema(responses={200: UserListSerializer(many=True), 403: OpenApiResponse(description='Permission denied')})增强客户端容错能力
文件上传文档request=OpenApiRequest(...)描述 multipart/form-data 接口@extend_schema(request=OpenApiTypes.BINARY, responses={201: ImageSerializer}); def upload_image(...): ...自动生成文件上传表单

第十三章:性能优化与最佳实践

方法语法用途代码示例注意事项
select_related()queryset.select_related('foreign_key')一对一或外键关联,生成 SQL JOINarticles = Article.objects.select_related('author', 'category').all()减少 N+1 查询,适用于单值关系
prefetch_related()queryset.prefetch_related('many_to_many')多对多或反向外键,执行额外查询并缓存articles = Article.objects.prefetch_related('tags', 'comments__user').all()适用于集合关系,支持嵌套
only().only('field1', 'field2')仅加载指定字段User.objects.only('id', 'name')减少数据传输量
defer().defer('large_field')延迟加载大字段(如 text、file)Post.objects.defer('content')适用于详情页拆分
values() / values_list().values('name', 'email')返回字典或元组,跳过模型实例化User.objects.values('name', 'email')用于只读场景,性能更高
count() 优化使用 .exists() 替代 .count() > 0判断是否存在if Article.objects.filter(status='draft').exists(): ...EXISTS 比 COUNT 更快
数据库索引db_index=TrueMeta.indexes加速 WHERE、ORDER BY 查询class Article(models.Model): status = models.CharField(..., db_index=True)对高频查询字段建立索引

13.2 缓存策略集成(Redis + CacheKey)

方法/工具语法/配置用途代码示例注意事项
Redis 作为缓存后端CACHES 配置分布式缓存存储CACHES = {'default': {'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1'}}需安装 django-redis
cache.set() / get()cache.set(key, value, timeout)手动缓存数据from django.core.cache import cache; data = cache.get('homepage_data'); if not data: data = expensive_query(); cache.set('homepage_data', data, 60*15) # 15分钟设置合理过期时间
缓存键设计使用前缀 + 主键 + 版本避免冲突,支持失效key = f"user_profile_{user_id}_v2"加版本号便于批量失效
@cache_page 装饰器@cache_page(60 * 15)缓存整个视图响应from django.views.decorators.cache import cache_page; @api_view(['GET']); @cache_page(60*15); def homepage(request): ...适用于静态内容
@method_decorator 用于类视图@method_decorator(cache_page(...), name='dispatch')为 CBV 添加缓存class ArticleListView(ListAPIView): @method_decorator(cache_page(60*10)); def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs)注意作用范围
缓存失效策略cache.delete(key)cache.clear()更新数据后清除缓存def update_article(article): article.save(); cache.delete(f'article_{article.id}')保持数据一致性
用户个性化内容缓存结合 request.user 生成键缓存登录用户数据key = f"dashboard_{request.user.id}"不要缓存跨用户敏感数据

13.3 序列化器嵌套优化与懒加载问题

问题/方案说明代码示例注意事项
嵌套序列化器导致 N+1 查询默认未优化关联查询class ArticleSerializer(serializers.ModelSerializer): author = UserSerializer(); tags = TagSerializer(many=True); class Meta: model = Article; fields = '__all__'每个 article 都会单独查 author 和 tags
使用 prefetch_related在视图层预加载关联数据def get_queryset(self): return Article.objects.prefetch_related('author', 'tags')必须在 get_queryset() 中设置
使用 select_related优化外键字段Article.objects.select_related('author')仅适用于 ForeignKey 和 OneToOne
自定义 to_representation()控制输出结构,避免无谓查询def to_representation(self, instance): rep = super().to_representation(instance); rep['author_name'] = instance.author.name; return rep可减少序列化器嵌套
懒加载(Lazy Loading)风险访问未预加载的关联属性触发查询for article in articles: print(article.author.name) # 每次都查数据库循环中极易造成性能瓶颈
使用 .only() 和 .prefetch_related() 组合最小化查询开销Article.objects.select_related('author').only('title', 'author__name').prefetch_related('tags')精确控制字段和关系
缓存序列化结果对频繁访问的静态数据serialized = cache.get(key); if not serialized: serialized = ArticleSerializer(articles, many=True).data; cache.set(key, serialized, 300)适用于低频更新的数据

第十四章:项目实战:构建博客 API

14.1 需求分析与模型设计

模型/功能字段设计说明注意事项
UserDjango 内置 auth.User 或 AbstractUser用户基础信息推荐扩展自定义字段
Blogname, description, owner(ForeignKey)博客站点(可选)支持多博客场景
Categoryname, slug文章分类可选功能
Tagname, slug标签系统多对多关联文章
Articletitle, content, author(User), category, status(choices), published_at, created_at, updated_at核心文章模型status 可为 draft/published
Commentarticle, user, content, parent, created_at评论及回复支持树形结构
关系设计Article.tags = ManyToManyField(Tag)文章与标签多对多使用中间表可扩展
权限需求作者可管理自己文章,管理员可管理所有,访客只读RBAC 基础在权限类中实现
RESTful 设计/api/articles/, /api/comments/遵循 REST 规范使用标准 HTTP 方法

14.2 用户认证与文章管理接口开发

接口URL方法功能示例代码(关键部分)
用户注册/api/auth/register/POST创建用户class RegisterView(APIView): … 使用 UserSerializer
用户登录/api/auth/login/POST返回 Token 或 Session使用 TokenObtainPairView(JWT)
获取文章列表/api/articles/GET分页显示文章class ArticleListCreateAPIView(ListCreateAPIView): …
创建文章/api/articles/POST作者创建新文章验证 request.user 是否有权
查看文章详情/api/articles//GET显示单篇文章及作者信息使用 RetrieveAPIView
更新文章/api/articles//PUT/PATCH作者编辑文章UpdateAPIView,检查所有权
删除文章/api/articles//DELETE作者或管理员删除DestroyAPIView
JWT 认证配置settings.py-启用 JWT安装 djangorestframework-simplejwt 并配置 DEFAULT_AUTHENTICATION_CLASSES

14.3 权限划分(作者、管理员、访客)

角色可访问接口权限规则实现方式
访客(Anonymous)GET /articles/, GET /articles//仅可读公开文章permission_classes = [IsAuthenticatedOrReadOnly] 或自定义
登录用户(Author)上述 + 创建、管理自己的文章可 CRUD 自己的文章自定义权限类 IsOwnerOrReadOnly
管理员(Admin)所有接口可管理所有文章,包括删除他人文章IsAdminUser 或 IsSuperUser
IsOwnerOrReadOnly自定义类has_object_permission 检查 obj.author == request.userclass IsOwnerOrReadOnly(BasePermission):; def has_object_permission(self, request, view, obj):; if request.method in SAFE_METHODS: return True; return obj.author == request.user
组合权限多个权限类如 [IsAuthenticated, IsOwnerOrReadOnly]所有权限必须通过
状态过滤仅显示 status=‘published’ 的文章给访客在 get_queryset() 中过滤def get_queryset(self):; if not request.user.is_authenticated: return Article.objects.filter(status='published')

14.4 接口测试与 Postman 验证

测试项测试方法预期结果注意事项
注册功能POST /api/auth/register/ 带用户名密码返回 201,用户创建成功检查密码是否加密存储
登录获取 TokenPOST /api/token/ 带凭证返回 access 和 refresh Token使用 JWT 时验证 Token 有效性
创建文章POST /api/articles/ 带 Token 和数据201 Created,作者为当前用户无 Token 应返回 401
获取文章列表GET /api/articles/返回分页数据,包含标题、作者匿名用户也应能访问
编辑他人文章PUT /api/articles/2/(非作者)403 Forbidden验证权限拦截
删除文章DELETE /api/articles/1/(作者)204 No Content数据库记录应被删除
搜索文章GET /api/articles/?search=python返回标题或内容包含”python”的文章确保 SearchFilter 正常工作
Postman 测试集合导出为 Collection团队共享测试用例可自动化运行(Newman)
环境变量在 Postman 中设置 base_url, token避免硬编码便于切换开发/生产环境
响应断言添加 Tests 脚本验证状态码、字段pm.test("Status 200", () => { pm.response.to.have.status(200); });提高测试可靠性