ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Django Rest Framework构建API的实现示例

Django Rest Framework构建API的实现示例 前言Django REST framework通常简称 DRF是 Django 生态里最主流的 REST API 框架。它不是 Django 自带的而是一个独立的第三方包要单独安装。这一点常被误解——很多人以为 Django 生来就能写 REST 接口实际上不加 DRF 也完全可以手写接口只是要自己处理序列化、内容协商、状态码、分页很啰嗦。另一个常见误解是「DRF 只有一种写法」。实际上它提供了三种粒度不同的视图APIView最原始、泛型视图针对单个模型的常用操作、ViewSet 路由器把一组相关操作打包。新手常见的两种极端是要么全部手写APIView把 DRF 当普通 Django 用要么一上来就ModelViewSet却不清楚它到底暴露了哪几个端点、哪些该关掉。本文用一个博客文章Post的例子从序列化器到路由完整走一遍。示例代码以 Django 5.x / 6.x 搭配当前稳定版 DRF 为背景DRF 的具体 API 以官方文档为准Python 版本要求取决于你选的 Django 版本如 Django 6.x 需要 Python 3.12 及以上。一、安装与最小配置pip install djangorestframework然后在settings.py里注册并给一组全局默认值# settings.pyINSTALLED_APPS [# ... Django 自带应用rest_framework,blog, # 你自己的应用]REST_FRAMEWORK {DEFAULT_AUTHENTICATION_CLASSES: [rest_framework.authentication.TokenAuthentication,],DEFAULT_PERMISSION_CLASSES: [rest_framework.permissions.IsAuthenticatedOrReadOnly,],DEFAULT_PAGINATION_CLASS: rest_framework.pagination.PageNumberPagination,PAGE_SIZE: 10,}两个要点REST_FRAMEWORK是一个字典键名全大写分页要同时设置DEFAULT_PAGINATION_CLASS和PAGE_SIZE因为两者的默认值都是None只设一个不会生效。二、被拆解的数据模型# blog/models.pyfrom django.db import modelsclass Post(models.Model):title models.CharField(max_length200)body models.TextField()created models.DateTimeField(auto_now_addTrue)def __str__(self):return self.title改完模型照例要python manage.py makemigrations blog再python manage.py migrate。三、序列化器模型与 JSON 之间的翻译层序列化器serializer负责两件事把模型实例变成可以返回的 JSON序列化把请求里的 JSON 变成经过校验的 Python 数据反序列化。# blog/serializers.pyfrom rest_framework import serializersfrom .models import Postclass PostSerializer(serializers.ModelSerializer):class Meta:model Postfields [id, title, body, created]read_only_fields [created] # 只读不接受客户端传入def validate_title(self, value):# 针对单个字段的校验钩子名字必须是 validate_字段名if len(value.strip()) 3:raise serializers.ValidationError(标题至少 3 个字符)return valuedef validate(self, attrs):# 跨字段校验attrs 是已校验的字段字典if attrs.get(title) attrs.get(body):raise serializers.ValidationError(标题和正文不能一模一样)return attrs用ModelSerializer时fields列表决定了哪些字段会被暴露。这里有个重要的安全习惯永远显式列出fields不要图省事写fields __all__——数据库里的敏感字段密码哈希、内部标记会随着模型演进而自动泄露出去。手动使用序列化器的流程# 适用于 DRF 当前稳定版from blog.serializers import PostSerializer# 反序列化校验请求数据ser PostSerializer(data{title: 第一篇, body: 正文内容})ser.is_valid(raise_exceptionTrue) # 校验失败直接抛 400post ser.save() # 调用 create()# 序列化把对象转成可返回的数据print(PostSerializer(post).data)is_valid()返回布尔值加了raise_exceptionTrue后校验失败会直接抛出 DRF 的异常由框架转成 HTTP 400省去手写判断。校验通过的数据在ser.validated_data错误信息在ser.errors。四、视图的三种粒度写法抽象程度适合APIView最低非 CRUD 的特殊逻辑完全自定义泛型视图ListCreateAPIView等中针对单模型的常见操作ViewSetModelViewSet最高标准 CRUD配合路由器自动生成 URL最省事的是ModelViewSet。官方文档写明它提供.list()、.retrieve()、.create()、.update()、.partial_update()、.destroy()六个动作也就是标准的「增删改查」全套。# blog/views.pyfrom rest_framework import permissions, viewsetsfrom rest_framework.decorators import actionfrom rest_framework.response import Responsefrom .models import Postfrom .serializers import PostSerializerclass PostViewSet(viewsets.ModelViewSet):queryset Post.objects.all().order_by(-created)serializer_class PostSerializerpermission_classes [permissions.IsAuthenticatedOrReadOnly]action(detailFalse, methods[get])def recent(self, request):额外动作GET /posts/recent/只返回最新 5 篇。qs self.filter_queryset(self.get_queryset())[:5]serializer self.get_serializer(qs, manyTrue)return Response(serializer.data)action用来给 ViewSet 加「不标准」的端点。两个关键参数detailTrue表示针对单个对象URL 里带主键detailFalse表示针对整个集合methods指定允许的 HTTP 方法。注册后recent会挂到/posts/recent/上。官方文档有一条提醒值得记住不要对action方法用.as_view()——那会绕过路由器的设置导致permission_classes之类的动作配置被忽略。五、路由交给路由器自动生成# blog/urls.pyfrom django.urls import include, pathfrom rest_framework.routers import DefaultRouterfrom .views import PostViewSetrouter DefaultRouter()router.register(rposts, PostViewSet, basenamepost)urlpatterns [path(, include(router.urls)),]# 根 urls.pyfrom django.contrib import adminfrom django.urls import include, pathfrom rest_framework.authtoken import views as token_viewsurlpatterns [path(admin/, admin.site.urls),path(api/, include(blog.urls)),path(api-token-auth/, token_views.obtain_auth_token),]DefaultRouter自动生成的端点大致是HTTP 方法URL动作GET/api/posts/列表POST/api/posts/新建GET/api/posts/{id}/详情PUT/api/posts/{id}/整体更新PATCH/api/posts/{id}/局部更新DELETE/api/posts/{id}/删除GET/api/posts/recent/自定义动作register方法支持可选的basename参数。当 ViewSet 没有定义queryset属性时必须显式给basename否则路由器无法推断 URL 名称。六、认证与权限DRF 把「认证」你是谁和「权限」你能不能做分成两层可以分别配置。REST_FRAMEWORK {DEFAULT_AUTHENTICATION_CLASSES: [rest_framework.authentication.TokenAuthentication,],DEFAULT_PERMISSION_CLASSES: [rest_framework.permissions.IsAuthenticated,],}用TokenAuthentication需要额外做两件事把rest_framework.authtoken加进INSTALLED_APPS并执行python manage.py migrate该应用自带数据库迁移。之后客户端用请求头发送令牌Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b默认权限类很关键。DEFAULT_PERMISSION_CLASSES不设置时DRF 默认是AllowAny——任何人都能读写你的接口。新项目上线前务必确认这一项已改成IsAuthenticated或IsAuthenticatedOrReadOnly。官方文档还有一条硬性要求在传输层使用TokenAuthentication时必须保证 API 只通过 HTTPS 暴露否则令牌等于明文裸奔。获得令牌可以用内置视图把用户名密码 POST 上去from rest_framework.authtoken import views as token_viewsurlpatterns [path(api-token-auth/, token_views.obtain_auth_token),]常见坑点1. 忘了把rest_framework加进INSTALLED_APPS❌ 直接from rest_framework import serializers后页面报配置错误。 ✅ 先pip install djangorestframework再加进INSTALLED_APPS。2.fields __all__埋下泄露隐患❌class Meta: model User; fields __all__密码哈希也被暴露。 ✅ 显式列出fields [id, username, email]。3. 以为 DRF 默认要求登录❌ 不配DEFAULT_PERMISSION_CLASSES接口对全网开放还不自知。 ✅ 显式设置权限类默认是AllowAny。4. ViewSet 没有queryset也没给basename❌router.register(rposts, PostViewSet)抛路由命名错误。 ✅ 补上basenamepost或给 ViewSet 定义queryset。5. 只设了PAGE_SIZE却没设分页类❌ 以为配个PAGE_SIZE就自动分页结果返回全量数据。 ✅DEFAULT_PAGINATION_CLASS和PAGE_SIZE两个都要设。6. 用HttpResponse返回 DRF 的数据❌ 在 DRF 视图里写return HttpResponse(serializer.data)收到一个str的字典。 ✅ 用rest_framework.response.Response。7. 忘记raise_exceptionTrue❌if ser.is_valid(): ...校验失败时静默跳过客户端收到 200。 ✅ser.is_valid(raise_exceptionTrue)失败自动返回 400。8. 用 TokenAuthentication 却跑在 HTTP 上❌ 明文 HTTP 传Authorization: Token ...令牌可被截获。 ✅ 生产必须走 HTTPS或用更适合浏览器的会话认证。总结组件作用关键设置ModelSerializer模型与 JSON 互转显式列fields用validate_*校验ModelViewSet一套 CRUD 动作提供 list/retrieve/create/update/destroyaction加自定义端点detail决定是否带主键DefaultRouter自动生成 URL无queryset时必须给basename认证 / 权限分层控制访问默认AllowAny上线要改分页控制返回数量分页类与PAGE_SIZE同时设DRF 的核心价值是把「序列化 校验 标准 CRUD 路由」这套重复劳动模板化。上手时按Serializer → ViewSet → Router的顺序理解再回头记牢两件事fields要显式列权限默认是开放。做到这两点你已经能写出既简洁又不至于把数据泄露出去的 API 了。
返回列表