如何在 Django Rest Framework 中将当前用户设置为用户字段?

Posted

技术标签:

【中文标题】如何在 Django Rest Framework 中将当前用户设置为用户字段?【英文标题】:How to set current user to user field in Django Rest Framework? 【发布时间】:2016-06-01 19:00:00 【问题描述】:

我的以下代码运行良好。我可以通过选择图像和用户从 DRF 面板创建 Post 对象。但是我希望 DRF 由当前登录的用户填充用户字段。

models.py

class Post(TimeStamped):
    user = models.ForeignKey(User)
    photo = models.ImageField(upload_to='upload/')
    hidden = models.BooleanField(default=False)
    upvotes = models.PositiveIntegerField(default=0)
    downvotes = models.PositiveIntegerField(default=0)
    comments = models.PositiveIntegerField(default=0)

serializers.py

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'user', 'photo']

views.py

class PhotoListAPIView(generics.ListCreateAPIView):
    queryset = Post.objects.filter(hidden=False)
    serializer_class = PostSerializer
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)

我该怎么做?

【问题讨论】:

【参考方案1】:

在我的脑海中,你可以重写 perform_create() 方法:

class PhotoListAPIView(generics.ListCreateAPIView):
    ...
    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

试一试,让我知道它是否有效

【讨论】:

嗨。如果我在serializers.py 文件上使用def validate_user(self, value): return self.context['request'].user 会怎样。这也有效。这两者有什么内在区别吗? @MiniGunnR 这也很好,假设您希望用户成为序列化的一部分。我的方法无论如何都会奏效。 如果字段 'user' 是必需的,您将永远无法使用该方法 - perform_create 因为在 create 方法中引发异常。这就是我的情况。 当你想禁用可浏览api中的字段时,将其设置为只读django-rest-framework.org/api-guide/serializers/… 致 2019 年阅读此答案的任何人:您还应该传递 kwargs,因此您可以这样做:``` def perform_create(self, serializer, **kwargs): kwargs['user' ] = self.request.user serializer.save(**kwargs) ```【参考方案2】:

你可以使用CurrentUserDefault:

user = serializers.PrimaryKeyRelatedField(
    read_only=True, 
    default=serializers.CurrentUserDefault()
)

【讨论】:

这行不通了。即django-rest-framework.org/community/release-notes/#380 它在使用 PrimaryKeyRelatedField 时对我有用。从文档中我认为只有在您使用 HiddenField 时才不应设置 read_only=True? 在 Django 3.0 中使用 HiddenField 为我工作 不得不放弃 read_only 并传递 queryset 参数让我工作。 django 3.1【参考方案3】:

这取决于您的用例。如果您希望它是“只写”,这意味着 DRF 在写入时自动填充字段并且在读取时不返回 User,最直接的实现 according to the docs 将使用 HiddenField:

class PhotoListAPIView(generics.ListCreateAPIView):
    user = serializers.HiddenField(
        default=serializers.CurrentUserDefault(),
    )

如果您希望它可读,您可以使用PrimaryKeyRelatedField,同时注意您的序列化程序在写入时预先填充该字段 - 否则用户可以将user 字段设置为指向其他一些随机的@987654327 @。

class PhotoListAPIView(generics.ListCreateAPIView):
    user = serializers.PrimaryKeyRelatedField(
        # set it to read_only as we're handling the writing part ourselves
        read_only=True,
    )

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

最后,请注意,如果您使用更详细的 APIView 而不是 generics.ListCreateAPIView,则必须覆盖 create 而不是 perform_create,如下所示:

class PhotoListAPIView(generics.ListCreateAPIView):
    user = serializers.PrimaryKeyRelatedField(
        read_only=True,
    )

    def create(self, validated_data):
        # add the current User to the validated_data dict and call
        # the super method which basically only creates a model
        # instance with that data
        validated_data['user'] = self.request.user
        return super(PhotoListAPIView, self).create(validated_data)

【讨论】:

我能够在我的序列化程序中使用 valid_data['created_by_id'] = self.context['request'].user 这肯定是最全面的答案。谢谢olieidel 对于可读版本,我们必须覆盖perform_create,而我们可以将default=serializers.CurrentUserDefault() 与HiddenField 一起使用吗?【参考方案4】:

您可以避免在请求中传递user,您不会在输出中看到它,但 DRF 会自动填充它:

