Allow User to Upload Arbitrary Number of Files?

77 views
Skip to first unread message

Taylor Marks

unread,
Jun 15, 2014, 9:47:29 PM6/15/14
to django-res...@googlegroups.com
Hello,

I have followed the Django tutorials and the Django Rest Framework tutorials, and I've done a bit more on my own beyond where those tutorials end.

I would like to allow users to upload an arbitrary number of files with their HTTP POSTs.

So I believe the first step is I need an APIView that, in its post method, passes request.FILES to a serializer. That serializer will then need to write to a model. The model will ultimately put those files into FileFields.

Here's my problem - I need to be able to handle an arbitrary number of files. My understanding is that each FileField can only hold a single file. Further, models need to have a fixed number of files - there's no way to have an arbitrary number of fields (possibly on account of a limitation of the backing store, MySQL).

So I think what I'm ultimately going to want is to instead have a separate model class/SQL table which stores all of my files, one per instance/SQL row. Then I'll have a field (a Varchar?) in my other table which just lists the rows that hold the associated files.

So if that's the best solution, then I understand what my Model needs to look like, as well as my View. The part that I don't understand is what I need to do in my serializer.

Thus far, serializers I've written always look something like...

class Serializer(serializers.ModelSerializer):

    class Meta:
        model = Message
        fields = ('id', 'title', 'created', 'body')

... I don't understand how I would do something more complicated. Or should the complication not go in my Serializer class, but in the View class?

Thanks for reading, and for any advise you have.

Jani Tiainen

unread,
Jun 16, 2014, 12:51:40 AM6/16/14
to django-res...@googlegroups.com
On Sun, 15 Jun 2014 18:47:29 -0700 (PDT)
Taylor Marks <taylo...@ranttapp.com> wrote:

> Hello,
>
> I would like to allow users to upload an arbitrary number of files with
> their HTTP POSTs.

That is relatively simple and it's not actually DRF related. You can use
views DRF provides but you need to handle this manually.

> So I believe the first step is I need an APIView that, in its post method,
> passes request.FILES to a serializer. That serializer will then need to
> write to a model. The model will ultimately put those files into FileFields.

You can do it that way, but it's simpler to bypass DRF in this.

> Here's my problem - I need to be able to handle an arbitrary number of
> files. My understanding is that each FileField can only hold a single file.
> Further, models need to have a fixed number of files - there's no way to
> have an arbitrary number of fields (possibly on account of a limitation of
> the backing store, MySQL).

Yes, you would be storing your files using standard Django file upload mechanism.

Do not even try to save files in the database - it will blow up to your face eventually.

You would need to have first model that stores your file(s):

class Attachment(models.Model):
'''
Files attached to katulupa_license
'''
target = models.ForeignKey('myapp.TargetModel', related_name='attachments')

file = models.FileField(upload_to='attachments/')


And then a view that actually binds files to your model:

class MyAPI(APIView):
def post(self, request, target_pk=None, format=None):
"""
Receive uploaded files
"""
attachments = request.FILES.getlist('file', [])
for attachment in attachments:
a = Attachment(target=target_pk, file=attachment)
a.save()

data = {'success': True }

# Hack to render JSON as text/html. This is required for "ajax" fileuploads
request.accepted_renderer = renderers.JSONRenderer()
return Response(data, content_type='text/html')

The hack part is there because there is no such thing as a json uploads. Most JS frameworks
implements it as an upload through hidden frame and they require response usually in JSON format
that is rendered as a text/html.

If you're not using ajax to upload files, or your framework response expectations is different, or you
like to pass uploaded file links for example that's the place to do it.

You need also a url config for that view:

url(r'^api/mymodelapi/(?P<pk>\w+)/upload_files/$', MyAPI.as_view(), name='mymodelapi-upload-files')

Now the final part is to fiddle with client side - you can add multiple uploaded files, just use always
same name attribute for each file filed so Django knows how to gather them in request.FILES.

--

Jani Tiainen

Taylor Marks

unread,
Jun 17, 2014, 9:03:39 PM6/17/14
to django-res...@googlegroups.com
Hi Jani,

Thanks for your help so far, but it's not working for me yet.

Based on what you said, I did the following:

I added a new model class in models.py:

class Attachment(models.Model):
    target  = models.ForeignKey('server_app.Message', related_name='attachments')
    theFile = models.FileField(upload_to='attachments/')

In serializers.py, I wrote an __init__ method to my MessageSerializer class, so that it would handle files it's given:

    def __init__(self, instance=None, data=None, files=None,
                 context=None, partial=False, many=None,
                 allow_add_remove=False, **kwargs):
        super(MessageSerializer, self).__init__(**kwargs)
        for attachment in files.getlist('file', []):
            Attachment(target=id, file=attachment).save()

It's otherwise the same as the Snippets class from the Django Rest Framework tutorials.

And I didn't touch views.py, whose relevant view looks like this:

class MessageList(generics.ListCreateAPIView):
    permission_classes = (IsAuthenticatedOrReadOnly,)
    queryset           = Message.objects.all()
    serializer_class   = MessageSerializer

    def pre_save(self, obj):
        obj.owner = self.request.user

I tried testing by writing the following in CURL:

curl -X POST http:myserver.com/myapp -u user:pass -F "body=test" -F "eyecon.png=@/local/path/to/eyecon.png"


But the response I get is:

{"non_field_errors": ["No input provided"]}


I'm confused - I definitely provided input. Not only did I attempt to include an attachment, but I also attempted to include text.

Any suggestions about what I'm doing wrong?

Thanks again

Subodh Nijsure

unread,
Jun 18, 2014, 2:23:12 AM6/18/14
to django-res...@googlegroups.com
If you really want to store files, consider uploading them to Amazon s3. Then store object names in your database.there is package called boot for django that lets you do the a ws upload in few lines of code.

Subodh

Reply all
Reply to author
Forward
0 new messages