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

时间:2015-10-18 18:32:58

标签: python django django-rest-framework

我正在开发一个具有一些社交功能的项目,并且需要做到这一点,以便用户可以查看他的个人资料的所有详细信息,但只能查看其他人的公开部分'配置文件。
有没有办法在一个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在查询集中使用FullProfileSerializer序列化自己的个人资料,但其余的使用BasicProfileSerializer
这是否可以使用DRF的API?

2 个答案:

答案 0 :(得分:1)

我们可以覆盖retrieve()中的listProfileViewSet方法,以根据所查看的用户返回不同的序列化数据。

list方法中,我们使用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

答案 1 :(得分:0)

我设法破解了为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视图,我非常感谢你的答案。