【问题标题】:How to set current user to user field in Django Rest Framework?如何在 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,)

我该怎么做?

【问题讨论】:

    标签: django django-rest-framework


    【解决方案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()
    )
    

    【讨论】:

    • 它在使用 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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-07-28
            • 1970-01-01
            • 2022-06-12
            • 2013-03-24
            • 1970-01-01
            • 2022-01-17
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多