如何使用 Axios 和 react hook 形式将文件上传到 Django rest framework API?

How to upload file to Django rest framework API using Axios and react hook form?

我使用 Django Rest Framework 创建了一个 API。它有一个图像上传选项。但是我无法上传文件。我正在使用 Axios 进行 API 调用,并使用 react hook form 进行表单处理。我在下面发布代码以便更好地理解。

Django: 型号:

class BlogModel(models.Model):
    user = models.ForeignKey(user_model.User, on_delete=models.CASCADE, related_name="user_blog")
    blogtitle = models.CharField(max_length=250)
    blogcontent = models.TextField()
    blogimg = models.ImageField(upload_to="blog_image", blank=True)
    slug = models.SlugField(max_length=250, unique=True)
    tags = models.ManyToManyField(BlogTagsModel, related_name='blog_tags', blank=True, null=True)
    published_date = models.DateTimeField(auto_now_add=True)
    edit_date = models.DateTimeField(auto_now=True)

序列化器

class BlogSerializer(serializers.ModelSerializer):
    class Meta:
        model = blog_model.BlogModel
        fields = '__all__'
        extra_kwargs = {
            'user': {'read_only': True},
            'slug': {'read_only': True},
        }

查看

class BlogPostView(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    serializer_class = blog_ser.BlogSerializer
    queryset = blog_model.BlogModel.objects.all()

    def perform_create(self, serializer):
        rand_num = random.randint(99, 222)
        blog_slug_str = f"{serializer.validated_data.get('blogtitle')}{rand_num}"
        sample_string_bytes = blog_slug_str.encode("ascii")
        base64_bytes = base64.b64encode(sample_string_bytes)
        slug = base64_bytes.decode("ascii")
        serializer.save(user=self.request.user, slug=slug)

反应:

形成 JSX

                <form className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" onSubmit={handleSubmit(onSubmit)}>
                    <div className="mb-4">
                        <label
                            className="block text-gray-700 text-sm font-bold mb-2"
                            htmlFor="title"
                        >
                            Title
                        </label>
                        <input
                            className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
                            id="title"
                            type="text"
                            {...register('title', { required: true })}
                        />
                        {errors.title && <p className="text-red-500 text-xs italic">Title is required</p>}
                    </div>
                    <div className="mb-4">
                        <label
                            className="block text-gray-700 text-sm font-bold mb-2"
                            htmlFor="image"
                        >
                            Image
                        </label>
                        <input
                            className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
                            id="image"
                            type="file"
                            {...register('image', { required: true })}
                        />
                        {errors.image && <p className="text-red-500 text-xs italic">Image is required</p>}
                    </div>
                    <div className="mb-4">
                        <label
                            className="block text-gray-700 text-sm font-bold mb-2"
                            htmlFor="details"
                        >
                            Details
                        </label>
                        <textarea
                            className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
                            id="details"
                            {...register('details', { required: true })}
                        />
                        {errors.details && <p className="text-red-500 text-xs italic">Details is required</p>}

                    </div>
                    <button
                        className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
                        type="submit"
                    >
                        Submit
                    </button>
                </form>

Axios 调用

const onSubmit = data => {
        console.log(data.image['0']);
        const payload = {
            blogtitle: data.title,
            blogcontent: data.details,
            blogimg: data.image,
        }
        console.log(payload);
        myAxios.post('/api/post/', payload).then(res => {
            console.log(res);
        }).catch(err => {
            console.log(err.response.data);
        })
    }

当我提交表单时,错误提示是 The submitted data was not a file. Check the encoding type on the form.

当安慰负载时,我得到:

{
    "blogtitle": "nok",
    "blogcontent": "asasa",
    "blogimg": {
        "0": {}
    }
}

请帮帮我...

我找到了解决方案。

原来我必须在 Axios 请求中添加一个 header 'Content-Type': 'multipart/form-data'

我在下面发布更新的代码:

Axios 调用

const onSubmit = data => {
        const payload = {
            blogtitle: data.title,
            blogcontent: data.details,
            blogimg: data.image['0'],
        }
        console.log(payload);
        myAxios.post('/api/post/', payload, {
            headers: {
                'Content-Type': 'multipart/form-data'
            }
        }).then(res => {
            console.log(res);
        }).catch(err => {
            console.log(err.response.data);
        })
    }