Django REST Framework

Web开发专题 · 掌握Django构建RESTful API的能力

专题:Python Web开发系统学习

关键词:Python, Web开发, DRF, Django REST Framework, ModelSerializer, ViewSet, JWT认证, API文档

一、DRF概述

Django REST Framework(简称DRF)是构建RESTful API的强大工具包,建立在Django之上,提供了序列化、认证、权限、视图集、路由等完整的API开发组件。DRF遵循Django的设计哲学——"约定优于配置",同时提供了极高的灵活性,让开发者能够快速构建出高质量的Web API。

1.1 安装与配置

使用pip安装DRF非常简单,同时建议安装配套的过滤支持和文档生成工具:

pip install djangorestframework pip install django-filter pip install drf-spectacular # Swagger文档生成

在Django项目的settings.py中,将rest_framework注册到INSTALLED_APPS列表中:

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', # ... 'rest_framework', 'drf_spectacular', # API文档 ]

推荐在settings.py中配置全局的DRF设置,包括默认的认证方式、权限策略、分页大小等:

REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework_simplejwt.authentication.JWTAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10, 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', }

1.2 序列化与反序列化

序列化是DRF的核心概念,指将Python对象(如Django模型实例、Queryset)转换为JSON、XML等前端可消费的格式;反序列化则是将前端提交的JSON数据解析为Python对象,并经过校验后存入数据库。DRF的Serializer类同时承担了序列化和反序列化的职责,通过同一个类完成数据的双向转换。

1.3 请求响应处理

DRF对Django原生的HttpRequest进行了封装,提供了Request对象,它自动解析JSON请求体;同时提供了Response对象,替代Django的HttpResponse,自动将数据渲染为合适的格式(JSON默认)。此外,DRF还提供了状态码常量(status.HTTP_200_OK、status.HTTP_400_BAD_REQUEST等),让API响应更加语义化。

二、序列化器(Serializers)

序列化器是DRF的基石,负责数据的序列化与反序列化。DRF提供了多种序列化器,适应不同场景的需求。

2.1 Serializer基本序列化

最基本的Serializer类需要手动定义每个字段,适合完全自定义的数据结构。例如,定义一个简单的博客文章序列化器:

from rest_framework import serializers class ArticleSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=200) content = serializers.CharField() author = serializers.CharField(max_length=100) created_at = serializers.DateTimeField(read_only=True) def create(self, validated_data): return Article.objects.create(**validated_data) def update(self, instance, validated_data): instance.title = validated_data.get('title', instance.title) instance.content = validated_data.get('content', instance.content) instance.save() return instance

可以看到,基本Serializer需要手动实现create()和update()方法,适合非模型数据或复杂自定义逻辑。

2.2 ModelSerializer自动生成

ModelSerializer是日常开发中使用频率最高的序列化器,它能根据Django模型自动推断字段定义、验证规则以及create/update方法,大幅减少重复代码:

class ArticleSerializer(serializers.ModelSerializer): class Meta: model = Article fields = ['id', 'title', 'content', 'author', 'created_at'] read_only_fields = ['id', 'created_at']

通过fields指定需要暴露的字段,使用'__all__'表示全部字段,使用exclude排除不需要的字段。read_only_fields指定只读字段(通常是服务器自动生成的字段如id、created_at)。

2.3 字段定义与验证

ModelSerializer支持字段级别的验证和方法级别的验证。字段验证可以通过在Meta中覆盖字段定义来实现,也可以使用validate_方法进行自定义逻辑校验:

class ArticleSerializer(serializers.ModelSerializer): # 覆盖自动生成的字段,添加额外约束 title = serializers.CharField(min_length=5, max_length=200) class Meta: model = Article fields = '__all__' # 字段级验证 def validate_title(self, value): if '敏感词' in value: raise serializers.ValidationError("标题包含非法内容") return value # 对象级验证 def validate(self, data): if data['end_date'] and data['start_date'] and \ data['end_date'] <= data['start_date']: raise serializers.ValidationError("结束日期必须晚于开始日期") return data

2.4 嵌套序列化

当模型之间存在外键或多对多关系时,嵌套序列化用于展现关联对象的信息。DRF支持只读嵌套(直接嵌套另一个序列化器)和可写嵌套(需要自定义create/update方法):

class CommentSerializer(serializers.ModelSerializer): class Meta: model = Comment fields = ['id', 'content', 'created_at'] class ArticleSerializer(serializers.ModelSerializer): comments = CommentSerializer(many=True, read_only=True) author_detail = serializers.SerializerMethodField() class Meta: model = Article fields = ['id', 'title', 'comments', 'author_detail'] def get_author_detail(self, obj): return { 'username': obj.author.username, 'email': obj.author.email, }

