DRF嵌套序列化器中获取父模型ID解决多对多关联查询错误
解决方案
方案1:通过上下文传递父模型ID
这是最直接的处理方式,在父序列化器嵌套子序列化器时,把当前House的ID传入子序列化器的上下文,子序列化器就能拿到这个ID做精准查询,避免多结果异常。
代码示例:
先修改父序列化器(比如HouseSerializer),嵌套子序列化器时传递上下文:
class HouseSerializer(serializers.ModelSerializer): users = UserBasicInfoSerializer(many=True, context={'house_id': self.instance.id}) class Meta: model = House fields = ['id', 'name', 'users']
再在子序列化器UserBasicInfoSerializer中,从上下文取ID查询中间表:
class UserBasicInfoSerializer(serializers.ModelSerializer): is_admin = serializers.SerializerMethodField() class Meta: model = CustomUser fields = ['id', 'username', 'is_admin'] def get_is_admin(self, obj): house_id = self.context.get('house_id') if not house_id: return False # 无ID时根据业务需求返回默认值 try: user_housing = UserHousing.objects.get(habitant=obj, house_id=house_id) return user_housing.isAdmin except UserHousing.DoesNotExist: return False # 用户未关联该房屋时返回False
方案2:预取关联数据,在父序列化器中处理管理员状态
这个方案能避免N+1查询问题,同时把中间表的isAdmin字段直接传递给子序列化器,不用子序列化器自己查数据库。
代码示例:
首先在视图查询House时,预取关联的用户和中间表数据:
from django.db.models import Prefetch # 视图中查询House的逻辑 houses = House.objects.prefetch_related( Prefetch( 'users', queryset=CustomUser.objects.prefetch_related('userhousing_set'), to_attr='prefetched_users' ) )
然后修改父序列化器,用SerializerMethodField处理用户数据,把管理员状态附加进去:
class HouseSerializer(serializers.ModelSerializer): users = serializers.SerializerMethodField() class Meta: model = House fields = ['id', 'name', 'users'] def get_users(self, obj): user_data = [] for user in obj.prefetched_users: # 找到当前用户与该房屋的关联记录 user_housing = next((uh for uh in user.userhousing_set.all() if uh.house == obj), None) is_admin = user_housing.isAdmin if user_housing else False # 序列化用户数据并添加管理员字段 serializer = UserBasicInfoSerializer(user) data = serializer.data data['is_admin'] = is_admin user_data.append(data) return user_data
此时子序列化器UserBasicInfoSerializer只需负责序列化用户基本字段,不用再写get_is_admin方法。
方案3:通过父序列化器实例获取当前House
如果是单个House的详情序列化场景,可以直接通过子序列化器的self.parent.instance拿到当前House对象,再做反向查询:
class UserBasicInfoSerializer(serializers.ModelSerializer): is_admin = serializers.SerializerMethodField() class Meta: model = CustomUser fields = ['id', 'username', 'is_admin'] def get_is_admin(self, obj): house = self.parent.instance if not house: return False try: user_housing = obj.userhousing_set.get(house=house) return user_housing.isAdmin except UserHousing.DoesNotExist: return False
注意:这种方式只适合单个House的序列化场景,如果父序列化器处理的是QuerySet(列表视图),self.parent.instance会是集合,需要额外做遍历处理,实用性不如前两种方案。
内容的提问来源于stack exchange,提问作者SaFteiNZz
相关产品推荐
相关产品推荐

