How to setup AWS S3 storage backend for File Upload

Django delegates the files handing to the storage backend. By default Django uses the FileSystemStorage to store files on local filesystem. It is possible to customize storage system to use different storage backend such as AWS S3. This article will walk you through how to configure Django to use AWS S3 as storage backend.

Install the following python libraries that are dependencies

   pip install boto3 django-storages 

Above command install both boto3 and django-storages libraries

Following are essential settings that are need to be added to settings.py module

Add AWS IAM user Access Key and Access Secret Key

AWS_ACCESS_KEY_ID = <API_ACCESS_KEY>
AWS_SECRET_ACCESS_KEY = <API_SECRET_ACCESS_KEY>

Setup the DEFAULT_FILE_STORAGE, so that by default all uploads will use AWS S3 storage unless specified

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

Set following two settings for default bucket name region

AWS_STORAGE_BUCKET_NAME = <Bucket Name>
AWS_S3_REGION_NAME = 'eu-west-2'

If you don’t want AWS S3 storage to be default, it is also possible set storage for specific field as follows

from django.db import models

from  storages.backends.s3boto3 import S3Boto3Storage

s3storage = S3Boto3Storage()

class Car(models.Model):
    ...
    photo = models.ImageField(storage=s3storage)

Staring from Django 3.1, it is also possible dynamically select the storage backend using callable as follows

from django.db import models
from .storages import MyLocalStorage, MyS3Storage


def select_storage():
    return MyS3Storage() if settings.DEBUG else MyLocalStorage()


class MyModel(models.Model):
    my_file = models.FileField(storage=select_storage)

Here is the sample view that saves received (uploaded) files and returns back the URL of objects created in S3 Bucket

@require_POST
def fileUpload(request):
    user = request.user   # request.user is set by auth backend(session or token) on successful authentication
    urls = []
    for name, fobj in request.FILES.items():
        obj = Uploads.objects.create(user=user)
        obj.media.save(name, fobj)
        urls.append(obj.media.url)
    return HttpResponse(
        {
            "status": "OK",
            "files": urls
        }
    )

Related model

class UserUpload(models.Model):
    user = models.ForeignKey(User, related_name="uploads", on_delete=models.PROTECT)
    created = models.DateTimeField(auto_now_add=True)
    media = models.FileField(null=True)

It is possible to set bucket and location on fly by accessing storage object on FileField or ImageField as follows

# Assume obj is the model instance which contain the media(FileField) field 
obj.media.storage.location = 'users/media'    
obj.media.storage.bucket_name = <BUCKET NAME> 

Where obj is the model instance and media is the FileField through which accessing storage to set the location (that is folder) to store/save uploaded file

Or set the location per field using upload_to keyword argument

media = models.ImageField(upload_to='avatars')

When working with multiple buckets, we can specify the bucket name itself int he model field as follows

# module models.py 

from django.db import models
from storages.backends.s3boto import S3BotoStorage

class UserModel(models.Model):
    name = models.CharField() 
    myfile_1 = models.FileField()  # uses the default storage
    myfile_2 = models.FileField(storage=S3BotoStorage(bucket='bucket_2')) # Override the default bucket 

Default image
neotam
Naveen T aka neotam. Programming language agnostic, Software architect, Python expert, Networking & DevOps engineer & consultant with 7+ years of experience in creating serious web applications, real time event-driven non blocking applications and database driven applications ranging from small scale to enterprise grade. website
Leave a Reply