如何在ModelViewSet中限制非超级用户的ManyToManyField访问?
问题原因
当前代码中,非超级用户的站点过滤逻辑是正确的(只返回包含用户有权限设备的站点),但prefetch_related("devices")会预取该站点下所有设备,而非用户有权限的设备。因此序列化时会把站点下全部设备返回,导致用户看到未授权的设备。
解决方案
方法1:使用Prefetch精准预取授权设备(推荐,性能最优)
通过django.db.models.Prefetch对象,指定预取设备时只包含当前用户有权限的设备,避免冗余数据。
修改视图集
from django.db.models import Prefetch class SiteViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = (permissions.IsAuthenticated,) serializer_class = SiteSerializer def get_queryset(self): user = self.request.user if user.is_superuser: return Site.objects.all().prefetch_related("devices") else: # 定义预取规则:只预取当前用户有权限的设备 authorized_devices_prefetch = Prefetch( 'devices', queryset=Device.objects.filter(users=user), to_attr='authorized_devices' # 存储到自定义属性中 ) return Site.objects.filter( devices__users=user ).prefetch_related(authorized_devices_prefetch).distinct()
修改序列化器
将序列化器中的devices字段指向预取的authorized_devices属性:
class SiteSerializer(serializers.ModelSerializer): # 用source指定预取的自定义属性 devices = SiteDeviceSerializer(source='authorized_devices', many=True, read_only=True) class Meta: model = Site fields = "__all__"
方法2:在序列化器中过滤设备
通过SerializerMethodField在序列化阶段过滤出用户有权限的设备,适合快速修改场景,但性能略逊于方法1(若未优化可能产生N+1查询)。
修改序列化器
class SiteSerializer(serializers.ModelSerializer): devices = serializers.SerializerMethodField() def get_devices(self, obj): user = self.context['request'].user if user.is_superuser: target_devices = obj.devices.all() else: # 过滤出当前用户有权限的设备 target_devices = obj.devices.filter(users=user) return SiteDeviceSerializer(target_devices, many=True).data class Meta: model = Site fields = "__all__"
优化性能(可选)
为避免N+1查询,视图集的get_queryset中仍需保留prefetch_related("devices"),让序列化时直接使用预取的内存数据:
def get_queryset(self): user = self.request.user if user.is_superuser: return Site.objects.all().prefetch_related("devices") else: return Site.objects.filter( devices__users=user ).prefetch_related("devices").distinct()
内容的提问来源于stack exchange,提问作者darkvodka
相关产品推荐
相关产品推荐

