Friday, April 29, 2016

Django Rest Framework + Django Rest Swagger + ImageFileField

Leave a Comment

I created a simple Model with an ImageField and I wanna make an api view with django-rest-framework + django-rest-swagger, that is documented and is able to upload the file.

Here is what I got:

models.py

from django.utils import timezone from django.db import models  class MyModel(models.Model):      source = models.ImageField(upload_to=u'/photos')     is_active = models.BooleanField(default=False)     created_at = models.DateTimeField(default=timezone.now)      def __unicode__(self):         return u"photo {0}".format(self.source.url) 

serializer.py

from .models import MyModel  class MyModelSerializer(serializers.ModelSerializer):      class Meta:         model = MyModel         fields = [             'id',             'source',             'created_at',         ] 

views.py

from rest_framework import generics from .serializer import MyModelSerializer  class MyModelView(generics.CreateAPIView):     serializer_class = MyModelSerializer     parser_classes = (FileUploadParser, )      def post(self, *args, **kwargs):         """             Create a MyModel             ---             parameters:                 - name: source                   description: file                   required: True                   type: file             responseMessages:                 - code: 201                   message: Created         """         return super(MyModelView, self).post(self, *args, **kwargs) 

urls.py

from weddings.api.views import MyModelView  urlpatterns = patterns(     '',     url(r'^/api/mymodel/$', MyModelView.as_view()), ) 

For me this should be pretty simple. However, I can't make the upload work. I always get this error response: enter image description here

I've read this part of the documentation from django-rest-framework:

If the view used with FileUploadParser is called with a filename URL keyword argument, then that argument will be used as the filename. If it is called without a filename URL keyword argument, then the client must set the filename in the Content-Disposition HTTP header. For example Content-Disposition: attachment; filename=upload.jpg.

However the Header is being passed by django-rest-swagger in the Request Payload property (from chrome console).

If any more info is necessary, please let me know.

I'm using Django==1.8.8, djangorestframework==3.3.2 and django-rest-swagger==0.3.4.

2 Answers

Answers 1

I got this working by making a couple of changes to your code.

First, in models.py, change ImageField name to file and use relative path to upload folder. When you upload file as binary stream, it's available in request.data dictionary under file key (request.data.get('file')), so the cleanest option is to map it to the model field with the same name.

from django.utils import timezone from django.db import models   class MyModel(models.Model):      file = models.ImageField(upload_to=u'photos')     is_active = models.BooleanField(default=False)     created_at = models.DateTimeField(default=timezone.now)      def __unicode__(self):         return u"photo {0}".format(self.file.url) 

In serializer.py, rename source field to file:

class MyModelSerializer(serializers.ModelSerializer):      class Meta:         model = MyModel         fields = ('id', 'file', 'created_at') 

In views.py, don't call super, but call create():

from rest_framework import generics from rest_framework.parsers import FileUploadParser  from .serializer import MyModelSerializer   class MyModelView(generics.CreateAPIView):     serializer_class = MyModelSerializer     parser_classes = (FileUploadParser,)      def post(self, request, *args, **kwargs):         """             Create a MyModel             ---             parameters:                 - name: file                   description: file                   required: True                   type: file             responseMessages:                 - code: 201                   message: Created         """         return self.create(request, *args, **kwargs) 

I've used Postman Chrome extension to test this. I've uploaded images as binaries and I've manually set two headers:

Content-Disposition: attachment; filename=upload.jpg Content-Type: */* 

Answers 2

It has been my experience that the FileUploadParser works with this format of a request:

    curl -X POST -H "Content-Type:multipart/form-data" \                  -F "file=@{filename};type=image/jpg" \                  https://endpoint.com/upload-uri/ 

The request.data['file'] in your view will have the file.

Maybe if you try a Content-Type:multipart/form-data header, you will have luck.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment