Django REST Framework按当前用户限制外键选择的问题求助
问题描述
我查了好多资料、试了好几种方法都没用,试过限制外键可选范围和用CurrentUserDefault。我的需求是根据当前用户限制provider的可选范围,毕竟每个用户都有自己的provider。写了下面的Serializer和View代码后,运行报错:
TypeError: Field 'id' expected a number but got CurrentUserDefault(),
Serializer代码
class RideSerializer(serializers.ModelSerializer): invoice = InvoiceSerializer(required=False) duration = serializers.ReadOnlyField() riding_duration = serializers.ReadOnlyField() heading_duration = serializers.ReadOnlyField() earning_per_km = serializers.ReadOnlyField() earning_per_minute = serializers.ReadOnlyField() provider = serializers.PrimaryKeyRelatedField( queryset=User.objects.select_related( 'driver_profile__team', 'company_profile__team').filter( Q(driver_profile__team__user=serializers.CurrentUserDefault()) | Q(Company_profile__team__user=serializers.CurrentUserDefault())), default=serializers.CurrentUserDefault()) class Meta: model = Ride exclude = ['shift', ] read_only_fields = ['id', 'start_time', ]
View代码
class RideView( viewsets.GenericViewSet, mixins.UpdateModelMixin, mixins.RetrieveModelMixin, mixins.ListModelMixin, mixins.CreateModelMixin,): permission_classes = [IsDriver, ] serializer_class = RideSerializer http_method_names = ['post', 'patch', 'get'] def get_queryset(self): return super().get_queryset().select_related( 'shift', "invoice").filter(shift__driver=self.request.user.driver_profile) def create(self, request, *args, **kwargs): self.serializer = self.get_serializer( data=request.data, context={'request': request}) return super().create(request, *args, **kwargs)
解决方案
报错原因
CurrentUserDefault()是用来给字段设置默认值的工具,不能直接塞到查询集的filter条件里。序列化器初始化查询集时,还未获取到请求上下文,无法将CurrentUserDefault()解析为当前用户对象,导致把它当成普通实例传入,自然触发类型错误。
修改步骤
要动态获取当前用户过滤provider范围,我们可以自定义PrimaryKeyRelatedField,重写它的get_queryset方法来访问请求上下文:
- 编写自定义字段类
from rest_framework import serializers from django.db.models import Q class UserFilteredPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): def get_queryset(self): # 从上下文获取当前用户 user = self.context['request'].user queryset = super().get_queryset() # 根据当前用户过滤provider可选范围 return queryset.select_related( 'driver_profile__team', 'company_profile__team' ).filter( Q(driver_profile__team__user=user) | Q(company_profile__team__user=user) # 原代码的Company_profile需改为小写,符合Django字段命名规范 )
- 更新RideSerializer中的provider字段
class RideSerializer(serializers.ModelSerializer): invoice = InvoiceSerializer(required=False) duration = serializers.ReadOnlyField() riding_duration = serializers.ReadOnlyField() heading_duration = serializers.ReadOnlyField() earning_per_km = serializers.ReadOnlyField() earning_per_minute = serializers.ReadOnlyField() # 使用自定义字段类,基础查询集设为User.objects.all(),后续动态过滤 provider = UserFilteredPrimaryKeyRelatedField( queryset=User.objects.all(), default=serializers.CurrentUserDefault() ) class Meta: model = Ride exclude = ['shift', ] read_only_fields = ['id', 'start_time', ]
- 额外注意
- 原代码中
Q(Company_profile__team__user=...)的Company_profile需改为小写company_profile,否则Django会提示字段不存在。 - 你的View中create方法已将request传入上下文,其他方法(如update)GenericViewSet会默认处理,无需额外修改。
内容的提问来源于stack exchange,提问作者usama Shehab
相关产品推荐
相关产品推荐

