Django Rest Framework & Filepond VueJS

Integrating DRF with Filepond VueJS

Why? Because it is amazing out-of-the-box solution for Pythonists. Just have a quick look:

FilepondDjango

There is a Django REST Framework plugin for uploading pictures via Filepond components. It works amazing! Filepond has everything you might need for uploading pictures. Everything neatly works together and I am really happy after using it. Works on different browsers, integrates with Amazin S3 and has powerful options.

Django is known to be cumbersome when uploading pictures. Thank god we have a ready to use solution that neatly integrates with Filepond. There has been some issues when understanding the docs in my case that is why I decided to make a tutorial.

Goal: Integrate django-drf-filepond with vue-filepond

What we need to do:

  1. Install and customize Filepond VueJS
  2. Install and customize DRF filepond
  3. Prepare backend endpoint
  4. Upload picture extras
  5. Nuxt.JS filepond implementation.

1. Install and customize

Generally it is pretty straight-forward. You can follow the official docs at the official repo1. The most important part that is omitted there is that when installing and placing the VueJS component on your front-end remember to change name to filepond:

<file-pond
    name="filepond"
    ref="pond"
    label-idle="Drop file here..."
    v-bind:allow-multiple="false"
    accepted-file-types="image/jpeg, image/png, image/jpg"
    v-bind:files="myFiles"
    v-on:init="handleFilePondInit"                     <-- Example optional hook
    v-on:processfile="handleFilePondSuccessProcessed"  <-- Hook we use to get UID
    :server="serverOptions"
/>

Then handle plugins and set options for the component’s way of connecting with your backend according to the official docs. Check out my gist if you are having problems. 💁🏻

3. Install and customize DRF filepond

Install the pip package and add necessarry settings. Here is basic implementation for local and prod env:

if DEBUG:
    # Use a location on a local filesystem on the host server for file storage
    DJANGO_DRF_FILEPOND_FILE_STORE_PATH = os.path.join(
        BASE_DIR, 'stored_uploads')

if not DEBUG:
    # Required for FILEPOND
    AWS_ACCESS_KEY_ID = env('AWS_ACCESS_KEY_ID')
    AWS_SECRET_ACCESS_KEY = env('AWS_SECRET_ACCESS_KEY')
    AWS_STORAGE_BUCKET_NAME = env('AWS_STORAGE_BUCKET_NAME')
    AWS_AUTO_CREATE_BUCKET = True
    AWS_S3_REGION_NAME = 'eu-west-1'
    # AWS_DEFAULT_ACL = 'public-read'
    AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'

    # s3 static settings
    STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{AWS_STORAGE_BUCKET_NAME}/'
    STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

    # Use a remote file storage backend via the django-storages library
    #  for the Amazon S3 backend:
    DJANGO_DRF_FILEPOND_STORAGES_BACKEND = STATICFILES_STORAGE
    DJANGO_DRF_FILEPOND_FILE_STORE_PATH = None

    # DigitalOcean workaround - makes files expire in 20 years.
    AWS_QUERYSTRING_EXPIRE = 6220800000 # 20 years

    DEFAULT_FILE_STORAGE = 'core.storage_backends.PublicMediaStorage'

###############
# FILE UPLOAD #
###############

DJANGO_DRF_FILEPOND_UPLOAD_TMP = os.path.join(
    BASE_DIR, 'filepond-temp-uploads')

Specified settings for media storage:

#core/storage_backends.py

class PublicMediaStorage(BaseS3Storage):
    location = 'media'
    default_acl = 'public-read'
    file_overwrite = False

All this is basically AWS settings for django-storages==1.9.1 package. Last step here is adding urls.py

urlpatterns = [
    #...
    path('fp/', include('django_drf_filepond.urls')),
    # ...
]

3. Prepare backend endpoint

Once we have all this, we can start finally understanding how the basic file upload works. More detailed explanation of architecture you can find in docs but also in my github issue.

Basically: filepond saves the file and returns you UID which identifies the file. Then you can use this UID to fetch files from AWS (or your local storage when DEBUG=True). So the UID is the information you want to store in the DB in order to later retrieve the picture. Again - UID is obtained once filepond successfully uploads the picture using POST /process endpoint. This is done automatically and this action gives you UID.

In order to retrieve the picture call from POSTMAN:

{{MYLOCALORPRODURL}}/fetch/?target=UIDSAVEDIN_DB

Now let’s finally make the endpoint to see what happens.

class UploadPictureInformationAPIView(UpdateAPIView):


    def update(self, request, *args, **kwargs):
        # The upload_id is obtained from front-end after having successfully uploaded the picture.
        upload_id = request.data['upload_id']
        # Given a variable upload_id containing a 22-character unique file upload ID:
        if upload_id != '' and upload_id != None:
            su = store_upload(
                upload_id, destination_file_path='target_dir/{}.png'.format(upload_id))

        id = self.kwargs.get(self.lookup_url_kwarg)
        your_obj = YouObject.objects.filter(id=id).get()

         serializer = YouObjectSerializer(data=serializer, many=True)
        your_obj.is_valid()
        your_obj.save()
        # return Response(serializer.data, status=status.HTTP_201_CREATED)

This is your custom view:

urlpatterns = [
    path("/<int:id>", UploadPictureInformationAPIView.as_view(), name="upload-picture-info"),
]

So how to get the UID?

You can use filepond hook that fires up when picture has been uploaded. Your DOM has now UID of all pictures that have been uploaded so you can retrieve it:

handleFilePondSuccessProcessed: function() {
      // FilePond instance methods are available on `this.$refs.pond`
      this.upload_id = document.getElementsByClassName(
        "filepond--data"
      )[0].firstChild.value;
    },

Now we can use e.g. axios to send POST or PUT to our endpoint and include this.upload_id.

Let me know if that was helpful or if you want me to explain ir better / more.


Did you make any mistakes when using Filepond or you’ve seen one here? Tell me about your insights. Leave a comment with You are most welcome to see more posts of this type just go to home page

Here should be a newsletter

but trust me, you don't need another spam email that would distract your from reading a paper book. Enjoy reading books, not mails!