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

时间:2016-02-20 02:04:46

标签: django django-rest-framework

我有以下代码完美运行。我可以通过选择图像和用户从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,)

我该怎么做?

9 个答案:

答案 0 :(得分:52)

您可以使用CurrentUserDefault:

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

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

答案 1 :(得分:41)

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

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

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

答案 2 :(得分:13)

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

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

如果您希望它是可读的,您可以使用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)

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

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)

答案 3 :(得分:4)

@ DaveBensonPhillips的答案可能会在你的特定情况下使用一段时间,但它不是非常通用的,因为它打破了OOP继承链。

ListCreateAPIView继承自CreateModelMixinsaves序列化程序。除非你有充分的理由不这样做,否则你应该总是努力获得完整的重写方法链。通过这种方式,您的代码可以保持DRY并且可以抵御更改:

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

答案 4 :(得分:2)

您必须覆盖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()没有必要,因为您可以指定哪些字段是只读序列化程序的,但根据我所处理的项目,我通常最终得到'非对称'序列化程序,即取决于不同的序列化程序关于预期的行动。

答案 5 :(得分:2)

您可以避免在请求中传递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',
        )

答案 6 :(得分:1)

试试这个:

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

答案 7 :(得分:0)

这是在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

答案 8 :(得分:0)

DRF version 3.8.0Pull 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)