Javascript Django+;JS:Can';t跟踪喜欢的帖子。错误请求400

Javascript Django+;JS:Can';t跟踪喜欢的帖子。错误请求400,javascript,python,django,django-rest-framework,django-serializer,Javascript,Python,Django,Django Rest Framework,Django Serializer,小背景:我正在youtube上的教程中创建类似twitter的应用程序。所以,我是django的初学者,只知道JS的语法 几天前,我遇到了这样一个问题:类似的帖子不算在内。我认为问题在于序列化程序或我的环境(django 2.2;python 3.8.5;使用VS代码) 我的操作处理视图: import random from django.conf import settings from django.http import HttpResponse, Http404, JsonRespon

小背景:我正在youtube上的教程中创建类似twitter的应用程序。所以,我是django的初学者,只知道JS的语法

几天前,我遇到了这样一个问题:类似的帖子不算在内。我认为问题在于序列化程序或我的环境(django 2.2;python 3.8.5;使用VS代码)

我的操作处理视图:

import random
from django.conf import settings
from django.http import HttpResponse, Http404, JsonResponse
from django.shortcuts import render, redirect
from django.utils.http import is_safe_url

from rest_framework.authentication import SessionAuthentication
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .forms import TweetForm
from .models import Tweet
from .serializers import TweetSerializer, TweetActionSerializer

ALLOWED_HOSTS = settings.ALLOWED_HOSTS

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def tweet_action_view(request, *args, **kwargs):
    '''
    id is required.
    Action options are: like, unlike, retweet
    '''
    serializer = TweetActionSerializer(data=request.data)
    if serializer.is_valid(raise_exception=True):
        data = serializer.validated_data
        tweet_id = data.get("id")
        action = data.get("action")
        content = data.get('content')
        qs = Tweet.objects.filter(id=tweet_id)
        if not qs.exists():
            return Response({}, status=404)
        obj = qs.first()
        if action == "like":
            obj.likes.add(request.user)
            serializer = TweetSerializer(obj)
            return Response(serializer.data, status=200)
        elif action == "unlike":
            obj.likes.remove(request.user)
        elif action == "retweet":
            new_tweet = Tweet.objects.create(
                user=request.user,
                parent=obj,
                content = content)
            serializer = TweetSerializer(new_tweet)
            return Response(serializer.data, status=200)
    return Response({}, status=200)

serializers.py:

from django.conf import settings
from rest_framework import serializers

from .models import Tweet

MAX_TWEET_CHAR = settings.MAX_TWEET_CHAR
TWEET_ACTION_OPTIONS = settings.TWEET_ACTION_OPTIONS

class TweetActionSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    action = serializers.CharField()
    content = serializers.CharField(allow_blank = True, required = False)


    def validate_action(self, value):
        value = value.lower().strip()
        if not value in TWEET_ACTION_OPTIONS:
            raise serializers.ValidationError('This is not a valid action')
        return value


class TweetSerializer(serializers.ModelSerializer):
    likes = serializers.SerializerMethodField(read_only=True)
    class Meta:
        model = Tweet
        fields = ['id', 'content', 'likes']
    
    def get_likes(self, obj):
        return obj.likes.count()

    def validate_content(self, value):
        if len(value) > MAX_TWEET_CHAR:
            raise serializers.ValidationError("You exceeded maximum number of characters (240)")
        return value
home.html:

{% block content %}
<script>
    function tweetActionBtn(tweet_id, currentCount) {
        console.log(tweet_id, currentCount)
        const url = 'api/tweet/action'
        const method = 'POST'
        const data = JSON.stringify({
            id: tweet_id,
            action: 'like'
        })
        const xhr = new XMLHttpRequest()
        const csrftoken = getCookie('csrftoken');
        xhr.open(method, url)
        xhr.setRequestHeader("Content-Type", "application/json")
        xhr.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest")
        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
        xhr.setRequestHeader("X-CSRFToken", csrftoken)
        xhr.onload = function() {
            loadTweets(tweetsContainerElement)
        }
        console.log(xhr)
        xhr.send()
        return
    }
 
    function UnlikeBtn(tweet) {
        return "<button class='btn btn-outline-primary btn-sm' onclick=tweetActionBtn(" + 
            tweet.id + "," + tweet.likes + ",'unlike')>Unlike</button>"
    }

    function RetweetBtn(tweet) {
        return "<button class='btn btn-outline-success btn-sm' onclick=tweetActionBtn(" + 
            tweet.id + "," + tweet.likes + ",'unlike')>Retweet</button>"
    }

    function LikeBtn(tweet) {
        return "<button class='btn btn-primary btn-sm' onclick=tweetActionBtn(" + 
            tweet.id + "," + tweet.likes + ",'like')>" + tweet.likes + " Likes</button>"
    }


    function formatTweets(tweet) {
        var formattedTweet = "<div class='col-12 col-md-10 mx-auto border rounded py-3 mb-4 tweet' id='tweet-" + tweet.id + "'><p>" + tweet.content +
             "</p><div class='btn-group'>" + 
                LikeBtn(tweet) + 
                UnlikeBtn(tweet) +
                RetweetBtn(tweet) +
                "</div></div>"
        return formattedTweet
    }

    
</script>
{% endblock content %}
服务器向我发送以下消息:

XMLHttpRequest { onreadystatechange: null, readyState: 1, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, responseURL: "", status: 0, statusText: "", responseType: "", response: "" }

400 Bad request.
Bad Request: /api/tweet/action
[09/Jan/2021 16:43:11] "POST /api/tweet/action HTTP/1.1" 400 71
我去了
/api/tweet/action
,试图发布一些东西,得到了以下结果:

HTTP 400 Bad Request
Allow: OPTIONS, POST
Content-Type: application/json
Vary: Accept

{
    "detail": "JSON parse error - Expecting value: line 1 column 1 (char 0)"
}

下一步可以尝试什么?

如果未发送预期数据或数据格式错误,则会出现该错误

建模数据的方式决定了post请求是否需要一些数据。如果在您的模型中,某个字段被标记为
required=true
,那么它应该在您的post请求中可用。在您的情况下,它可能是操作字段。因此,您需要在post请求中发送此数据

如果使用浏览器中的开发者控制台查看post请求,您将看到post请求中没有正文

因此,您应该使用
xhr.send(data)
而不是
xhr.send()

此外,您不需要为tweet操作创建id字段,Django可以自动处理该操作。默认情况下,Django向每个模型添加一个id字段,该字段用作该模型的主键


让客户端能够发送id是个坏主意,应该在服务器中生成id,即post请求不应该为要在后端创建的数据设置
id
字段。

xhr.send(data)
而不是
xhr.send()
。您正在执行一个post请求,该请求需要一些数据。建议您只显示重新创建问题所需的代码,而不要发布所有内容,因为这会降低您的问题得到回答的机会。请阅读
HTTP 400 Bad Request
Allow: OPTIONS, POST
Content-Type: application/json
Vary: Accept

{
    "detail": "JSON parse error - Expecting value: line 1 column 1 (char 0)"
}