簡體   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