3

I have a view and serializer:

class UserView(generics.RetrieveUpdateAPIView):
    model = get_user_model()
    serializer_class = UserProfileSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def get_object(self, *args, **kwargs):
        return self.request.user


class UserImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = get_user_model()
        fields = ('image',)

They work great with httpie:

http -f put localhost:8000/accounts/api/image/ "Authorization: Token mytoken" image@~/Downloads/test.jpg
HTTP/1.0 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Date: Thu, 03 Sep 2015 22:50:33 GMT
Server: WSGIServer/0.2 CPython/3.4.3
Vary: Accept
X-Frame-Options: SAMEORIGIN

{
    "image": "http://localhost:8000/media/accounts/user_images/test.jpg"
}

and my image is uploaded and shows up in the admin.

Now I want to be able to upload a file using AJAX, but it apparently doesn't want to work:

<form action="http://localhost:8000/accounts/api/image/"
      method="put"
      enctype="multipart/form-data">
    <input name="image" type="file">
    <input type="submit">
</form>

<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>

<script>
    $('form').submit(function(e) {
        var formData = new FormData($(this));
        $.ajax({
            url: $(this).attr('action'),
            type: $(this).attr('method'),
            data: formData,
            headers: {'Authorization': 'Token mytoken'},
            cache: false,
            contentType: false,
            processData: false,
            success: function() { alert('it works') },
        });
        e.preventDefault();
    });
</script>

Now, my "It works" alert appears. I know the form is being submitted to the right place, I can see in the Django dev server that it's being requested as PUT and that it responds with 200 (same response as with httpie):

[03/Sep/2015 22:47:23] "PUT /accounts/api/image/ HTTP/1.1" 200 77

But it seems like the file isn't being uploaded, and it doesn't show up in the admin.

I'm out of ideas.

Tom Carrick
  • 6,349
  • 13
  • 54
  • 78
  • I think you might be sending the wrong content type. According to http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings you should specify `contentType: 'multipart/form-data'`to upload files. – dukebody Sep 06 '15 at 15:56
  • @dukebody, I tried this real quick but it gives me a 400 Bad Request with this error: `{detail: "Multipart form parse error - Invalid boundary in multipart: None"}` – Tom Carrick Sep 06 '15 at 16:30
  • Sorry I was wrong. See http://stackoverflow.com/questions/9622901/how-to-upload-a-file-using-jquery-ajax-and-formdata and http://stackoverflow.com/questions/2320069/jquery-ajax-file-upload – dukebody Sep 06 '15 at 17:38

1 Answers1

8

Ok, I can't exactly explain why, but it seems like var formData = new FormData($(this)); is not enough alone, it needs to be explicitly appended, because reasons? If anyone can explain, please do.

The working code:

<form action="http://localhost:8000/accounts/api/image/"
      method="put"
      enctype="multipart/form-data">
    <input name="image" type="file" id="image">
    <input type="submit">
</form>

<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>

<script>
    $('form').submit(function(e) {
        var formData = new FormData($(this)[0]);
        formData.append('image', $('#image')[0].files[0]);
        $.ajax({
            url: $(this).attr('action'),
            type: $(this).attr('method'),
            data: formData,
            headers: {'Authorization': 'Token mytoken'},
            cache: false,
            contentType: false,
            processData: false,
            success: function() { alert('it works') },
        });
        e.preventDefault();
    });
</script>
Tom Carrick
  • 6,349
  • 13
  • 54
  • 78
  • 2
    FormData expects non-jQuery object in its constructor. If you initialise your formData like this ```var formData = new FormData($(this)[0]);``` it should be properly initialised and then you don't need to append in next line. –  Mar 07 '16 at 16:01
  • 1
    $(this)[0] == this – Luis Masuelli Dec 22 '17 at 18:01