Search code examples
pythondjangoresttastypie

Trying to upload image to a FileField model using REST framework or Tastypie


Can anyone give me a step by step/ link resource on how I can upload a file from a mobile/desktop application to a Django based server using a REST API?

The server has a model with a FileField named "thumbnail". I'm able to upload other data but files seem to be a huge problem.

Note that I'm not talking about uploading using a browser/Django form but from an app through an Http request

api:

from models import Article

class ArticleResource(ModelResource):

    class Meta:
        queryset = Article.objects.all()
        resource_name = 'article'
        filtering = {'title': ALL}
        authorization=Authorization()

independent python script I'm using to make Http Requests(mock mobile app)

url="http://127.0.0.1:8000/articles/api/article/"

data={
    'title':'Tastypie',
    'body':'First Restful client',
    'pub_date':'05/02/2015',
    }
files=  {'thumbnail': open('django.png', 'rb')}
headers =  {'content-type': 'image/png'}
print requests.post(url, files=files)

model:

class Article(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    pub_date = models.DateTimeField('date published')
    likes = models.IntegerField(default=0)
    thumbnail = models.FileField(blank=True,null=True,upload_to=get_upload_file_name)
    def __unicode__(self):
        return str(self.title)

EDIT:

This worked:

api:

class MultipartResource(object):
        def deserialize(self, request, data, format=None):
            if not format:
                 format = request.META.get('CONTENT_TYPE', 'application/json')
            if format =='application/x-www-form-urlencoded':
                return request.POST
            if format.startswith('multipart'):
                data = request.POST.copy()
                photo = Article()
                photo.thumbnail = request.FILES['thumbnail']
                photo.title = request.POST.get('title')
                photo.body=request.POST.get('body')
                photo.pub_date = request.POST.get('pub_date')
                photo.save()
                # ... etc
                return data
            return super(ArticleResource, self).deserialize(request, data, format)

        # overriding the save method to prevent the object getting saved twice 
        def obj_create(self, bundle, request=None, **kwargs):
             pass


class ArticleResource(MultipartResource,ModelResource):

    class Meta:
        queryset = Article.objects.all()
        resource_name = 'article'
        filtering = {'title': ALL}
        authorization=Authorization()

Http Request Python script:

url="http://127.0.0.1:8000/articles/api/article/"

data={
    'title':'Tastypie',
    'body':'First Restful client',
    'pub_date':'2015-02-05',
    }
files=  {'thumbnail': open('django.png', 'rb')}

print requests.post(url, data=data, files=files).text

Solution

  • Tastypie doens't have any good way to save binary files. I'd try something like this:

    class ArticleResource(ModelResource):
    
        class Meta:
            queryset = Article.objects.all()
            resource_name = 'article'
            filtering = {'title': ALL}
            authorization=Authorization()    
    
        # save the photo
            def deserialize(self, request, data, format=None):
                if not format:
                    format = request.META.get('CONTENT_TYPE', 'application/json')
    
                if format.startswith('multipart'):
                    data = request.POST.copy()
                    photo = Article()
                    photo.thumbnail = request.FILES['thumbnail']
                    photo.title = request.POST.get('title')
                    # ... etc
                    return data
                return super(ArticleResource, self).deserialize(request, data, format)
    
            # overriding the save method to prevent the object getting saved twice 
            def obj_create(self, bundle, request=None, **kwargs):
                pass