1

I'm trying to upload an image using Django Rest Framework and AngularJs I'm using ng-file-upload to upload the image with AngularJs.

I saw it's possible to use ApiViewSet to do this. But as my Image is in a model and I use ModelSerializer so if it's possible I prefer to use ModelViewSet.

The problem is when I upload the image I got an error:

image: ["The submitted data was not a file. Check the encoding type on the form."]

Here is my code

Models.py

class Image(models.Model):
   image = models.ImageField(upload_to="articles")
   article = models.ForeignKey(Article)

Serializers.py

class ImageSerializer(serializers.ModelSerializer):
   class Meta:
       #article = ArticleSerializer(read_only=True, required=False)
       image = serializers.ImageField(use_url=True, allow_empty_file=True)
       model = Image

       fields = ('id', 'image', 'article')
       read_only_fields = ('id', )

#def get_validation_exclusions(self, *args, **kwargs):
#    exclusions = super(ImageSerializer, self).get_validation_exclusion()

#   return exclusions + ['article']

Views.py

class ImageViewSet(viewsets.ModelViewSet):
queryset = Image.objects.order_by('id')
serializer_class = ImageSerializer

class ImageArticleViewset(viewsets.ModelViewSet):
    queryset = Image.objects.select_related('article').all()
    serializer_class = ImageSerializer

    def list(self, request, *args, image_pk=None):
       queryset = self.queryset.filter(article__id=image_pk)
       serializer = self.serializer_class(queryset, many=True)

       return Response(serializer.data)

And the Controller

(function () {

'use strict';

angular
    .module(nameProject + '.article.post.controller')
    .controller('ArticlePostController', ArticlePostController);

ArticlePostController.$inject = ["$scope", "Articles", "Upload", "$timeout"];

function ArticlePostController($scope, Articles, Upload, $timeout) {
    var vm = this;
    vm.postArticle = postArticle;


    $scope.$watch('files', function () {

        $scope.upload($scope.files);
        console.debug("files = ", $scope.files);
        //console.debug("upload = ", $scope.upload);

    });

    $scope.upload = function (files) {
        if (files && files.length) {
            for (var i = 0; i < files.length; i++) {
                var file = files[i];
                console.debug("file = ", file, "type = ", file.type);

                Upload.upload({
                    url: '/api/v1/images/',
                    fields: {
                        'idArticle': 1,
                        'article': 1,
                        'image': file
                    },
                    file: file,
                    image:file
                }).progress(function (evt) {
                    var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
                    $scope.log = 'progress: ' + progressPercentage + '% ' +
                    evt.config.file.name + '\n' + $scope.log
                }).success(function (data, status, headers, config) {
                    $timeout(function () {

                        console.log("data === ", data.result);

                        $scope.log = 'file: ' + config.file.name + ', Response: ' + JSON.stringify(data) + '\n' + $scope.log;


                    });
                });
            }
        }
    };




}
})();

And this line

console.debug("files = ", $scope.files);

prints in the console

type =  image/jpeg

The Jade template

form.form-signin(ng-submit="vm.postArticle()" enctype="multipart/form-data")
h2.form-signin-heading Post un article
    input.input-block-level(type="text", name="title", placeholder="Title" ng-model="vm.title")
    input.input-block-level(type="number", name="price", placeholder="Price" ng-model="vm.price")
    input.input-block-level(type="text", name="content", placeholder="Description" ng-model="vm.description")
    input.input-block-level(type="number", name="quantity", placeholder="Quantity" ng-model="vm.quantity")
    input.input-block-level(type="text", name="color", placeholder="Color" ng-model="vm.color")
    input.input-block-level(type="text", name="state", placeholder="State" ng-model="vm.state")
    input.input-block-level(type="number", name="year", placeholder="Year" ng-model="vm.year")
    p watching model
    div(class="button" ngf-select ng-model="files" ngf-multiple="multiple") Select File on file change:
    button.btn.btn-large.btn-primary(type="submit") Submit article
0

1 Answer 1

2

Django's REST Framework default way of dealing with uploaded files is to examine the header and decide how to deal with it:

http://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser

By default ng-file-upload, uploads the file in json format, this means that the file you are uploading is submitted as Base64 to Django, which in turn will try to decode the file using the following:

http://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser

This does not work with the default Django File field, there are different ways to accomplish this, one is to use the following option (sendFieldAs) in your request:

Upload.upload({
    url: '/api/v1/images/',
    ...
    sendFieldsAs: form,

This will submitt the data as a form, which Django REST framework will process as it should. The other options, would include decoding the Base64 and manually creating the File to be used with the file field, something that adds overhead compared to the sendFieldAs option.

Sign up to request clarification or add additional context in comments.

1 Comment

I tried the first solution sendFieldsAs: "form" And I got image: ["No file was submitted."] I'll use FileUploadParser

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.