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

Min*_*nnR 41 django django-rest-framework

我有以下代码完美地工作.我可以Post通过选择图像和用户从DRF面板创建对象.但是,我希望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)
Run Code Online (Sandbox Code Playgroud)

serializers.py

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

views.py

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

我怎样才能做到这一点?

Mar*_*ons 55

您可以使用CurrentUserDefault:

user = serializers.PrimaryKeyRelatedField(
    read_only=True, 
    default=serializers.CurrentUserDefault()
)
Run Code Online (Sandbox Code Playgroud)

http://www.django-rest-framework.org/api-guide/validators/#currentuserdefault

  • 这不再适用了.Viz https://www.django-rest-framework.org/community/release-notes/#380 (3认同)
  • 在 Django 3.0 中使用 HiddenField 为我工作 (2认同)

Dav*_*ips 49

在我的头顶,您可以覆盖该perform_create()方法:

class PhotoListAPIView(generics.ListCreateAPIView):
    ...
    def perform_create(self, serializer):
        serializer.save(user=self.request.user)
Run Code Online (Sandbox Code Playgroud)

给它一个镜头,让我知道它是否有效

  • 如果需要字段'user',则永远不会达到该方法 - perform_create,因为create方法中出现了raise异常.这是我的情况. (4认同)
  • 你好.如果我使用`def validate_user(self,value)怎么办:在`serializers.py`文件中返回self.context ['request'].user`.这也有效.这两者之间有内在差异吗? (2认同)

oli*_*del 15

这取决于您的使用案例.如果您希望它是"只写",意味着DRF会在写入时自动填充该字段而不返回读取,则根据文档的最直接实现将使用HiddenField:

class PhotoListAPIView(generics.ListCreateAPIView):
    user = serializers.HiddenField(
        default=serializers.CurrentUserDefault(),
    )
Run Code Online (Sandbox Code Playgroud)

如果你想要它是可读的,你可以使用PrimaryKeyRelatedField,同时小心你的序列化程序在写入时预先填充字段 - 否则用户可以设置user指向其他随机用户的字段.

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)
Run Code Online (Sandbox Code Playgroud)

最后,请注意,如果您使用的是更详细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)
Run Code Online (Sandbox Code Playgroud)

  • 这肯定是最全面的答案。谢谢 olieidel (2认同)

小智 8

DRF 版本 3.8.0拉取请求讨论)开始,您可以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)
Run Code Online (Sandbox Code Playgroud)


Joz*_*zef 6

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

ListCreateAPIView继承自CreateModelMixin它已经保存了序列化器。您应该始终努力执行覆盖方法的完整链,除非您有充分的理由不这样做。这样你的代码就可以保持干爽并且能够抵抗变化:

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


Bab*_*yan 6

您可以避免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',
        )
Run Code Online (Sandbox Code Playgroud)