42

我正在使用 Django REST Framework 来序列化 Django 模型。我有一个 ListCreateAPIView 视图来列出对象和一个 RetrieveUpdateDestroyAPIView 视图来检索/更新/删除单个对象。该模型存储用户自己提交的信息。他们提交的信息包含一些私人信息和一些公共信息。我希望所有用户都能够列出和检索公共信息,但我只希望所有者列出/检索/更新/删除私人信息。因此,我需要每个字段的权限而不是对象权限。

我发现的最接近的建议是https://groups.google.com/forum/#!topic/django-rest-framework/FUd27n_k3U0,它根据请求类型更改序列化程序。这不适用于我的情况,因为那时我没有查询集或对象来确定它是否归用户所有。

当然,我的前端隐藏了私人信息,但聪明的人仍然可以窥探 API 请求以获取完整的对象。如果需要代码,我可以提供,但我的请求适用于原版 Django REST 框架设计。

4

8 回答 8

61

如何根据用户切换序列化程序类?

在文档中:

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
于 2015-12-27T23:50:05.933 回答
15

前几天我也遇到了类似的问题。这是我的方法:

这是一个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

还有一个 DRF 3.x 解决方案:

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类中实现。

于 2014-09-25T19:33:10.167 回答
6

我想出了一个办法。在序列化程序中,我可以访问对象和发出 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
于 2013-10-02T21:43:39.990 回答
6

这里:

-- 模型.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"),
        )

-- 序列化器.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
于 2018-03-12T02:42:24.943 回答
3

对于允许读写的解决方案,请执行以下操作:

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()
    ...

有关示例,请参阅文档。

于 2015-08-30T23:16:45.047 回答
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

这应该足以隐藏敏感字段。

于 2016-12-09T11:27:18.797 回答
2

只是分享另一个可能的解决方案

例如,让电子邮件只显示给自己。

在 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'
于 2019-06-27T02:22:36.980 回答
2

这是一个老问题,但主题仍然相关。

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', )

披露:我是这个扩展的作者

于 2021-04-06T14:19:37.483 回答