我正在使用Django REST Framework来序列化Django模型。我有一个ListCreateAPIView视图列出对象和一个RetrieveUpdateDestroyAPIView视图来检索/更新/删除单个对象。该模型存储用户自己提交的信息。他们提交的信息包含一些私人信息和一些公共信息。我希望所有用户能够列出和检索公共信息,但我只希望所有者列出/检索/更新/删除私人信息。因此,我需要每个字段的权限而不是对象权限。
我发现最接近的建议是https://groups.google.com/forum/#!topic/django-rest-framework/FUd27n_k3U0,它会根据请求类型更改序列化程序。这对我的情况不起作用,因为我当时没有查询集或对象来确定它是否归用户所有。
当然,我的前端隐藏了私人信息,但聪明的人仍然可以窥探API请求以获取完整的对象。如果需要代码,我可以提供它,但我的请求适用于vanilla Django REST Framework设计。
答案 0 :(得分:34)
如何基于用户切换序列化程序类?
在文档中:
http://www.django-rest-framework.org/api-guide/generic-views/#get_serializer_classself
def get_serializer_class(self):
if self.request.user.is_staff:
return FullAccountSerializer
return BasicAccountSerializer
答案 1 :(得分:13)
前几天我遇到了类似的问题。这是我的方法:
DRF 2.4
解决方案。class PrivateField(serializers.Field):
def field_to_native(self, obj, field_name):
"""
Return null value if request has no access to that field
"""
if obj.created_by == self.context.get('request').user:
return super(PrivateField, self).field_to_native(obj, field_name)
return None
#Usage
class UserInfoSerializer(serializers.ModelSerializer):
private_field1 = PrivateField()
private_field2 = PrivateField()
class Meta:
model = UserInfo
class PrivateField(serializers.ReadOnlyField):
def get_attribute(self, instance):
"""
Given the *outgoing* object instance, return the primitive value
that should be used for this field.
"""
if instance.created_by == self.context['request'].user:
return super(PrivateField, self).get_attribute(instance)
return None
这次我们仅ReadOnlyField
扩展,因为to_representation
类中未实现serializers.Field
。
答案 2 :(得分:5)
我想出了办法。在序列化程序中,我可以访问对象和发出API请求的用户。因此,我可以检查请求者是否是对象的所有者并返回私人信息。如果不是,序列化程序将返回一个空字符串。
class UserInfoSerializer(serializers.HyperlinkedModelSerializer):
private_field1 = serializers.SerializerMethodField('get_private_field1')
class Meta:
model = UserInfo
fields = (
'id',
'public_field1',
'public_field2',
'private_field1',
)
read_only_fields = ('id')
def get_private_field1(self, obj):
# obj.created_by is the foreign key to the user model
if obj.created_by != self.context['request'].user:
return ""
else:
return obj.private_field1
答案 3 :(得分:4)
下面:
- models.py:
class Article(models.Model):
name = models.CharField(max_length=50, blank=False)
author = models.CharField(max_length=50, blank=True)
def __str__(self):
return u"%s" % self.name
class Meta:
permissions = (
# name
('read_name_article', "Read article's name"),
('change_name_article', "Change article's name"),
# author
('read_author_article', "Read article's author"),
('change_author_article', "Change article's author"),
)
- serializers.py:
class ArticleSerializer(serializers.ModelSerializer):
class Meta(object):
model = Article
fields = "__all__"
def to_representation(self, request_data):
# get the original representation
ret = super(ArticleSerializer, self).to_representation(request_data)
current_user = self.context['request'].user
for field_name, field_value in sorted(ret.items()):
if not current_user.has_perm(
'app_name.read_{}_article'.format(field_name)
):
ret.pop(field_name) # remove field if it's not permitted
return ret
def to_internal_value(self, request_data):
errors = {}
# get the original representation
ret = super(ArticleSerializer, self).to_internal_value(request_data)
current_user = self.context['request'].user
for field_name, field_value in sorted(ret.items()):
if field_value and not current_user.has_perm(
'app_name.change_{}_article'.format(field_name)
):
errors[field_name] = ["Field not allowed to change"] # throw error if it's not permitted
if errors:
raise ValidationError(errors)
return ret
答案 4 :(得分:2)
对于允许读写的解决方案,请执行以下操作:
class PrivateField(serializers.Field):
def get_attribute(self, obj):
# We pass the object instance onto `to_representation`,
# not just the field attribute.
return obj
def to_representation(self, obj):
# for read functionality
if obj.created_by != self.context['request'].user:
return ""
else:
return obj.private_field1
def to_internal_value(self, data):
# for write functionality
# check if data is valid and if not raise ValidationError
class UserInfoSerializer(serializers.HyperlinkedModelSerializer):
private_field1 = PrivateField()
...
有关示例,请参阅docs。
答案 5 :(得分:2)
如果您只执行READ操作,则只需弹出序列化程序的to_representation方法中的字段。
def to_representation(self,instance):
ret = super(YourSerializer,self).to_representation(instance)
fields_to_pop = ['field1','field2','field3']
if instance.created_by != self.context['request'].user.id:
[ret.pop(field,'') for field in fields_to_pop]
return ret
这足以隐藏敏感字段。
答案 6 :(得分:1)
这是一个老问题,但主题仍然相关。
DRF 建议为不同的权限创建不同的序列化程序。但这种方法仅适用于只有少数权限或组的情况。
restframework-serializer-permissions 是 drf 序列化程序的替代品。 您不是从 drf 导入序列化程序和字段,而是从 serializer_permissions 导入它们。
安装:
$ pip install restframework-serializer-permissions
示例序列化程序:
# import permissions from rest_framework
from rest_framework.permissions import AllowAny, IsAuthenticated
# import serializers from serializer_permissions instead of rest_framework
from serializer_permissions import serializers
# import you models
from myproject.models import ShoppingItem, ShoppingList
class ShoppingItemSerializer(serializers.ModelSerializer):
item_name = serializers.CharField()
class Meta:
# metaclass as described in drf docs
model = ShoppingItem
fields = ('item_name', )
class ShoppingListSerializer(serializers.ModelSerializer):
# Allow all users to list name
list_name = serializers.CharField(permission_classes=(AllowAny, ))
# Only allow authenticated users to retrieve the comment
list_comment = serializers.CharField(permissions=(IsAuthenticated, ))
# show owner only, when the current user has 'auth.view_user' permission
owner = serializers.CharField(permissions=('auth.view_user', ), hide=True)
# serializer which is only available, when the user is authenticated
items = ShoppingItemSerializer(many=True, permissions=(IsAuthenticated, ), hide=True)
class Meta:
# metaclass as described in drf docs
model = ShoppingItem
fields = ('list_name', 'list_comment', 'owner', 'items', )
披露:我是此扩展程序的作者
答案 7 :(得分:0)
只需分享另一个可能的解决方案
例如,使电子邮件仅显示给自己。
在UserSerializer上,添加:
email = serializers.SerializerMethodField('get_user_email')
然后像这样实现get_user_email:
def get_user_email(self, obj):
user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
user = request.user
return obj.email if user.id == obj.pk else 'HIDDEN'