S3 storage for Django static, media & private files

First install these:

django-storages==1.6.5
django-uuid-upload-path==1.0.0
boto3==1.4.7

Add 'storages' to your settings.INSTALLED_APPS.

Add custom_storages.py to your project folder with this content:

from storages.backends.s3boto3 import S3Boto3Storage
from django.conf import settings


StaticS3BotoStorage = lambda: S3Boto3Storage(location=settings.S3_STATIC_DIRECTORY, querystring_auth=False)
MediaS3BotoStorage = lambda: S3Boto3Storage(location=settings.S3_MEDIA_DIRECTORY, querystring_auth=False)
PrivateS3BotoStorage = lambda: S3Boto3Storage(location=settings.S3_PRIVATE_DIRECTORY, querystring_auth=True)

Add the following to your settings.py:

DEFAULT_FILE_STORAGE = 'yourproject.custom_storages.MediaS3BotoStorage' 
STATICFILES_STORAGE = 'yourproject.custom_storages.StaticS3BotoStorage'

S3_MEDIA_DIRECTORY = 'media'
S3_STATIC_DIRECTORY = 'static'
S3_PRIVATE_DIRECTORY = 'private'
AWS_ACCESS_KEY_ID = 'YOURACCESSKEY' AWS_SECRET_ACCESS_KEY = 'YOURSECRETACCESSKEY' AWS_STORAGE_BUCKET_NAME = 'your-bucket-name' # I'd suggest <projectname>-production

Now make sure that on that S3 bucket:

Authenticated users have: Upload/Delete & View Permissions

Also make sure that the user with that ACCESS_KEY & SECRET_ACCESS_KEY has the policy: AmazonS3FullAccess.

Now you should be able to collectstatic files to S3 like so:

$ python src/manage.py collectstatic

For private files I'd suggest creating the custom FileField in project/fields.py:

from django.conf import settings
from django.db import models
from project.custom_storages import PrivateS3BotoStorage


class S3PrivateFileField(models.FileField):

def __init__(self, verbose_name=None, name=None, upload_to='', storage=PrivateS3BotoStorage(), **kwargs):
# When running tests, we use the normal storage instead of S3
if settings.DEBUG or hasattr(settings, 'RUNNING_TESTS') and settings.RUNNING_TESTS:
storage = None

super(S3PrivateFileField, self).__init__(verbose_name=verbose_name,
name=name, upload_to=upload_to, storage=storage, **kwargs)
self.storage.default_acl = 'private'

This way all uploaded files with that field will be made privately (you'll need authentication parameters to be able to download it)
To make sure your files don't get overriden when users use the same filename (this is default behaviour for the S3 storage (!)), use this:

from django.db import models
from project.fields import S3PrivateFileField
from uuid_upload_path import upload_to_factory

# located here for Django migrations
def upload_path(*args):
return upload_to_factory('originals')(*args)

Class YourModel(models.Model):    
original_document = S3PrivateFileField(_('original document'), upload_to=upload_path)