Django Rest Framework如何高效优化数据序列化性能
Django DRF N+1优化方案
关于select_related的生效逻辑
select_related/prefetch_related是Django ORM层的原生缓存能力,和DRF没有耦合:只要查询时预加载的关联路径,和后续代码(包括序列化器)访问关联对象的路径完全匹配,ORM就会直接读取预加载的缓存数据,不会重复发起数据库查询。- 该操作必然带来性能提升,配置后无明显效果基本都是关联路径写漏、写错,或者序列化器访问的关联路径和预加载路径不匹配导致的,比如只预加载了一级关联
bar,但序列化时访问了二级关联bar.element,还是会触发N+1查询。
最高效的字段返回实现方案
首先先修正模型代码里的拼写错误,否则代码无法正常运行:max_lenth改为max_length、IntergerField改为IntegerField、ForeingKey/ForeingField改为ForeignKey。
1. 调整ViewSet查询集,一次性预加载全路径关联
用到的所有关联都是外键/一对一类型,全部用select_related在单次SQL中JOIN完成预加载即可,不需要用prefetch_related:
class FooViewSet(ModelViewSet): serializer_class = FooSerializer permission_classes = [IsAuthenticated] authentication_classes = [JWTAuthentication] http_method_names = ['get', 'post', 'patch'] def get_queryset(self): active = self.request.query_params.get('is_active', False) name = self.request.query_params.get('name', '') filter_kwargs = {'is_active': active} if name == 'Fire': filter_kwargs['bar__element__name'] = name # 预加载全层级关联:bar关联的element、customer关联的user queryset = Foo.objects.filter(**filter_kwargs).select_related( 'bar__element', 'customer__user' ) return queryset
2. 精简序列化器,直接取需要的字段
去掉多余的嵌套序列化器,通过source参数直接读取关联路径上的目标字段,同时避免用fields = '__all__'返回多余字段,减少序列化开销:
class UserSerializer(ModelSerializer): class Meta: model = User # 显式列出需要返回的字段,禁止用__all__ fields = ['id', 'name', 'age'] class FooSerializer(ModelSerializer): # 直接映射深层关联字段,不需要嵌套Bar、Element序列化器 element_name = serializers.CharField(source='bar.element.name', read_only=True) element_text = serializers.CharField(source='bar.element.text', read_only=True) user = UserSerializer(source='customer.user', read_only=True) class Meta: model = Foo # 显式列出返回字段 fields = ['id', 'is_active', 'user', 'element_name', 'element_text']
优化验证方式
在DEBUG模式下开启Django数据库查询日志,统计单接口的SQL执行数量:优化完成后,不管返回多少条Foo数据,SQL查询数会稳定在2-3条,不会随返回数据条数线性增长,N+1问题完全解决。
注意:不要在模型层写
user()、element_name()这类方法封装关联取值,序列化器的source参数原生支持跨关联路径取字段,逻辑更透明,也不会破坏ORM预加载缓存的命中逻辑。
内容的提问来源于stack exchange,提问作者Humberto Vieira
相关产品推荐
相关产品推荐