SerializerMethodField允许动态计算返回数据,适合无需单独定义序列化器的简单转换。

2.5 反序列化与数据校验

反序列化流程中,is_valid()触发所有验证规则,验证通过后validated_data包含清洗后的数据,errors字典包含校验失败信息。开发者可以在视图中如下使用:

serializer = ArticleSerializer(data=request.data) if serializer.is_valid(): article = serializer.save() # 调用create()或update() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

三、视图(Views)

DRF提供了从函数视图到完整视图集的多种抽象层次,开发者可以根据需求的复杂度选择合适的视图类型。

3.1 @api_view装饰器(函数视图)

@api_view是DRF中最低层级的视图方式,基于Django的普通函数视图,添加了DRF的请求处理、响应渲染和异常处理能力。适合非常简单的API端点:

from rest_framework.decorators import api_view from rest_framework.response import Response @api_view(['GET', 'POST']) def article_list(request): if request.method == 'GET': articles = Article.objects.all() serializer = ArticleSerializer(articles, many=True) return Response(serializer.data) elif request.method == 'POST': serializer = ArticleSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

3.2 APIView类视图

APIView是DRF中所有类视图的基类,继承了Django的View类,并添加了认证、权限、限流等支持。通过类方法区分HTTP方法,代码更加模块化:

class ArticleList(APIView): def get(self, request, format=None): articles = Article.objects.all() serializer = ArticleSerializer(articles, many=True) return Response(serializer.data) def post(self, request, format=None): serializer = ArticleSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

3.3 GenericAPIView通用视图

GenericAPIView在APIView的基础上增加了queryset、serializer_class、lookup_field等属性,并与Mixin类搭配使用,大幅简化常见CRUD操作的代码量:

class ArticleList(GenericAPIView, ListModelMixin, CreateModelMixin): queryset = Article.objects.all() serializer_class = ArticleSerializer def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)

3.4 Mixin混合类

DRF内置了五个Mixin类,分别对应五种基础操作,开发者可以根据需要组合使用:

  • ListModelMixin — 列表查询,对应GET请求,返回queryset列表
  • CreateModelMixin — 创建资源,对应POST请求,创建新对象
  • RetrieveModelMixin — 单个查询,对应GET请求(带pk),返回单个对象
  • UpdateModelMixin — 更新资源,对应PUT/PATCH请求,更新现有对象
  • DestroyModelMixin — 删除资源,对应DELETE请求,删除对象

Mixin类提供的是行为(action),将它们与GenericAPIView组合,即可快速构建完整的CRUD视图。

3.5 ViewSet视图集

ViewSet将一组相关的操作聚合到一个类中,通过Router自动映射URL。ViewSet继承自ViewSetMixin和APIView,将HTTP方法与自定义action关联:

