向缺少用户的Django Rest APIView发布请求(令牌身份验证)

时间:2019-07-12 13:53:39

标签: django django-rest-framework

我正在像Django中的应用程序一样为Twitter创建API,并且由于我实现了令牌身份验证(rest-auth),因此在创建新推文时遇到了问题:

{
    "author": [
        "This field is required."
    ]
}

我尝试过CreateAPIView:

class TweetCreateAPIView(CreateAPIView):
    serializer_class = TweetModelSerializer
    permission_classes = (IsAuthenticated,)

    # I've also tried to add the author field mannualy
    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

但是它不起作用,所以我创建了自定义帖子方法:

class TweetCreateAPIView(APIView):
    permission_classes = (IsAuthenticated,)

    def post(self, request, format=None):

        serializer = TweetModelSerializer(data=request.data)

        if serializer.is_valid():
            serializer.save(author=request.user)

            return Response(serializer.data, status=status.HTTP_201_CREATED)

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

但是它仍然无法识别创建推文的用户

型号:

class Tweet(models.Model):

    retweeted_by = models.ManyToManyField(
        TwitterUser, blank=True, related_name='retweets')

    commented_tweet = models.ManyToManyField(
        'self', related_name='comments', blank=True, symmetrical=False)

    author = models.ForeignKey(
        TwitterUser, on_delete=models.CASCADE, related_name='tweets')

    content = models.CharField(max_length=280)
    created_at = models.DateTimeField(auto_now_add=True)
    liked_by = models.ManyToManyField(
        TwitterUser, blank=True, related_name='likes')

    objects = TweetManager()

    def __str__(self):
        return self.content

    def get_comments(self):
        comments = Tweet.objects.filter(commented_tweet__pk=self.pk)
        return comments

    def get_retweets(self):
        retweets = TwitterUser.retweets(tweet__id=self.id)

    def get_absolute_url(self):
        return reverse('tweets:pk', kwargs={'pk': self.pk})

序列化器:

class TweetModelSerializer(serializers.ModelSerializer):

    likes_count = serializers.SerializerMethodField()
    already_liked = serializers.SerializerMethodField()

    def get_likes_count(self, tweet):
        return tweet.liked_by.all().count()

    def get_already_liked(self, tweet):
        user = None
        request = self.context.get("request")

        if request and hasattr(request, "user"):
            user = request.user

        if user is not None:
            if user in tweet.liked_by.all():
                return True
            else:
                return False
        else:
            pass

    class Meta:
        model = Tweet
        fields = [
            'id',
            'author',
            'commented_tweet',
            'content',
            'retweeted_by',
            'likes_count',
            'already_liked',
            'created_at',
        ]

设置:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # 3rd party
    'rest_framework',
    'rest_framework.authtoken',
    'rest_auth',
    'django.contrib.sites',
    'channels',
    'allauth',
    'allauth.account',
    'rest_auth.registration',
    'reset_migrations',
    'corsheaders',

]

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PAGINATION_CLASS':     'twitter.paginations.StandardResultsSetPagination',
    'TEST_REQUEST_DEFAULT_FORMAT': 'json',
}

身份验证本身可以很好地进行,包括注册在内,因此我不认为这是令牌的问题,但是我什至没有其他想法,即使CreateAPIView不起作用,该错误在哪里。

1 个答案:

答案 0 :(得分:0)

在序列化器中,字段列表中有author。由于它是ModelSerializer,因此drf从模型中获取该字段的详细信息,并且在模型作者中默认为null = False,因此drf使其成为必填字段,因为它不能空值。但是,由于您希望作者自动成为发出请求的用户,因此您不需要该字段可编辑。因此,使其成为序列化器中的只读字段,如下所示:

class TweetModelSerializer(serializers.ModelSerializer):
    ...
    class Meta:
        model = Tweet
        fields = ('id', 'author', 'commented_tweet', 'content', 'retweeted_by',
                  'likes_count', 'already_liked', 'created_at', )
        read_only_fields = ('author', )  # it's read only now, so drf go looking for it in POST data

现在,您只需要覆盖perform_create方法,而无需更改post方法。

如果您需要更多的自定义内容,例如superuser可以编辑作者,而没有其他人,则可以覆盖序列化程序的__init__方法,并根据request.user将字段设置为read_only,这会有点复杂。

此外,对于tuplelist,您应该考虑使用fields而不是read_only_fields来获得较小的性能提升。