React and Django DRF

The backend works, when i test it with an http file. and directly at the endpoint.

POST http://localhost:8000/api/products/ HTTP/1.1
Content-Type: application/json
Authorization: Bearer my_access_token


{
    "name": "test",
    "price": 23,
    "stock": 3,
    "description": "test description",
    "in_sale": true,
    "sale_price": 21,
    "color": [
        {
            "name":"black",
            "color_code": "#000000"
        }
    ]
}

However when i fill the forms created in react jsx and send it to the API, i get Key Error when i leave the Color Nested Serializer active, when i remove it, i get Bad Request.

Here is my serializer.py

class ProductCreateSerializer(serializers.ModelSerializer):
     class ColorCreateSerializer(serializers.ModelSerializer):
         class Meta:
             model = Color
             fields = ('name', 'color_code')
            
    # color = ColorCreateSerializer(many=True, required=False)

    def update(self, instance, validated_data):
        # color_data = validated_data.pop("color")

        with transaction.atomic():
            instance = super().update(instance, validated_data)
             if color_data is not None:
                 instance.color.all().delete()
                 for item in color_data:
                     Color.objects.create(product=instance, **item)
        return instance

    def create(self, validated_data):
        color_data = validated_data.pop('color')

        with transaction.atomic():
            product = Product.objects.create(**validated_data)
             for item in color_data:
                 Color.objects.create(product=product, **item)
        return product

    class Meta:
        model = Product
        fields = (
            'id',
            'name',
            'description',
            'price',
            'stock',
            'in_sale',
            'sale_price',
            'image',
            'color'

        )
        extra_kwargs = {
            'id': {'read_only': True}
        }

Here is my views.py