class ArticleViewSet(ViewSet): def list(self, request): queryset = Article.objects.all() serializer = ArticleSerializer(queryset, many=True) return Response(serializer.data) def create(self, request): serializer = ArticleSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def retrieve(self, request, pk=None): article = get_object_or_404(Article, pk=pk) serializer = ArticleSerializer(article) return Response(serializer.data) def update(self, request, pk=None): article = get_object_or_404(Article, pk=pk) serializer = ArticleSerializer(article, data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def destroy(self, request, pk=None): article = get_object_or_404(Article, pk=pk) article.delete() return Response(status=status.HTTP_204_NO_CONTENT)

3.6 ModelViewSet完整CRUD

ModelViewSet继承自GenericAPIView,并组合了ListModelMixin、CreateModelMixin、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin五大Mixin。只需指定queryset和serializer_class,即可获得完整的CRUD端点,是日常开发中最高效的视图选择:

class ArticleViewSet(ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer permission_classes = [IsAuthenticatedOrReadOnly] def perform_create(self, serializer): # 自动填充当前用户为作者 serializer.save(author=self.request.user)

ModelViewSet还支持通过@action装饰器添加自定义端点,比如给文章点赞、发布草稿等业务操作:

class ArticleViewSet(ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer @action(detail=True, methods=['post']) def publish(self, request, pk=None): article = self.get_object() article.status = 'published' article.save() return Response({'status': 'published'})

四、路由器(Routers)

路由器是DRF将ViewSet与URL配置解耦的关键组件。它自动为ViewSet中的每个action生成对应的URL模式,省去了手动编写urlpatterns的繁琐工作。

4.1 DefaultRouter自动生成URL

DefaultRouter是功能最完整的路由器,它会为ViewSet的list和detail视图自动生成URL,同时还会生成一个API根视图(列出所有注册的端点)和一个可浏览的API页面:

from rest_framework.routers import DefaultRouter from .views import ArticleViewSet, UserViewSet router = DefaultRouter() router.register(r'articles', ArticleViewSet, basename='article') router.register(r'users', UserViewSet, basename='user') urlpatterns = [ path('', include(router.urls)), ]

上述配置自动生成以下URL模式:GET /articles/(列表)、POST /articles/(创建)、GET /articles/{id}/(详情)、PUT /articles/{id}/(全量更新)、PATCH /articles/{id}/(部分更新)、DELETE /articles/{id}/(删除)。

4.2 SimpleRouter

SimpleRouter与DefaultRouter的功能基本相同,区别在于SimpleRouter不会生成API根视图,也不会生成可浏览的API页面,适合仅需API功能的项目。SimpleRouter默认只生成标准的list和detail URL,但同样支持@action装饰器定义的自定义路由。

4.3 自定义路由

对于特殊需求,可以自定义路由类,继承BaseRouter并实现get_urls()方法。更常见的方式是使用@action装饰器为ViewSet添加自定义端点,路由器会自动识别并生成URL。@action支持detail=True(在单个资源上操作)和detail=False(在集合上操作),并通过url_path参数自定义URL路径名。

五、认证与权限

认证(Authentication)解决的是"你是谁"的问题,权限(Permission)解决的是"你能做什么"的问题。两者组合使用,构建安全的API访问控制体系。

5.1 认证方式

  • BasicAuthentication — 基于HTTP Basic Auth,适合测试环境,生产环境不建议使用(明文传输凭据)。
  • SessionAuthentication — 基于Django Session,适合与前端共享Session的Web应用,利用CSRF token防止跨站请求伪造。
  • TokenAuthentication — DRF内置的Token认证,简单易用但缺乏过期机制,适合小型项目或内部API。
  • JWTAuthentication — 通过djangorestframework-simplejwt实现,使用JSON Web Token,支持过期时间、刷新令牌,是目前最流行的API认证方式。

JWT认证配置示例:

from datetime import timedelta SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30), 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), 'AUTH_HEADER_TYPES': ('Bearer',), 'AUTH_TOKEN_CLASSES': ( 'rest_framework_simplejwt.tokens.AccessToken', ), }

5.2 内置权限类

  • AllowAny — 允许所有访问(无论是否认证),适合公开API
  • IsAuthenticated — 仅允许已认证用户访问
  • IsAdminUser — 仅允许管理员(is_staff=True)访问
  • IsAuthenticatedOrReadOnly — 未认证用户只能读取(GET/HEAD/OPTIONS),认证用户可以写入

权限可以在全局设置中配置,也可以在单个视图或ViewSet中覆盖:

