0

I am trying to perform a file upload. I am using Angularjs as front-end and Django as backend. I am able to upload a file successfully as shown in my logs. However, when the file data is passed to Django, I don't know why the form is not valid. This is my error message:

There's a mistake in field 'file': <ul class="errorlist"><li>This field is required.</li></ul>

My JSON message:

<JsonResponse status_code=200, "application/json">

Request Payload: content-disposition

html:

<body ng-app="myApp" ng-controller="appCtrl">
     <input type="file" id="file" name="files" accept="text/*" 
            data-url="file" class="inputfile_upload" select-ng-files
            ng-model="uploadedFile"/>
     <label for="file"> <span id="chooseFile"></span>Upload a file!</label>
     <button id="submitBtn" type="submit" ng-click="upload()">Upload</button>
</body>

controller.js:

var app = angular.module('myApp', [])
app.controller('appCtrl', function ($scope, $rootScope, $http, fileUploadService){

    $scope.upload = function() {
       var file = $scope.uploadedFile; 
       console.log('file is ' );
       console.dir(file);

       var uploadUrl = "/uploaded_file";
       fileUploadService.uploadFileToUrl(file, uploadUrl);
    };
}

service.js:

app.factory('fileUploadService', function ($rootScope, $http) {
    var service = {};
    service.uploadFileToUrl = function upload(file, uploadUrl){
        var fd = new FormData();
        fd.append('file', file);
        $http.post(uploadUrl, fd, {
            transformRequest: angular.identity,
            headers: {'Content-Type': undefined}
        }).then(function successCallback(response){
            console.log("Files added");
        }, function errorCallback(response){
            console.log("Files not successfully added");
        })    
    }
    return service;
});

directives.js:

app.directive("selectNgFiles", function($parse) {
  return {
    require: "ngModel",
    link: function postLink(scope,elem,attrs,ngModel) {
      elem.on("change", function(e) {
        var files = elem[0].files;
        ngModel.$setViewValue(files);
      })
    }
  }
});

views.py:

@csrf_exempt
def uploadFile(request):
    try:
        if request.method == 'POST':
            # When files are submitted to the server, the file data ends up placed in request.FILES.

            form = UploadFileForm(request.POST, request.FILES)
            for error_field in form.errors:
                print("There's a mistake in field '{field}': {problem}".format(
                    field=error_field,
                    problem=form.errors[error_field]
                ))
            if form.is_valid():
                # file is saved
                form.save()
                data = request.FILES['file'].read();
                print(data);
                messages.success(request, 'File uploaded successfully!', extra_tags='alert')
                return JsonResponse({"status": "success", "message": "Success"})
            else:
                print(JsonResponse({"status": "error", "message": form.errors}))
                return JsonResponse({"status": "error", "message": form.errors})
        else:
            form = UploadFileForm()
        return render(request, uploadFile(), {'form': form})
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=500)

Model.py:

class UploadFile(models.Model):
    file = models.FileField(null=True)


    def __unicode__(self):
        return self.file

form.py

class UploadFileForm(ModelForm):
    class Meta:
        model = UploadFile
        fields = '__all__'
  • you are not using form. use UploadFileForm in template. – sandeep Jan 02 '20 at 10:56
  • It's not clear how the file is uploaded from your browser. You might want to check the browser developer tools (network tab) to see your request and check that a file is uploaded. Or you need to tell us what this `upload()` javascript function is doing exactly. And set a breakpoint in your view and inspect the `request.POST` and `request.FILES` values. – dirkgroten Jan 02 '20 at 11:06
  • One thing I notice is that your `` has `name` attribute `files` instead of `file`. But since you don't have a `
    ` and I don't know what `upload()` does, it's hard to say if this is the issue.
    – dirkgroten Jan 02 '20 at 11:08
  • @sandeep i called `form = UploadFileForm(request.POST, request.FILES)` in the view. is that what you meant? –  Jan 02 '20 at 11:19
  • @dirkgroten my upload function basically just pass the file to django, because some actions are to be done by django. do you want me to upload my code for the upload function as well? –  Jan 02 '20 at 11:21
  • @dirkgroten i am using angularjs modules. –  Jan 02 '20 at 11:22
  • yes because obviously it's not uploading a file for the POST variable `file`. Again look at your network tab in your browser. You'll see what it's posting. – dirkgroten Jan 02 '20 at 11:22
  • @dirkgroten added! –  Jan 02 '20 at 11:32
  • ok, seems you upload it as `file`. So again, use your debuggers. Please confirm that the POST request data contains a part where Content-Disposition has name="file". Also confirm the contents of `request.FILES` by setting a breakpoint in your view and inspecting it. – dirkgroten Jan 02 '20 at 11:41
  • you calling form in view but not in template. everything is correct. – sandeep Jan 02 '20 at 11:59
  • @dirkgroten I have `print(request.FILES)` and I've got `` I don't get what do you mean for the first part though. –  Jan 02 '20 at 12:12
  • Learn to use your browser's debugger. If you open the developer tools, you'll see a network tab where you can view all the requests/responses. If you filter by `XHR` you'll see the angular post. It looks like your angular code is not working since `request.FILES` is empty. So you're actually not submitting any files. – dirkgroten Jan 02 '20 at 12:23
  • ahh, you're right! I've got this in my XHR preview: `{status: "error", message: {file: ["This field is required."]}} status: "error" message: {file: ["This field is required."]}` –  Jan 02 '20 at 12:26
  • You need to look at the request data, this is the response. At the bottom of the Headers section – dirkgroten Jan 02 '20 at 12:28
  • @dirkgroten under request payload? `Content-Disposition: form-data; name="file"` –  Jan 02 '20 at 12:33
  • @dirkgroten so, is my front-end part actually correct? –  Jan 02 '20 at 12:44
  • Do you see the contents of the file? Maybe show us the entire Request-Data. This looks ok to me. Weird. – dirkgroten Jan 02 '20 at 12:57
  • That does not look correct. There’s no content type and the contents of the file are missing. It shouldn’t be showing [object FileList] like this. So yes, your front-end code is wrong. Look up how to upload a file with angular or ask a new question specifically on this. Django is not the problem here. – dirkgroten Jan 02 '20 at 13:07
  • change name="files" to name="file". – sandeep Jan 02 '20 at 13:17

1 Answers1

-2

You have to use form in template:

template

    <body ng-app="myApp" ng-controller="appCtrl">
        <form method="post" enctype="multipart/form-data">
         {{ form.file }}
         <label for="file"> <span id="chooseFile"></span>Upload a file!</label>
         <button id="submitBtn" type="submit">Upload</button>
</form>
    </body>
sandeep
  • 721
  • 1
  • 7
  • 14
  • It is not required to do that. Since OP is trying to submit the file with angular, as long as the file is taken from the input, it would work. `{{ form.file }}` is nothing else than `` – dirkgroten Jan 02 '20 at 12:27
  • @dirkgroten {{ form.file }} and are different. first one is django form. form.is_valid validate this form. 2nd is just html input. – sandeep Jan 02 '20 at 12:51
  • @user123, I have updated my code. just copy paste code and check if this works. In case of error, please comment. – sandeep Jan 02 '20 at 12:53
  • @sandeep no it’s the same. Look at your html source in the browser. `{{ form.file }}` renders as the `` field I mentioned in my previous comment. – dirkgroten Jan 02 '20 at 12:55
  • @sandeep my angularjs modules is already calling `
    `
    –  Jan 02 '20 at 12:56
  • @dirkgroten, it's not same. {{ form }} is form we declare in views. This looks same in browser, but both are not same. form.is_valid only validates entities we declare in our form. If we don't use this in template, fields will be submitted blank. – sandeep Jan 02 '20 at 13:01
  • @user123, I don't angular. Can you remove all angular code just to check. Just use normal code as I have write. "This field is required." that means we are not giving file to form. – sandeep Jan 02 '20 at 13:03
  • @user123, I thins problem is name="files" in your form. django will create name="file" and you have declared name="files". change this and try – sandeep Jan 02 '20 at 13:16