Django+DRF如何正确获取中间模型对应当前用户的关联对象
问题原因
- 你定义的
user_skills是Skill模型指向UserSkill的一对多反向关联,默认序列化该字段时,会返回当前Skill关联的所有UserSkill记录,不会自动按当前请求用户做过滤,因此会带出其他用户的关联数据。 - 你的查询代码存在拼写错误:跨关联查询字段应使用双下划线连接关联名与字段名,
user_skills_user__disabled的正确写法为user_skills__disabled,原写法会触发字段不存在的报错。
正确实现方案
通过Django ORM提供的Prefetch对象提前过滤反向关联的数据集,同时修改序列化器返回过滤后的单条中间表记录,全程不会产生额外SQL性能损耗,也不会返回无关数据。
1. 修改ViewSet查询逻辑
from django.db.models import Prefetch from rest_framework import viewsets, permissions from .models import Skill, UserSkill from .serializers import AssignedSkillSerializer class AssignedSkillViewSet(viewsets.ModelViewSet): serializer_class = AssignedSkillSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): current_user = self.request.user # 定义过滤规则:只取当前用户、未禁用的中间表记录 matched_user_skill_qs = UserSkill.objects.filter( user=current_user, disabled=False ) return Skill.objects.filter( user_skills__user=current_user, user_skills__disabled=False # 预加载过滤后的中间表数据,挂载到自定义属性上,避免返回全量关联记录 ).prefetch_related( Prefetch( 'user_skills', queryset=matched_user_skill_qs, to_attr='current_user_skill' ) ).distinct()
说明:因为查询Skill时已经加了「关联当前用户、未禁用的UserSkill」的过滤条件,每个返回的Skill对象对应的
current_user_skill属性中,只会存在0或1条匹配的UserSkill记录,不会出现多条结果。
2. 修改序列化器
先定义中间模型的精简序列化器,再替换原来全量返回的user_skills字段:
from rest_framework import serializers from .models import Skill, UserSkill # 中间模型序列化器,按需配置返回字段即可 class UserSkillBriefSerializer(serializers.ModelSerializer): class Meta: model = UserSkill fields = ['id', 'disabled'] # 可自行添加需要返回的UserSkill字段 class AssignedSkillSerializer(serializers.HyperlinkedModelSerializer): # 直接返回预加载好的当前用户对应的中间表记录 current_user_skill = UserSkillBriefSerializer(read_only=True) class Meta: model = Skill fields = [ 'id', 'url', 'title', 'description', 'current_user_skill', ]
方案优势
- 不会返回其他用户的关联数据,完全符合需求
- 借助
prefetch_related避免了N+1查询问题,性能和原生关联查询一致 - 不需要对返回列表做取下标、排序等不可靠操作,结果稳定
内容的提问来源于stack exchange,提问作者Andrés Root
相关产品推荐
相关产品推荐

