Exploring django-storages with S3

django-storages allows you to replace the default file-system storage with S3 or other cloud-provider block storage solutions. In this article, we explore this package by setting it up in a new Django project. I assume some familiarity with Django and S3.

First, let's get our project setup and our dependencies installed:

python3 -m virtualenv venv
source venv/bin/activate
pip install django
pip install django-storages[s3]
pip install python-dotenv
django-admin startproject proj .
./manage.py startapp myapp

Next, let's add myapp to proj/settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp', # new
]

Follow along with this testdriven.io article to get your bucket and user setup on AWS. I found though that the following settings are a little quicker and more up-to-date for getting django-storages setup.

Add environment variables to .env:

AWS_ACCESS_KEY_ID=<access_key>
AWS_SECRET_ACCESS_KEY=<secret_access_key>
AWS_STORAGE_BUCKET_NAME=<bucket_name>

Setup STORAGES in settings:

import os
from dotenv import load_dotenv

load_dotenv()

...

# django-storages
# https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html
STORAGES = {
        "default": {
            "BACKEND": "storages.backends.s3.S3Storage",
            "OPTIONS": {
                "access_key": os.environ['AWS_ACCESS_KEY_ID'],
                "secret_key": os.environ['AWS_SECRET_ACCESS_KEY'],
                "bucket_name": os.environ['AWS_STORAGE_BUCKET_NAME'],
            },
        },
        "staticfiles": {
            "BACKEND": "storages.backends.s3.S3Storage",
            "OPTIONS": {
                "access_key": os.environ['AWS_ACCESS_KEY_ID'],
                "secret_key": os.environ['AWS_SECRET_ACCESS_KEY'],
                "bucket_name": os.environ['AWS_STORAGE_BUCKET_NAME'],
            },
        }
}

And here's a simple model we can use for testing

from django.db import models


class MyModel(models.Model):
    upload = models.FileField()

Don't forget to make migrations and migrate.

Dump some contents into hello.txt: echo "hello" > hello.txt.

Finally, from the shell, we can upload a file to S3:

>>> from django.core.files import File
>>> f = File(open('hello.txt', 'rb'))
>>> from myapp.models import MyModel
>>> m = MyModel(upload=f)
>>> m.upload.save('hello.txt', f)
>>> m.save()
>>> m
<MyModel: MyModel object (1)>
>>> m.upload
<FieldFile: hello.txt>
>>> m.refresh_from_db()
>>> m.upload
<FieldFile: hello.txt>
>>> m.upload.url
'https://<bucket-name>.s3.us-east-2.amazonaws.com/hello.txt?AWSAccessKeyId=...'
>>> m.upload.file
<S3File: hello.txt>
>>> m.upload.file.read()
b'hello\n'
>>> m.upload.file.read()
b''
>>> m.upload.file.seek(0)
0
>>> quit()

I hope that this brief article gave you an idea for how to setup django-storages in your Django project and how to use it.