from rest_framework import serializers

class MyModelSerializer(serializers.ModelSerializer):
    user = serializers.HiddenField(default=serializers.CurrentUserDefault())

    class Meta:
        model = models.MyModel
        fields = (
            'user',
            'other',
            'fields',
        )

【讨论】:

如果我想在输出中看到它怎么办?【参考方案5】:

从DRF version 3.8.0 (Pull Request discussion) 开始,您可以在序列化程序中覆盖save()

from rest_framework import serializers
...

class PostSerializer(serializers.ModelSerializer):
    user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())

    class Meta:
        model = Post
        fields = ['id', 'user', 'photo']

    def save(self, **kwargs):
        """Include default for read_only `user` field"""
        kwargs["user"] = self.fields["user"].get_default()
        return super().save(**kwargs)

【讨论】:

这对我的问题特别有帮助。谢谢【参考方案6】:

@DaveBensonPhillips 的答案可能会在您的特定情况下工作一段时间,但它不是很通用,因为它破坏了 OOP 继承链。

ListCreateAPIView 继承自 CreateModelMixin 其中 saves 已经是序列化程序。除非您有充分的理由不这样做,否则您应该始终努力执行完整的覆盖方法链。这样一来,您的代码就可以保持 DRY 并且对更改保持稳健:

class PhotoListAPIView(generics.ListCreateAPIView):
    ...
    def perform_create(self, serializer):
        serializer.validated_data['user'] = self.request.user
        return super(PhotoListAPIView, self).perform_create(serializer)

【讨论】:

【参考方案7】:

您必须覆盖generics.ListCreateAPIView 创建对象的默认行为。

class PhotoListAPIView(generics.ListCreateAPIView):
    queryset = Post.objects.filter(hidden=False)
    authentication_classes = (SessionAuthentication, BasicAuthentication)
    permission_classes = (IsAuthenticated,)

    def get_serializer_class(self):
        if self.request.method == 'POST':
            return CreatePostSerializer
        else:
            return ListPostSerializer

    def create(self, request, *args, **kwargs):
        # Copy parsed content from HTTP request
        data = request.data.copy()

        # Add id of currently logged user
        data['user'] = request.user.id

        # Default behavior but pass our modified data instead
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

.get_serializer_class() 不是必需的,因为您可以从序列化程序中指定哪些字段是只读的,但根据我从事的项目,我通常会使用“不对称”序列化程序,即不同的序列化程序,具体取决于预期的操作。

【讨论】:

【参考方案8】:

试试这个:

def post(self, request, format=None)

        serializer = ProjectSerializer(data=request.data)
        request.data['user'] = request.user.id


        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST

【讨论】:

【参考方案9】:

这在 serializers.py 中对我有用,我也在其中使用嵌套数据。我想显示 created_by_username 而无需查找其他用户。

class ListSerializer(serializers.ModelSerializer):
    """
    A list may be created with items
    """
    items = ItemSerializer(many=True)

    # automatically set created_by_id as the current user's id
    created_by_id = serializers.PrimaryKeyRelatedField(
        read_only=True,
    )

    created_by_username = serializers.PrimaryKeyRelatedField(
        read_only=True
    )


    class Meta:
        model = List
        fields = ('id', 'name', 'description', 'is_public',
            'slug', 'created_by_id', 'created_by_username', 'created_at',
            'modified_by', 'modified_at', 'items')

    def create(self, validated_data):
        items_data = validated_data.pop('items', None)
        validated_data['created_by_id'] = self.context['request'].user
        validated_data['created_by_username'] = self.context['request'].user.username
        newlist = List.objects.create(**validated_data)

        for item_data in items_data:
            Item.objects.create(list=newlist, **item_data)
        return newlist

【讨论】:

以上是关于如何在 Django Rest Framework 中将当前用户设置为用户字段?的主要内容,如果未能解决你的问题,请参考以下文章

Django.rest_framework:如何序列化一对多?

如何使用 TemplateHTMLRenderer 在 Django-REST-Framework 中创建/放置?

django-rest-framework:如何序列化已经包含 JSON 的字段?

如何在 Django Rest Framework 中散列 Django 用户密码?

如何仅使用 django 作为后端并使用 django-rest-framework 发布

如何在 React 中显示来自 django-rest-framework 的错误消息