简体   繁体   中英

FormData() object is always empty in Django backend

I am trying to upload an HTML form via AJAX (just JS, no jQuery). The form is assembled by my template by adding three components: The csrf token, a ModelForm and a regular Django form (forms.Form). The model form {{ form.as_p}} comprises the visible parts of the form whereas the form {{ order_form }} comprises some hidden fields. The form section of my template looks like this:

<form id="{{ form_id }}" action="javascript:submitThisForm('{{ form_id }}', '/submit_blog_entry/')" method='POST' enctype='multipart/form-data'>
        {% csrf_token %}
        {{ form.as_p }}
        {{ other_form }}
        <input type='submit' value='SAVE changes' />
</form>

I already tried to remove enctype from the <form> tag (I read in a reply to another question that FormData() adds this automatically), but to no avail.

When the submit button is pressed, the JS function submitBlodEntryForm() is called, passing the form ID and the url to use for the AJAX request. The code of that JS function is here:

function submitThisForm(form_ID, url){

    var submit_form = document.getElementById(form_ID);
    var formData = new FormData(document.getElementById(form_ID));

    httpRequest = new XMLHttpRequest();

    if (!httpRequest){
        alert("Giving up, cannot create an XMLHTTP instance.");
        return false;
    };

    var url = "/submit_blog_entry/";
    var sendCSRFtoken = "csrfmiddlewaretoken="+String(getCookie('csrftoken'));
    var sendContent = sendCSRFtoken+"&"+formData;

    httpRequest.onreadystatechange = alertContents;
    httpRequest.open('POST', url, true);
    httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    //httpRequest.send();

    httpRequest.send(sendContent);
    // alternatively: httpRequest.send(formData); 

}

The AJAX request is submitted to and received by the server (a Django view). If I do not manually add the csrf token as shown in the JS code above (variable sendContent) and just send formData, I get a 403 error, apparently because the server does not find the token. It should be part of the form, though...

When I try binding the received data to the respective form, the validation fails:

form = ThisForm(request.POST)
if form.is_valid():
    #do something

If I print what is in request.POST, I get the following in the Terminal:

<QueryDict: {'[object FormData]': [''], 'csrfmiddlewaretoken': ['token goes here']}>

Apparently, the FormData object is empty. I also assume this because I get the two following errors for two required fields in my form (by using form.errors.as_data()):

[ValidationError(['This field is required.'])]

What is going wrong? Did I mess up the template such that FormData() does not produce useful data? Am I incorrectly creating the AJAX request? Or is the problem on the server side (although I am hardly doing anything there so far)?

Thank you, any help is greatly appreciated!

You have 2 problems

  • You have to use FormData.append to add data to a request that uses Formdata.
  • FormData objects use the multipart/form-data content type in request(which is set automatically and correctly)
...
var url = "/submit_blog_entry/";
formData.append("csrfmiddlewaretoken",getCookie('csrftoken'));

httpRequest.onreadystatechange = alertContents;
httpRequest.open('POST', url, true);
//httpRequest.send();

httpRequest.send(formData);
...

It might be better to not pass the form element to FormData like this:

new FormData(document.getElementById(form_ID))

I'm almost sure, it's only supported by firefox. Other browsers do not auto-populate the object.

Also where you do:

var sendContent = sendCSRFtoken+"&"+formData;

Since 'sendCSRFtoken' is a string, it calls the toString() method on formData and concatenates the two, that's why you get '[object FormData]' on the django side.

One way to make this work add the form fields using:

formData.append(name, value);

Do the same for the CRSF token and then call send like this:

httpRequest.send(formData);

XMLHttpRequest has multiple overloads for send so you can send an encoded string too if you prefer: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#send()

It will be really helpful when debugging to have the network tab open in chrome developer tools before the ajax call to verify what gets posted is correct before ruling out the client-side.

Thanks to all of you. I now found the problem, stupid copy&paste. The JS function submitBlogEntryForm() creating the AJAX request was bad. Setting the httpRequest header

httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

caused contradicting encoding instructions. I just removed this line altogether and also refrained from specifying "enctype" in the form tag in my template and left all this to be set by FormData() automatically. Now it works!

Thanks again for your help!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM