Django REST Framework嵌套序列化器中子序列化器如何使用预取查询集
问题原因与解决方案
报错根因
- 你当前的写法
user.emailaddress_set.filter(verified=1).count()没有命中prefetch_related预取的缓存数据,会触发新的SQL查询,部分数据库(如PostgreSQL)不支持直接对布尔字段执行此类聚合查询,所以抛出function max(boolean) does not exist错误。 - 只要调用
filter、count这类QuerySet方法,就会绕开已经预取到内存的数据集,直接请求数据库,完全浪费了预取的性能优化效果。
正确实现(命中预取缓存)
直接操作已经加载到内存的关联数据集,不触发新的数据库查询,修改UserDetailSerializer中的get_is_verified方法即可:
def get_is_verified(self, user): # 直接遍历预取到内存的所有邮箱对象,不触发新查询 return any(email.verified for email in user.emailaddress_set.all())
进阶优化(减少预取数据量)
如果只需要判断是否存在已验证邮箱,可以在预取阶段就过滤数据,进一步降低内存开销:
- 修改视图层的查询集逻辑,使用
Prefetch自定义预取规则:
from django.db.models import Prefetch class PlaylistListView(generics.ListAPIView): serializer_class = PlaylistSerializer def get_queryset(self): return ( Playlist.objects.all() # user是单对一外键,用select_related比prefetch_related性能更好 .select_related("user") .prefetch_related( Prefetch( "user__emailaddress_set", # 预取阶段只过滤已验证的邮箱 queryset=EmailAddress.objects.filter(verified=True), # 自定义属性名存储预取结果 to_attr="verified_emails" ) ) )
- 序列化层直接判断自定义预取属性的长度即可:
def get_is_verified(self, user): return len(user.verified_emails) > 0
内容的提问来源于stack exchange,提问作者Jvn。
相关产品推荐
相关产品推荐

