繁体   English   中英

在一个ViewSet中基于每个对象更改序列化程序?

[英]Change serializers on per-object basis within one ViewSet?

我正在开发一个具有一些社交功能的项目,需要制作它以便用户可以查看他的个人资料的所有详细信息,但只能查看其他人个人资料的公开部分。
有没有办法在一个ViewSet中执行此操作?

这是我的模型示例:

class Profile(TimestampedModel):
    user = models.OneToOneField(User)

    nickname = models.CharField(max_length=255)
    sex = models.CharField(
        max_length=1, default='M',
        choices=(('M', 'Male'), ('F', 'Female')))

    birthday = models.DateField(blank=True, null=True)

对于这个模型,我希望生日,例如,保持私密。
在实际模型中,有大约十几个这样的领域。

我的序列化器:

class FullProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = Profile


class BasicProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = Profile
        fields = read_only_fields = ('nickname', 'sex', 'birthday')

我写的自定义权限:

class ProfilePermission(permissions.BasePermission):
    """
    Handles permissions for users.  The basic rules are

    - owner and staff may do anything
    - others can only GET
    """

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        else:
            return request.user == obj.user or request.user.is_staff

我的看法:

class RUViewSet(
        mixins.RetrieveModelMixin, mixins.UpdateModelMixin,
        mixins.ListModelMixin, viewsets.GenericViewSet):
    """ViewSet with update/retrieve powers."""


class ProfileViewSet(RUViewSet):
    model = Profile
    queryset = Profile.objects.all()
    permission_classes = (IsAuthenticated, ProfilePermission)

    def get_serializer_class(self):
        user = self.request.user
        if user.is_staff:
            return FullProfileSerializer
        return BasicProfileSerializer

我想要的是request.user在queryset中使用FullProfileSerializer序列化自己的配置文件,但其余的使用BasicProfileSerializer
这是否可以使用DRF的API?

我们可以覆盖我们的ProfileViewSetretrieve()list方法,以根据被查看的用户返回不同的序列化数据。

list方法中,我们使用get_serializer_class()方法返回的序列化程序序列get_serializer_class()当前用户之外的所有用户实例。 然后我们显式地使用FullProfileSerializer序列化当前用户配置文件信息,并将此序列化数据添加到之前返回的数据中。

retrieve方法中,我们在视图上设置accessed_profile属性以了解视图正在显示的用户。 然后,我们将使用此属性来决定get_serializer_class()方法中的序列化程序。

class ProfileViewSet(RUViewSet):
    model = Profile
    queryset = Profile.objects.all()
    permission_classes = (IsAuthenticated, ProfilePermission)

    def list(self, request, *args, **kwargs):
        instance = self.filter_queryset(self.get_queryset()).exclude(user=self.request.user)
        page = self.paginate_queryset(instance)
        if page is not None:
            serializer = self.get_pagination_serializer(page)
        else:
            serializer = self.get_serializer(instance, many=True)
        other_profiles_data = serializer.data # serialized profiles data for users other than current user
        current_user_profile = <get_the_current_user_profile_object>
        current_user_profile_data = FullProfileSerializer(current_user_profile).data
        all_profiles_data = other_profiles_data.append(current_user_profile_data)
        return Response(all_profiles_data)

    def retrieve(self, request, *args, **kwargs):
        self.accessed_profile = self.get_object() # set this as on attribute on the view
        serializer = self.get_serializer(self.accessed_profile)
        return Response(serializer.data)

    def get_serializer_class(self):
        current_user = self.request.user
        if current_user.is_staff or (self.action=='retrieve' and self.accessed_profile.user==current_user):
            return FullProfileSerializer    
        return BasicProfileSerializer

我设法破解了为detail视图提供所需行为的解决方案:

class ProfileViewSet(RUViewSet):
    model = Profile
    queryset = Profile.objects.all()
    permission_classes = (IsAuthenticated, ProfilePermission)

    def get_serializer_class(self):
        user = self.request.user
        if user.is_staff:
            return FullProfileSerializer
        return BasicProfileSerializer

    def get_serializer(self, instance=None, *args, **kwargs):

        if hasattr(instance, 'user'):
            user = self.request.user

            if instance.user == user or user.is_staff:
                kwargs['instance'] = instance
                kwargs['context'] = self.get_serializer_context()
                return FullProfileSerializer(*args, **kwargs)

        return super(ProfileViewSet, self).get_serializer(
            instance, *args, **kwargs)

但是,这不适用于list视图,因为它为get_serializer方法提供了一个Django Queryset对象来代替实际的实例。
我仍然希望在list视图中看到这种行为,即在序列化许多对象时,如果有人知道更优雅的方式来执行此操作,这也涵盖了list视图,我非常感谢您的回答。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM