Parth Pandya
Parth Pandya

Reputation: 31

Unable to send a PUT request to a Django REST API using Reactjs fetch() method

I've recently started learning Reactjs & made a simple Todo react appplication in order to learn it bit-by-bit, step-by-step.The data in this Todo react front-end is coming from a simple Todo Django REST API. But the problem is I want to change the state of "completed" to depict a task completed. Im trying to do this by sending a PUT request to the API using the unique "id" but getting some errors. Below is my approach :

List.js

import React from 'react'
import Todo from './Todo'
import Nav from './Nav'


//Class based Component
class List extends React.Component {

    constructor() {
        super()
        this.state = {
            todos: [],
        }
        this.handleChange = this.handleChange.bind(this)
    }

    fetchData() {
        fetch('http://localhost:8000/Todo-api/')
            .then(response => response.json())
            .then((data) => {
                this.setState({
                    todos: data
                });
            });
    }

    componentDidMount() {
        this.fetchData();
    }

    

    handleChange(id) {
        this.setState(prevState => {
            const updatedData = prevState.todos.map(todo => {
                if (todo.id === id) {
                    todo.completed = !todo.completed
                }
                return todo
            })
            return {
                todos: updatedData
            }
        })

        fetch(`http://127.0.0.1:8000/Todo-api/${id}/?format=json/`,
            {
                method: 'PUT',
                headers: {
                    'Content-Type': 'application/json',
                    'Accept' : 'application/json',
                
                    // Other possible headers
                },
                body: JSON.stringify(this.state)

            }).then(function (response) {
                return response.json();
            }).then(function (data) {
                console.log("Data is ok", data);
            }).catch(function (ex) {
                console.log("parsing failed", ex);
            });
    }

    currentDate() {
        return new Date().getFullYear();
    }


    render() {
        const Data = this.state.todos.map(details => <Todo key={details.id} det={details} handleChange={this.handleChange} />)

        const someStyle = {
            marginTop: "40px", marginLeft: "100px", marginRight: "100px",
        }
        console.log(this.state.todos)
        return (
            <div>
                <Nav />

                <div className="todo-list box">
                    {Data}
                </div>

                <hr style={someStyle} />

                <p className="text-center" style={{ fontFamily: "Roboto Mono", lineHeight: 0.5 }}>Copyright ©️ {this.currentDate()} Parth Pandya </p>

                <p className="text-center" style={{ fontFamily: "Roboto Mono", fontSize: 10, lineHeight: 0.5 }}>Some rights reserved.</p>
            </div>
        )
    }
}

export default List

Todo.js

import React from 'react'



function Todo(props) {
  const completeStyles = {
    fontStyle: "italic",
    textDecoration: "Line-through",
    color: "gray",
  }

  return (
    <div className="todo-item">
      <input type="checkbox" onChange={() => props.handleChange(props.det.id)} checked={props.det.completed} />
      <p style={props.det.completed ? completeStyles : null}>{props.det.title}</p>
    </div>
  );
}

export default Todo;

views.py of API

from django.shortcuts import render
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView
from .serializers import TodoDataSerializer
from .models import TodoData
# Create your views here.

class Todolist(APIView):
    serializer_class = TodoDataSerializer

    def get(self, request):
        ToDoData = TodoData.objects.all()
        serializer = TodoDataSerializer(ToDoData, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = TodoDataSerializer(data=request.data)
        if(serializer.is_valid()):
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.data, status=status.HTTP_404_BAD_REQUEST)

class TodoDetail(APIView):
    serializer_class = TodoDataSerializer

    def get(self, request, pk):
        ToDoData = TodoData.objects.get(pk=pk)
        serializer = TodoDataSerializer(ToDoData)
        return Response(serializer.data)

    def delete(self, request, pk):
        ToDoData = TodoData.objects.filter(pk=pk)
        ToDoData.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    def put(self, request, pk):
        snippets = TodoData.objects.get(pk=pk)
        serializer = TodoDataSerializer(snippets, data=request.data)
        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)

The DELETE method works well, but I'm not getting any idea about how to do it for PUT/POST. Went through many resources but most of them were suggesting axios which I don't want to use now. I want to get this done using fetch().

When I use body:JSON.stringify(this.state.todos) I get the error below:

PUT http://127.0.0.1:8000/Todo-api/4/ 400 (Bad Request) Data is ok {non_field_errors: Array(1)}non_field_errors: ["Invalid data. Expected a dictionary, but got list."]__proto__: Object

When I use body:JSON.stringify(this.state) I get the error below:

PUT http://127.0.0.1:8000/Todo-api/4/ 400 (Bad Request) Data is ok {title: Array(1), completed: Array(1)} completed: ["This field is required."] title: ["This field is required."] __proto__: Object

What to do now?

(Note : To test it locally, clone this git repository)

Upvotes: 0

Views: 1642

Answers (2)

Duresa Wata
Duresa Wata

Reputation: 1

remove ?format=json. Don't forget to add trailing forward slash after id!

Before

 fetch(`http://127.0.0.1:8000/Todo-api/${id}/?format=json/`, {
      method: "PUT"
      ...
    })

After

fetch(`http://127.0.0.1:8000/Todo-api/${id}/`, {
          method: "PUT"
          ...
        })

Upvotes: 0

pplonski
pplonski

Reputation: 5849

Please try to use PATCH instead of PUT.

  • PUT is for full object update,
  • PATCH is for partial update (you can update only selected field).

What you need is PATCH.

In the update request, please send only the dict with the field that you want to update ({"status": "complete"}) and set id in the request URL.

What is more, you can check the full working CRUD example in my article: CRUD in Django Rest Framework and React.

Upvotes: 1

Related Questions