[英]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?
我們可以覆蓋我們的ProfileViewSet
的retrieve()
和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.