class ArticleViewSet(ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer permission_classes = [IsAuthenticatedOrReadOnly] def get_permissions(self): if self.action == 'destroy': return [IsAdminUser()] return super().get_permissions()

5.3 自定义权限

继承BasePermission类并重写has_permission(视图级别)和has_object_permission(对象级别)方法,可以实现细粒度的权限控制,比如只允许资源所有者编辑自己的文章:

from rest_framework.permissions import BasePermission class IsOwner(BasePermission): def has_object_permission(self, request, view, obj): return obj.author == request.user

在ViewSet中配合使用:`permission_classes = [IsAuthenticated, IsOwner]`,确保只有登录用户中的资源所有者可以操作。

六、过滤、搜索与排序

DRF提供了强大的过滤、搜索和排序支持,让客户端可以灵活地查询所需数据。

6.1 django-filter集成

django-filter是Django生态中最流行的过滤库,DRF通过DjangoFilterBackend直接集成,支持精确匹配、范围查询、模糊搜索等多种过滤方式。首先定义FilterSet:

import django_filters from .models import Article class ArticleFilter(django_filters.FilterSet): title = django_filters.CharFilter(lookup_expr='icontains') created_after = django_filters.DateFilter( field_name='created_at', lookup_expr='gte') created_before = django_filters.DateFilter( field_name='created_at', lookup_expr='lte') class Meta: model = Article fields = ['status', 'author']

然后在视图中配置filter_backends和filterset_class:

class ArticleViewSet(ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer filter_backends = [DjangoFilterBackend] filterset_class = ArticleFilter

客户端即可通过URL参数进行过滤:`/api/articles/?status=published&created_after=2025-01-01`。

6.2 SearchFilter搜索

SearchFilter实现基于文本的全文搜索,通过search查询参数传递关键字。search_fields指定哪些字段参与搜索,支持^(开头匹配)、=(精确匹配)、@(全文搜索)、$(正则匹配)前缀:

class ArticleViewSet(ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer filter_backends = [SearchFilter] search_fields = ['^title', 'content']

客户端请求:`/api/articles/?search=Django`,会返回所有标题以"Django"开头或内容包含"Django"的文章。

6.3 OrderingFilter排序

OrderingFilter允许客户端通过ordering参数对结果进行排序,ordering_fields限定可排序的字段集合:

class ArticleViewSet(ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer filter_backends = [OrderingFilter] ordering_fields = ['created_at', 'title'] ordering = ['-created_at'] # 默认排序

客户端请求:`/api/articles/?ordering=-created_at`即按创建时间倒序排列。

6.4 分页

DRF提供了三种内置分页器,可以根据项目需求选择:

  • PageNumberPagination — 基于页码的分页,客户端指定page参数,适合绝大多数场景。可通过page_size_query_param允许客户端自定义每页数量。
  • LimitOffsetPagination — 基于偏移量的分页,客户端指定limit(数量)和offset(起始位置),适合需要精确位置控制的场景。
  • CursorPagination — 基于游标的分页,客户端通过cursor参数翻页,性能最优且数据一致性最好,但只能向前或向后翻页(不能跳转到指定页)。

自定义分页器示例:

class CustomPagination(PageNumberPagination): page_size = 20 page_size_query_param = 'page_size' max_page_size = 100

七、限流(Throttling)

限流机制用于控制客户端在单位时间内可以发起的请求数量,防止API被滥用。DRF内置了多种限流类,同时也支持自定义限流策略。

7.1 AnonRateThrottle

AnonRateThrottle针对未认证的匿名用户进行限流。DRF通过请求的IP地址区分不同的匿名用户。适合限制公开API的调用频率,防止爬虫过度访问:

REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.AnonRateThrottle', ], 'DEFAULT_THROTTLE_RATES': { 'anon': '100/hour', # 匿名用户每小时最多100次请求 } }

7.2 UserRateThrottle

UserRateThrottle针对已认证的用户进行限流。DRF通过user进行区分,因此即使用户切换IP地址,限流依然有效。适合需要区分付费用户和免费用户使用频率的场景:

REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.UserRateThrottle', ], 'DEFAULT_THROTTLE_RATES': { 'user': '1000/day', # 登录用户每天最多1000次请求 } }

7.3 ScopedRateThrottle

ScopedRateThrottle允许针对特定的视图或API端点设置不同的限流速率,通过在视图类中定义throttle_scope属性实现更精细的控制。比如对计算密集型接口设置更严格的限制:

class ExportView(APIView): throttle_scope = 'exports' throttle_classes = [ScopedRateThrottle] def get(self, request): # 导出大量数据的复杂操作 ... REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES': { 'exports': '10/hour', # 导出接口每小时最多10次 } }

7.4 自定义限流

继承SimpleRateThrottle并重写get_cache_key方法,可以实现自定义限流策略。例如,按API Key进行限流,或对特定用户组设置不同速率。自定义限流类可以灵活控制限流粒度和适用范围,满足复杂业务需求。

八、API文档

自动生成API文档是DRF生态的重要优势。当前最推荐的方式是使用drf-spectacular,它基于OpenAPI 3.0标准,生成规范且美观的Swagger文档。

8.1 drf-spectacular配置

首先安装并注册drf-spectacular,然后在项目的urls.py中配置文档视图:

from drf_spectacular.views import ( SpectacularAPIView, SpectacularSwaggerView, SpectacularRedocView, ) urlpatterns = [ # OpenAPI Schema 文件 path('api/schema/', SpectacularAPIView.as_view(), name='schema'), # Swagger UI(交互式文档) path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), # ReDoc UI(替代风格文档) path('api/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), ]

8.2 文档描述配置

通过装饰器或类属性为API端点添加详细的描述信息,使生成的文档更加丰富易用:

from drf_spectacular.utils import ( extend_schema, extend_schema_view, OpenApiParameter, OpenApiExample, ) @extend_schema( summary='获取文章列表', description='返回分页的文章列表,支持过滤和搜索', parameters=[ OpenApiParameter( name='search', description='搜索关键词', required=False, type=str), OpenApiParameter( name='status', description='文章状态:draft/published', required=False, type=str), ], responses={200: ArticleSerializer(many=True)}, ) def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs)

8.3 API交互测试

Swagger UI提供了强大的交互式测试功能,开发者可以直接在浏览器中填写参数并发送请求,无需额外使用Postman等工具。文档页面上展示了每个端点的请求方法、URL路径、请求参数、请求体格式以及响应示例,方便前端开发者快速对接。ReDoc则提供了更适合阅读的文档呈现方式,两者结合可以覆盖开发者和消费者的不同需求。

最佳实践:在settings.py中通过SPECTACULAR_SETTINGS配置文档标题、版本、描述等元信息。将API文档部署在独立的环境中,开发环境和测试环境开放文档访问,生产环境可以关闭文档以避免安全风险。

本学习笔记为本人学习资料,不得转载