首页 > 解决方案 > React: How to send a multipart/form-data to a database

问题描述

I am currently setting up a file upload with React for the frontend and Django for the backend. More specifically, I want to pass a file + some data to my database via an API.

But while uploading the file I get the error: "The submitted data was not a file. Check the encoding type on the form."

models.py (Django)

class Story (models.Model):
    title = models.CharField(max_length=100,blank=False)
    content = models.TextField(blank=False)
    author = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
    audio = models.FileField(default='SOME STRING', upload_to='audio_stories/',null=True, validators=[validate_file_extension_audio])


    def __str__(self):
        return self.title

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

serializers.py (Django)

class StorySerializer(serializers.ModelSerializer):
  class Meta:
    model = Story 
    fields = '__all__'

A MultiPartParser is used to pass file + data.

api.py (Django)

class StoryViewSet(viewsets.ModelViewSet) (Django):
    serializer_class = StorySerializer
    parser_classes = (MultipartJsonParser, parsers.JSONParser) 

    queryset = Story.objects.all()


    permission_classes = [
        permissions.IsAuthenticated
    ]

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

utils.py (Django)

class MultipartJsonParser(parsers.MultiPartParser):

    def parse(self, stream, media_type=None, parser_context=None):
        result = super().parse(
            stream,
            media_type=media_type,
            parser_context=parser_context
        )
        data = {}
        # find the data field and parse it
        data = json.loads(result.data["data"])
        qdict = QueryDict('', mutable=True)
        qdict.update(data)
        return parsers.DataAndFiles(qdict, result.files)

In actions story.js (React)

import axios from "axios";
import { tokenConfig } from './auth';
import { createMessage, returnErrors } from "./messages";



import {ADD_STORY} from "./types";

const apiBase = "http://localhost:8000/api";



export const addStory = story => (dispatch, getState) => {
  axios
    .post(apiBase +"/story/", story, tokenConfig(getState))
    .then(res => {
      dispatch(createMessage({ addStory: "Story Added" }));
      dispatch({
        type: ADD_STORY,
        payload: res.data
      });
    })
    .catch(err =>
      dispatch(returnErrors(err.response.data, err.response.status))
    );
};

In components create_story.js (React)

import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { addStory } from "../../actions/story";


export class CreateStory extends Component {
  state = {
    title: "",
    content:""
  };

  static propTypes = {
    addStory: PropTypes.func.isRequired
  };

  onChange = e => this.setState({ [e.target.name]: e.target.value });

  onSubmit = e => {
    e.preventDefault();
    const { title,content, audio} = this.state;
    const story = { title, content, audio};
    this.props.addStory(story);
    this.setState({
      title: "",
      content:"",
      audio:""
    });
  };

  render() {
    const {title, content, audio} = this.state;
    return (
      <div className="card card-body mt-4 mb-4">
        <h2>Add Story</h2>
        <form onSubmit={this.onSubmit}>

          <div className="form-group">
            <label>Title</label>
            <input
              className="form-control"
              type="text"
              name="title"
              onChange={this.onChange}
              value={title}
            />
             </div>
           <div className="form-group">
            <label>Content</label>
            <input
              className="form-control"
              type="text"
              name="content"
              onChange={this.onChange}
              value={content}
            />
          </div>

           <div className="form-group">
            <label>Audio</label>
            <input
              className="form-control"
              type="file"
              name="audio"
              onChange={this.onChange}
              value={audio}
            />
          </div>

         <div className="form-group">
            <button type="submit" className="btn btn-primary">
              Submit
            </button>
          </div>
        </form>
      </div>
    );
  }
}
export default connect(
  null,
  { addStory }
)(CreateStory);

I'm having trouble tracing the error in my code. I assume that the react form does not provide the correct data format, i.e. formdata. But i dont know how and wehre to change this. Without the file upload I was able to successfully pass data to my database.

Many thanks.

标签: djangoreactjsreact-redux

解决方案


Your onChange function in create_story.js is not gathering the file. You may need to reformat that function to something like:

onChange = (e) => {
        if(e.target.name === 'audio') {
            this.setState({
                [e.target.name]: e.target.files[0]
            }, () => console.log(this.state.audio))

        } else {

            this.setState({
                [e.target.name]: e.target.value
            }, () => console.log(this.state))
        }
    }

推荐阅读