ARTICLE DETAIL

资讯详情

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

Django REST Framework 实战指南:序列化、权限与性能优化全解析

Django REST Framework 实战指南:序列化、权限与性能优化全解析 1. 为什么是DRF选型背后的真实考量做后端开发这些年我见过太多团队在API框架选型上反复纠结。有人图轻量选了Flask-RESTful结果业务复杂以后路由和序列化逻辑堆成一团乱麻有人跟风上了FastAPI确实性能亮眼但碰到Django生态里现成的ORM、Admin、迁移工具链总觉得差了点什么。如果你本身就在用Django写业务系统或者团队里Django的技术积累已经比较厚实那么Django REST Framework简称DRF基本就是构建API的最优解没有之一。DRF不是把Django简单包装一层路由它是一套完整的API开发框架。从序列化器到视图、从路由到认证权限、从分页到限流、从文档生成到测试客户端官方都给你铺好了路。我自己的感受是用DRF写API前期配置稍微多花十分钟后期维护能省下一个通宵。尤其是当你需要同时维护Web端和App端接口时DRF那套基于Model的序列化器能直接把数据库模型和JSON输出之间的映射关系固定下来改模型字段时序列化器同步调整基本不会出现“后端改了字段前端还在用旧字段名”的脱节问题。选型这件事我个人的判断标准很朴素你的核心诉求是什么如果你的诉求是快速迭代业务、需要极强的管理后台支撑、团队又熟悉Python和Django那DRF就是顺理成章的选择。如果你的诉求是极致性能、异步原生支持、大流量高并发那最好考虑别的方案。DRF并不是万能的但在Django生态内做API它就是最成熟、最稳的那条路。2. DRF的四大核心机制序列化器、视图、路由、认证权限2.1 序列化器不只是Model转JSON序列化器是DRF的灵魂。很多初学者第一次接触DRF时以为序列化器就是把Django模型变成字典再变成JSON反序列化就是把JSON变回模型。这个理解方向没错但远远不够。序列化器真正的价值在于它定义了一套“数据进出API的契约”。你拿到一份前端传上来的JSON序列化器负责校验字段、检查类型、验证业务规则通过后才写入数据库。这就是为什么我强烈建议不要在视图里直接操作request.data然后手动创建模型对象——那样写起来快但校验逻辑散落在各个视图里改一处漏三处。说几个实际场景。用ModelSerializer时DRF会自动根据模型字段生成对应的序列化字段省掉大量重复代码。比如from rest_framework import serializers from .models import Product class ProductSerializer(serializers.ModelSerializer): category_name serializers.CharField(sourcecategory.name, read_onlyTrue) class Meta: model Product fields [id, name, price, stock, category, category_name, created_at] extra_kwargs { price: {min_value: 0, max_value: 999999}, stock: {min_value: 0}, }这里的extra_kwargs就很有讲究。如果不在序列化器里约束price不能为负数那你就得在模型层的clean()方法里校验或者在视图里手动判断。DRF把这些业务校验前置到了序列化层前端一提交非法数据直接返回带字段错误的400响应不用经过数据库那一趟。嵌套序列化器也是常见需求。比如商品详情里要带出该商品的全部SKU列表class SKUSerializer(serializers.ModelSerializer): class Meta: model SKU fields [id, spec, price, stock] class ProductDetailSerializer(serializers.ModelSerializer): skus SKUSerializer(manyTrue, read_onlyTrue) class Meta: model Product fields [id, name, description, skus]注意这里的read_onlyTrue。嵌套序列化器默认是可写的但实际场景中让前端一次性提交整个嵌套结构很容易出问题——比如多个SKU的事务一致性DRF默认是不管的。所以我的习惯是列表类嵌套全部设成read_only写操作走专门的端点一步只做一件事。2.2 视图层APIView与ViewSet的选择逻辑DRF提供了好几层视图抽象从最基础的APIView到GenericAPIView再到ModelViewSet。很多新手纠结该用哪个其实选择逻辑很简单接口越是“标准的CRUD”越应该往ViewSet靠接口越是有特殊业务逻辑越应该用APIView自己写。ModelViewSet适合那种对某个模型做增删改查、每个操作基本对齐CRUD语义的接口。配合DefaultRouter一个视图集直接生成列表、详情、创建、更新、删除五套路由代码量极少。比如用户地址管理这种功能用ViewSet十分钟搞定from rest_framework.viewsets import ModelViewSet from .models import Address from .serializers import AddressSerializer class AddressViewSet(ModelViewSet): queryset Address.objects.all() serializer_class AddressSerializer但如果你需要的是一个“下单”接口——它涉及订单创建、库存扣减、优惠券核销、支付单生成这明显不是单纯的CRUD。这种情况继续用ViewSet硬套反而要重写很多方法。我更建议直接用APIViewfrom rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status class CreateOrderView(APIView): def post(self, request): # 1. 校验参数 # 2. 检查库存 # 3. 创建订单 # 4. 扣减库存 # 5. 返回结果 return Response({order_id: order.id}, statusstatus.HTTP_201_CREATED)关键思路是框架给你的是便利不是枷锁。CRUD密集的地方用高抽象业务复杂的地方用低抽象两者混用完全没问题。我自己维护的项目里高抽象和低抽象的视图大概七三开CRUD用ViewSet省时间核心业务用APIView保证可控。2.3 路由与URL设计不要忽略的细节路由这块看起来简单但坑也不少。用DefaultRouter注册ViewSet以后URL会带上/api/addresses/和/api/addresses/{pk}/这种风格。这里有个小细节DRF默认的路由名称是{model}-list和{model}-detail如果你在前端需要反推URL用reverse方法时要写对这两个名字。团队协作时URL的命名规范一定要提前定。我的习惯是所有API前缀统一加/api/版本号放在第二层比如/api/v1/、/api/v2/。这样即便以后接口做了不兼容的升级旧版本还能继续服务老客户端一段时间不至于被业务方追着骂。每次看到有人把版本信息写在请求头里我就头疼——调试工具里看不见、日志里难追踪、第三方客户接入时还容易漏配。还有一点DRF的路由装饰器action挺好用能自定义ViewSet内的额外动作。比如商品的上架和下架from rest_framework.decorators import action from rest_framework.response import Response class ProductViewSet(ModelViewSet): queryset Product.objects.all() serializer_class ProductSerializer action(detailTrue, methods[post]) def publish(self, request, pkNone): product self.get_object() product.is_active True product.save() return Response({status: published}) action(detailTrue, methods[post]) def unpublish(self, request, pkNone): product self.get_object() product.is_active False product.save() return Response({status: unpublished})这样路由会生成/api/products/{pk}/publish/和/api/products/{pk}/unpublish/语义非常清晰。注意detailTrue表示针对单个对象的动作methods里指定HTTP方法。2.4 认证与权限光有Token远远不够DRF自带Session、Basic、Token三类认证但它们各有各的适用场景。Session认证适合前后端同源的Web应用Token认证适合简单的App后端如果是复杂一点的分布式系统我个人推荐直接用JWTJSON Web Token但DRF原生不集成JWT需要配合djangorestframework-simplejwt这个第三方库。SimpleJWT的配置不难关键是在settings.py里配好认证类和权限类REST_FRAMEWORK { DEFAULT_AUTHENTICATION_CLASSES: ( rest_framework_simplejwt.authentication.JWTAuthentication, ), DEFAULT_PERMISSION_CLASSES: ( rest_framework.permissions.IsAuthenticated, ), }然后通过/api/token/获取Token请求时在Header里带Authorization: Bearer token。这里我必须强调一个经验默认权限别放太宽。很多团队图调试方便一开始把DEFAULT_PERMISSION_CLASSES设成AllowAny上线前才想起来收紧结果漏掉了某个ViewSet的权限配置生产环境接口裸奔了大半个星期。我的建议是默认IsAuthenticated哪个接口需要匿名访问就在那个视图或视图集上单独用permission_classes [AllowAny]放开这样“默认安全、个别开放”的做法更可控。权限控制再往细走就涉及对象级权限。比如用户只能修改自己的地址、只能查看自己的订单光靠IsAuthenticated不够必须写自定义权限类from rest_framework.permissions import BasePermission class IsOwnerOrReadOnly(BasePermission): def has_object_permission(self, request, view, obj): if request.method in SAFE_METHODS: return True return obj.user request.user然后在视图集里指定permission_classes [IsOwnerOrReadOnly]覆盖DEFAULT那套。如果模型没有直接关联用户就得通过外键回溯。比如订单明细的权限要看obj.order.user是否等于当前用户。3. 从需求到上线一个真实API项目的完整实现3.1 需求定义与模型设计纸上谈兵没有意思拿一个电商后台的商品管理模块来完整走一遍。需求相对清晰管理员能创建商品和SKU、维护价格库存普通用户能浏览商品列表、查看商品详情商品有上下架状态。为了讲清楚DRF各个模块的配合我刻意把需求限定在一个经典且完整的范围内。模型层是根基。商品和SKU是一对多的关系SKU是具体可下单的售卖单元。为了让后续的序列化器演示不落空我加了个分类外键并设计了软删除字段is_active——注意这里不用Django默认的硬删除电商场景里商品往往只是下架并不会真的从数据库消失。from django.db import models # Create your models here. class Category(models.Model): name models.CharField(max_length50) sort models.IntegerField(default0) class Meta: ordering [sort, id] class Product(models.Model): category models.ForeignKey(Category, on_deletemodels.PROTECT, related_nameproducts) name models.CharField(max_length200) description models.TextField(blankTrue) is_active models.BooleanField(defaultTrue) created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue) class Meta: ordering [-created_at] class SKU(models.Model): product models.ForeignKey(Product, on_deletemodels.CASCADE, related_nameskus) spec models.CharField(max_length100) price models.DecimalField(max_digits10, decimal_places2) stock models.IntegerField(default0)on_deletemodels.PROTECT这个细节值得留意Category被Product引用的场景下如果强行删分类数据库会拒绝。理由是分类下的商品不能没有归属宁可先转移商品再删分类也不能让数据悬挂。3.2 序列化器设计从基础到嵌套序列化器的设计决定了API的“观感”。商品序列化器我会拆成List和Detail两个版本列表页只需要基础字段详情页需要嵌套SKU。分开写的好处是列表接口payload小、响应快详情接口信息全、一次到位。# serializers.py class CategorySerializer(serializers.ModelSerializer): class Meta: model Category fields [id, name] class SKUSerializer(serializers.ModelSerializer): class Meta: model SKU fields [id, spec, price, stock] class ProductListSerializer(serializers.ModelSerializer): category CategorySerializer(read_onlyTrue) class Meta: model Product fields [id, name, category, is_active, created_at] class ProductDetailSerializer(serializers.ModelSerializer): category CategorySerializer(read_onlyTrue) skus SKUSerializer(manyTrue, read_onlyTrue) class Meta: model Product fields [id, name, description, category, skus, is_active, created_at, updated_at]嵌套的CategorySerializer和SKUSerializer都标注了read_onlyTrue因为在商品这个接口里我们只读关联信息不在这个端点创建分类或SKU。创建SKU有独立的接口职责分离后代码好维护得多。创建和更新时同一个序列化器可能只需要部分字段可写。比如创建商品时要求传入category和name更新时category可以不动。DRF的partialTrue能处理部分更新。更进一步可以在序列化器里重写validate方法来加自定义校验def validate_name(self, value): if len(value.strip()) 2: raise serializers.ValidationError(商品名称至少需要两个字符) return value3.3 视图组装与路由注册商品列表和详情用ReadOnlyModelViewSet因为这两个端点只读SKU的CRUD需要独立的管理端点用ModelViewSet加权限限制分类的维护也是标准CRUD。from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet # views.py class ProductViewSet(ReadOnlyModelViewSet): queryset Product.objects.filter(is_activeTrue) def get_serializer_class(self): if self.action list: return ProductListSerializer return ProductDetailSerializer class ProductAdminViewSet(ModelViewSet): queryset Product.objects.all() serializer_class ProductDetailSerializer permission_classes [IsAdminUser]这里有个细节值得展开ReadOnlyModelViewSet只有一个list和retrieve动作内部已经帮我们处理了“列表取集合、详情取单个”的逻辑。我们只需要根据self.action区分序列化器DRF会在调用get_serializer()时自动把action传进来。这样写的好处是列表接口高效轻量详情接口数据完整。路由注册用DefaultRouterfrom rest_framework.routers import DefaultRouter router DefaultRouter() router.register(rproducts, ProductViewSet, basenameproduct) router.register(radmin/products, ProductAdminViewSet, basenameadmin-product)basename要记得显式指定。如果ViewSet没有定义queryset属性DRF无法自动推断模型名会报错或者生成奇怪的路由名。我见过有人漏掉这个参数结果路由生成成功后reverse死活找不到名字排查半天。3.4 测试客户端与联调验证API写完了不测试等于没写。DRF自带的APITestCase和APIClient很好用不用额外引入第三方库就能模拟认证和请求。我习惯每个接口至少覆盖三个用例正常请求、未认证请求、非法参数请求。from rest_framework.test import APITestCase from rest_framework import status from django.contrib.auth.models import User class ProductAPITests(APITestCase): def setUp(self): self.user User.objects.create_user(usernametestuser, passwordtestpass) self.category Category.objects.create(name数码) self.product Product.objects.create(categoryself.category, name手机) def test_list_products_requires_auth(self): response self.client.get(/api/products/) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) def test_list_products_after_login(self): self.client.force_authenticate(userself.user) response self.client.get(/api/products/) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 1) def test_create_product_by_admin(self): admin User.objects.create_superuser(usernameadmin, passwordadmin, emailaa.com) self.client.force_authenticate(useradmin) response self.client.post(/api/admin/products/, { category: self.category.id, name: 平板电脑, description: }, formatjson) self.assertEqual(response.status_code, status.HTTP_201_CREATED)force_authenticate直接绕过认证逻辑模拟一个已登录用户测试权限类的时候特别方便。真实的集成测试比如JWT签发和校验则可以用APIClient().post(/api/token/, {...})换取Token后放进请求头两种方式搭配着用。4. 性能优化从慢速API到高并发的调整实录4.1 N1查询第一杀手DRF项目最常见的性能问题就是N1查询。列表接口返回100个商品每个商品又要单独查一次分类数据库就要执行101条SQL。商品列表的响应时间从几十毫秒飙到几秒数据量再大一点直接拖垮数据库。解决办法就是select_related和prefetch_related。前者针对外键的单对象查询用SQL的JOIN一次查出来后者针对一对多和多对多的集合查询分两次查完再在内存里组装。商品列表接口需要分类信息可以在查询集上加上class ProductViewSet(ReadOnlyModelViewSet): queryset Product.objects.filter(is_activeTrue).select_related(category).prefetch_related(skus)一句改动列表接口的SQL从1N降到了2到3条响应时间肉眼可见地下降。我用Django Debug Toolbar实测过加了这两行后商品列表接口的SQL数量从102条降到了3条接口耗时从1.8秒降到120毫秒。4.2 分页与过滤大列表的必答题不设分页的列表接口都是耍流氓。想象一下一个用户历史订单几万条的接口一次性把几万条记录塞进JSON前端卡死服务端内存也吃紧。DRF内置了分页类在settings里配置一次全局生效REST_FRAMEWORK { DEFAULT_PAGINATION_CLASS: rest_framework.pagination.PageNumberPagination, PAGE_SIZE: 20, }更细的需求可以自定义分页类。比如电商前端需要知道总页数可以继承PageNumberPagination把page_size设为可配置class CustomPagination(PageNumberPagination): page_size 20 page_size_query_param page_size max_page_size 100过滤也是列表接口的刚需。业务上有“只看某个分类的商品”“只看上架的商品”这种诉求。DRF的django-filter集成很成熟配置好以后前端可以直接用?category3is_activetrue这种形式过滤。REST_FRAMEWORK { DEFAULT_FILTER_BACKENDS: ( django_filters.rest_framework.DjangoFilterBackend, rest_framework.filters.SearchFilter, rest_framework.filters.OrderingFilter, ), } # 视图里指定可过滤字段 class ProductViewSet(ReadOnlyModelViewSet): filterset_fields [category, is_active] search_fields [name, description] ordering_fields [created_at, price, stock]4.3 缓存高并发下的加速器DRF的API天然适合加缓存尤其是只读接口。商品列表这种变动频率低、读频率高的数据用Django的cache_page装饰器或者DRF的action配合缓存都可以。我踩过的坑是缓存键设计不合理数据更新了用户看到的还是旧数据。后来我习惯的做法是给每个对象维护一个updated_at时间戳缓存键里带上时间戳或者版本号对象一更新缓存自动失效。最省事的方案是用django-cacheops这类库它能在Django ORM层面做查询集缓存模型保存时自动清理相关缓存不需要人肉管理缓存键。4.4 数据库索引又慢又不起眼的优化性能问题排查到最后常常会发现卡在数据库索引上。按category过滤商品但category外键没建索引就会全表扫描。给高频过滤和排序字段加索引是最便宜也最有效的优化手段class Product(models.Model): category models.ForeignKey(Category, on_deletemodels.PROTECT, related_nameproducts, db_indexTrue) is_active models.BooleanField(defaultTrue, db_indexTrue) created_at models.DateTimeField(auto_now_addTrue, db_indexTrue)注意频繁更新的字段不适合盲加索引因为每次写入都要更新索引树写多读少的场景反而会拖慢性能。is_active这种区分度低的字段单独建索引效果一般但它常和其他字段组合查询可以建联合索引。Django不支持直接在字段定义里建联合索引要在模型的Meta里用indexes定义class Meta: indexes [ models.Index(fields[category, is_active], nameidx_cat_active), ]5. 第三方API的协作困境与容错设计5.1 大模型API时代的集成常态这两年业务系统接入第三方API已经成为常态。尤其是大模型API像DeepSeek、智谱、通义千问这些各家都提供了HTTP接口业务方通过API调用量计费。DRF项目中接入这类第三方API核心痛点不是“调不通”而是“如何优雅地调”。我遇到过的真实案例一个智能客服项目用户在网页端提问Django后端要调用大模型API拿回答。大模型API的响应时间不稳定快则两秒慢则三十秒。如果直接在DRF视图里同步等待整个请求链路被拖住前端连接一直挂着网关超时之后用户看到的就是“服务器错误”。正确做法是异步任务。把调用大模型API的任务丢给Celery视图立刻返回“任务已接收正在处理中”前端轮询任务状态接口拿到结果后再展示。这样用户体验顺滑后端也不会被慢API拖死。5.2 超时、重试与熔断容错三件套第三方API不可能永远可靠我自己在项目里总结了一套容错策略核心就是超时、重试、熔断。超时必须显式设置。Python的requests库默认没有超时时间一旦服务端不响应请求会一直挂在那里挤占线程池。调用任何第三方API都要写import requests resp requests.post(api_url, jsonpayload, timeout(5, 15))这里的(5, 15)分别代表连接超时和读取超时。连接超时5秒读取超时15秒够用又不至于让用户等太久。重试要注意幂等性。查询类接口重试没风险但创建订单、扣减库存这类写操作重试可能导致重复扣款。常见的策略是让上游生成一个幂等键下游根据幂等键判断是否已经处理过。大模型API的调用一般可以安全重试因为它的副作用是消耗token不会产生脏数据。熔断是防止雪崩的关键。如果第三方API已经连续报错5分钟继续调用只会加重对方负载自己也得不到好的结果。用一个简单的计数器失败率达到阈值就直接放行降级方案——比如给出预设的兜底回复过一段时间再尝试恢复。我在DRF项目里用Redis原子计数器实现过一个简单的熔断器代码不复杂但上线后效果立竿见影。5.3 错误码的标准化与排查链路第三方API的错误信息五花八门有的用英文报错有的用自定义错误码有的干脆只返回一个HTTP状态码加空body。作为接入方我们必须在自己的API层做一次“翻译”把第三方错误映射成内部统一的错误结构。我的做法是在DRF项目里维护一个统一的异常映射模块class ThirdPartyAPIError(Exception): def __init__(self, provider, code, message): self.provider provider self.code code self.message message def translate_error(exc): if exc.provider deepseek: code_map { rate_limit: 上游请求过于频繁请稍后再试, insufficient_quota: 上游配额不足, timeout: 上游响应超时, } message code_map.get(exc.code, exc.message) else: message exc.message return message日常排查第三方API问题时日志打全最重要。DRF项目里我用logging模块记录每一次第三方调用的时间、入参、出参、耗时、错误信息尤其在刚接入大模型API的调试期这些日志是定位问题的唯一线索。6. 生产环境的常见坑与排查链路6.1 认证失效JWT过期与用户状态不一致生产环境里最隐蔽的坑之一JWT过期时间的宽容度。很多人配置JWT时把过期时间设成24小时但对那些改了密码、被管理员封禁、或者被踢下线的用户旧Token仍然有效这就有安全隐患。我给出的建议是JWT的过期时间按业务风险来定。后台管理系统建议15分钟到1小时前台C端建议7天配合RefreshToken自动续期。在Django的项目中间件里检查用户状态发现被封禁直接返回401让前端强制跳登录。6.2 序列化器字段陷阱部分更新与脏数据部分更新是DRF的一个高频坑。前端传几个字段来做PATCH请求但序列化器如果没有正确处理可能把没传的字段覆盖成默认值。解决办法有两个要么视图里用partialTrue调用序列化器要么前端把整个资源的状态都传过来做全量更新。class OrderViewSet(ModelViewSet): def partial_update(self, request, *args, **kwargs): kwargs[partial] True return super().partial_update(request, *args, **kwargs)这个坑我在一个客户项目里真实踩过运营人员修改订单备注前端只传了remark字段结果后端序列化器把status、amount这些没传的字段全部重置为默认值订单金额变成了0差点造成一次资损事故。后来我立了一条规矩涉及金额、状态等核心字段的接口一律不接受部分更新要么全量提交要么用专门的action端点做单字段更新。6.3 权限配置不当越权访问的排查思路越权问题排查起来比认证失效更痛苦因为用户能正常登录、接口也能正常返回只是返回的数据不该他看。这类问题的根因通常是对象级权限没做。列表接口只做了“登录用户可访问”但用户A能查到用户B的订单因为列表查询集没有按user过滤。排查链路可以按这几步走先看视图类的queryset是否按当前用户过滤再看自定义权限类是否在has_object_permission里校验了对象归属最后检查get_queryset里的过滤条件是否真的生效。class OrderViewSet(ModelViewSet): serializer_class OrderSerializer permission_classes [IsAuthenticated, IsOwnerOrReadOnly] def get_queryset(self): return Order.objects.filter(userself.request.user)这里的get_queryset和IsOwnerOrReadOnly是双重保险。理论上get_queryset已经保证只能看到自己的订单但万一某个action里用了get_object()拿对象绕过查询集过滤权限类会做第二层校验。6.4 遇到“permission denied while trying to connect to the docker api”这类错误很多DRF项目部署在Docker容器里开发时遇到过“permission denied while trying to connect to the docker api at unix:///var/run/docker.sock”这类错误虽然不是DRF本身的问题但也是API开发环境的一部分。这类问题常见于当前用户不在docker组里或者socket文件权限不对。解决办法很简单把当前用户加进docker组或者用sudo运行docker命令然后重新登录。在开发DRF项目时如果用的还是Docker Compose管理整套服务Django PostgreSQL Redis这类权限问题会反复出现。我的建议是开发环境尽量统一用Dev Container或者VSCode的Remote Development方案避免人人都要配一遍本机环境。7. 实测心得与进阶建议最后说说我这两年用DRF的真实感受。框架本身的稳定性没话说从1.8版本用到现在的3.15十几年过去了核心机制没有大变化说明早期设计确实经得起考验。但DRF的学习曲线比FastAPI陡一些尤其是序列化器嵌套、权限体系、GenericViewSet这些概念新手很容易一上来就绕晕。我建议新人的学习路径是先写一遍原生Django的JSONResponse视图理解“数据从模型到JSON”这条路有多笨重然后再引入DRF的序列化器你会立刻感受到它的价值。还有一个建议DRF的Browsable API功能很多团队上线前忘了关。这个功能在Debug阶段是神器浏览器里直接能调试接口、看JSON渲染、甚至用表单提交测试数据。我习惯把它当作“API的自动化文档”来用写接口的同时文档也同步更新跟前端对接时不用额外维护一份Markdown文档。不过上线前一定记得在settings里区分环境if DEBUG: REST_FRAMEWORK[DEFAULT_RENDERER_CLASSES] [ rest_framework.renderers.BrowsableAPIRenderer, rest_framework.renderers.JSONRenderer, ] else: REST_FRAMEWORK[DEFAULT_RENDERER_CLASSES] [ rest_framework.renderers.JSONRenderer, ]不然生产环境的接口地址被扫到浏览器打开就是一套交互式调试面板虽然不直接导致安全问题总归是暴露了接口的更多细节。再分享一个我自己的小习惯写DRF序列化器时字段命名尽量“所见即所得”。前端要什么字段序列化器就输出什么字段。宁可多写几行source映射也不要做“字段名翻译”这种中间层。因为中间层一旦出现前后端就开始扯皮最后谁都说不清某个字段到底叫什么。DRF的未来我不做预测但有一点是可以肯定的只要Django还活着DRF就会一直是Python API开发的主力选手。把这套框架吃透了你写API的习惯、对数据流的态度、对容错和权限的认知都会整体上一个台阶。这些能力不绑定在某个框架上换到任何技术栈都值钱。
返回列表