class ProductView(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    permission_classes = [AllowAny]

    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

    def perform_create(self, serializer):
        product = serializer.save(user=self.request.user)

    def get_serializer_class(self):
        if self.action == 'create' or self.action == 'update':
            return ProductCreateSerializer
        return super().get_serializer_class()

And finally my CreateProduct.jsx file

function CreateProduct() {
    const [colors, setColors] = useState([{ name: '', color_code: '' }]);

    const handleColorChange = (index, event) => {
        let data = [...colors];
        data[index][event.target.name] = event.target.value
        setColors(data);
    }

    const addColorFields = () => {
        let newColor = { name: '', color_code: '' }
        setColors([...colors, newColor])
    }

    const removeColorFields = (index) => {
        let data = [...colors];
        data.splice(index, 1)
        setColors(data)
    }

    const [product, setProduct] = useState(
        {
            name: '',
            description: '',
            price: '',
            stock: '',
            image: null,
            in_sale: false,
            sale_price: '',
        }
    );

    const handleProductInputChange = (event) => {
        if (event.target.name === 'in_sale') {
            setProduct({
                ...product,
                [event.target.name]: event.target.checked
            })
        } else {
            setProduct({
                ...product,
                [event.target.name]: event.target.value
            })
        }
    };

    const [selectedFile, setSelectedFile] = useState(null)
    const [image, setImage] = useState(null)

    const handleFileChange = (event) => {
        setSelectedFile(event.target.files[0]);
        setImage(selectedFile)
    }


    const handleSubmit = async (e) => {
        e.preventDefault();
        const formData = new FormData();

        Object.entries(product).forEach(([key, value]) => {
            if (key === 'image' && value) {
                formData.append(key, image);
            } else {
                formData.append(key, value);
            }
        });

        await api.post(`my_endpoint/`, formData);
    };

return (
        <>

            <section className="bg-light">
                <div className="container pb-5">

                    <Form onSubmit={handleSubmit}>

                        {/* Product Info */}
                        <div className="row gutters-sm shadow p-4 rounded">
                            <h4 className="mb-4">Product Info</h4>
                            <div className="col-lg-5 mt-2">
                                <div className="card mb-3">
                                    {image ?
                                        <img src={image} />
                                        :
                                        <img src="http://127.0.0.1:8000/media/products/placeholder-image.png" alt="" />
                                    }
                                </div>
                            </div>
                            {/* <!-- col end --> */}

                            <div className="col-lg-7 mt-2">
                                <div className="card">
                                    <div className="card-body mb-4">
                                        <div style={{ marginBottom: 5 }}>
                                            <label htmlFor="image" className="mb-0">
                                                Product Image
                                            </label>
                                            <input
                                                type="file"
                                                className="form-control form-control-sm"
                                                name="image"
                                                id="image"
                                                onChange={handleFileChange}
                                            />
                                        </div>
                                        <div style={{ marginBottom: 5 }}>
                                            <label htmlFor="name" className="mb-0">
                                                Title
                                            </label>
                                            <input
                                                type="text"
                                                className="form-control form-control-sm"
                                                id="name"
                                                name="name"
                                                value={product.name || ''}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                        <div>
                                            <label htmlFor="name" className="mb-0">
                                                Description
                                            </label>
                                            <input
                                                type="text"
                                                className="form-control form-control-sm"
                                                id="description"
                                                name="description"
                                                value={product.description || ''}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                        <div>
                                            <label htmlFor="price" className="mb-0">
                                                Price
                                            </label>
                                            <input
                                                type="number"
                                                className="form-control form-control-sm"
                                                name="price"
                                                value={product.price || ''}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                        <div>
                                            <label htmlFor="stock" className="mb-0">
                                                Stock Qty
                                            </label>
                                            <input
                                                type="number"
                                                className="form-control form-control-sm"
                                                name="stock"
                                                value={product.stock || ''}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                        <div className='mt-3 mb-2'>
                                            <Form.Check
                                                type="checkbox"
                                                name="in_sale"
                                                label='On Sale'
                                                value={product.in_sale}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                        <div>
                                            <label htmlFor="sale_price" className="mb-0">
                                                Sale Price
                                            </label>
                                            <input
                                                type="number"
                                                className="form-control form-control-sm"
                                                name="sale_price"
                                                value={product.sale_price || ''}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>

<div className="row gutters-sm shadow p-4 rounded">
                            <h4 className="mb-4">Color</h4>
                            <div className="col-md-12">
                                <div className="card mb-3">
                                    <div className="card-body">
                                        {colors.map((input, index) => (
                                            <div className="row text-dark mb-3" key={index}>
                                                <div className="col-lg-2 mb-2">
                                                    <label htmlFor="color_name" className="mb-2">
                                                        Color Name
                                                    </label>
                                                    <input
                                                        type="text"
                                                        className="form-control"
                                                        name="name"
                                                        id="color_name"
                                                        value={input.name}
                                                        onChange={(event) => handleColorChange(index, event)} />
                                                </div>
                                                <div className="col-lg-2 mb-2">
                                                    <label htmlFor="color_code" className="mb-2">
                                                        Color Code
                                                    </label>
                                                    <input
                                                        type="text"
                                                        className="form-control"
                                                        name="color_code"
                                                        id="color_code"
                                                        value={input.color_code}
                                                        onChange={(event) => handleColorChange(index, event)} />
                                                </div>

                                                <div className="col-lg-2 mt-2">
                                                    <button type='button' onClick={() => removeColorFields(index)} className='btn btn-danger mt-4'>Remove</button>
                                                </div>

                                            </div>
                                        ))}
                                        <button type='button' onClick={addColorFields} className="btn btn-primary mt-2">
                                            <i className="fas fa-plus" /> Add More...
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>

    )
}

export default CreateProduct

What's wrong here??? If the endpoints works, it should be a frontend error, right? I'm new at React. Trying the FullStack life here. Please Help!

import React, { useState } from 'react';
import Form from 'react-bootstrap/Form';
import api from './api'; // Adjust the import path as needed

function CreateProduct() {
    const [colors, setColors] = useState([{ name: '', color_code: '' }]);
    const [image, setImage] = useState(null);
    const [selectedFile, setSelectedFile] = useState(null);

    const handleColorChange = (index, event) => {
        let data = [...colors];
        data[index][event.target.name] = event.target.value
        setColors(data);
    }

    const addColorFields = () => {
        let newColor = { name: '', color_code: '' }
        setColors([...colors, newColor])
    }

    const removeColorFields = (index) => {
        let data = [...colors];
        data.splice(index, 1)
        setColors(data)
const [product, setProduct] = useState(
    {
        name: '',
        description: '',
        price: '',
        stock: '',
        image: null,
        in_sale: false,
        sale_price: '',
    }
);

const handleProductInputChange = (event) => {
    if (event.target.name === 'in_sale') {
        setProduct({
            ...product,
            [event.target.name]: event.target.checked
        })
    } else {
        setProduct({
            ...product,
            [event.target.name]: event.target.value
        })
    }
};

const handleFileChange = (event) => {
    setSelectedFile(event.target.files[0]);
    setImage(URL.createObjectURL(event.target.files[0]));
    setProduct({
        ...product,
        image: event.target.files[0]
    });
};
        setImage(selectedFile)
    const handleSubmit = async (e) => {
        e.preventDefault();
        const formData = new FormData();

        Object.entries(product).forEach(([key, value]) => {
            if (key === 'image' && value) {
                formData.append(key, value);
            } else {
                formData.append(key, value);
            }
        });

        // Optionally add colors to formData if needed
        // formData.append('colors', JSON.stringify(colors));

        await api.post(`my_endpoint/`, formData);
    };

    return (
        <>
            <section className="bg-light">
                <div className="container pb-5">
                    <Form onSubmit={handleSubmit}>
                        {/* Product Info */}
                        <div className="row gutters-sm shadow p-4 rounded">
                            <h4 className="mb-4">Product Info</h4>
                            <div className="col-lg-5 mt-2">
                                <div className="card mb-3">
                                    {image ?
                                        <img src={image} alt="preview" />
                                        :
                                        <img src="http://127.0.0.1:8000/media/products/placeholder-image.png" alt="" />
                                    }
                                </div>
                            </div>
                            {/* <!-- col end --> */}
                            <div className="col-lg-7 mt-2">
                                <div className="card">
                                    <div className="card-body mb-4">
                                        <div style={{ marginBottom: 5 }}>
                                            <label htmlFor="image" className="mb-0">
                                                Product Image
                                            </label>
                                            <input
                                                type="file"
                                                className="form-control form-control-sm"
                                                name="image"
                                                id="image"
                                                onChange={handleFileChange}
                                            />
                                        </div>
                                        <div style={{ marginBottom: 5 }}>
                                            <label htmlFor="name" className="mb-0">
                                                Title
                                            </label>
                                            <input
                                                type="text"
                                                className="form-control form-control-sm"
                                                id="name"
                                                name="name"
                                                value={product.name || ''}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                        <div>
                                            <label htmlFor="description" className="mb-0">
                                                Description
                                            </label>
                                            <input
                                                type="text"
                                                className="form-control form-control-sm"
                                                id="description"
                                                name="description"
                                                value={product.description || ''}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                        <div>
                                            <label htmlFor="price" className="mb-0">
                                                Price
                                            </label>
                                            <input
                                                type="number"
                                                className="form-control form-control-sm"
                                                name="price"
                                                value={product.price || ''}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                        <div>
                                            <label htmlFor="stock" className="mb-0">
                                                Stock Qty
                                            </label>
                                            <input
                                                type="number"
                                                className="form-control form-control-sm"
                                                name="stock"
                                                value={product.stock || ''}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                        <div className='mt-3 mb-2'>
                                            <Form.Check
                                                type="checkbox"
                                                name="in_sale"
                                                label='On Sale'
                                                checked={product.in_sale}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                        <div>
                                            <label htmlFor="sale_price" className="mb-0">
                                                Sale Price
                                            </label>
                                            <input
                                                type="number"
                                                className="form-control form-control-sm"
                                                name="sale_price"
                                                value={product.sale_price || ''}
                                                onChange={handleProductInputChange}
                                            />
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>

                        <div className="row gutters-sm shadow p-4 rounded">
                            <h4 className="mb-4">Color</h4>
                            <div className="col-md-12">
                                <div className="card mb-3">
                                    <div className="card-body">
                                        {colors.map((input, index) => (
                                            <div className="row text-dark mb-3" key={index}>
                                                <div className="col-lg-2 mb-2">
                                                    <label htmlFor={`color_name_${index}`} className="mb-2">
                                                        Color Name
                                                    </label>
                                                    <input
                                                        type="text"
                                                        className="form-control"
                                                        name="name"
                                                        id={`color_name_${index}`}
                                                        value={input.name}
                                                        onChange={(event) => handleColorChange(index, event)} />
                                                </div>
                                                <div className="col-lg-2 mb-2">
                                                    <label htmlFor={`color_code_${index}`} className="mb-2">
                                                        Color Code
                                                    </label>
                                                    <input
                                                        type="text"
                                                        className="form-control"
                                                        name="color_code"
                                                        id={`color_code_${index}`}
                                                        value={input.color_code}
                                                        onChange={(event) => handleColorChange(index, event)} />
                                                </div>
                                                <div className="col-lg-2 mt-2">
                                                    <button type='button' onClick={() => removeColorFields(index)} className='btn btn-danger mt-4'>Remove</button>
                                                </div>
                                            </div>
                                        ))}
                                        <button type='button' onClick={addColorFields} className="btn btn-primary mt-2">
                                            <i className="fas fa-plus" /> Add More...
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <button type="submit" className="btn btn-success mt-4">Create Product</button>
                    </Form>
                </div>
            </section>
        </>
    );
}

export default CreateProduct;
}

export default CreateProduct

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class ColorCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Color
        fields = ('name', 'color_code')

class ProductCreateSerializer(serializers.ModelSerializer):
    color = ColorCreateSerializer(many=True, required=False)

    class Meta:
        model = Product
        fields = (
            'id',
            'name',
            'description',
            'price',
            'stock',
            'in_sale',
            'sale_price',
            'image',
            'color'
        )
        extra_kwargs = {
            'id': {'read_only': True}
        }

    def update(self, instance, validated_data):
        color_data = validated_data.pop("color", None)

        with transaction.atomic():
            instance = super().update(instance, validated_data)
            if color_data is not None:
                instance.color.all().delete()
                for item in color_data:
                    Color.objects.create(product=instance, **item)
        return instance

    def create(self, validated_data):
        color_data = validated_data.pop('color', [])
        with transaction.atomic():
            product = Product.objects.create(**validated_data)
            for item in color_data:
                Color.objects.create(product=product, **item)
        return product
Вернуться на верх