Adding S3 Resource Storage
There are a number of times when you might want to use S3 for storage rather than local storage. For example to add media storage for a field in a database table, or to store static files. This will walk you through the additional configuration required.
Setup
Using elastic beanstalk
When using Elastic Beanstalk, you don't need to host the static files separately, and instead, EB will handle the file serving for you. All you need to do is to include this in your .ebextensions/django.config
file:
1 2 |
|
The options should remain set for local static files in your settings.py
1 2 3 4 5 |
|
S3 Bucket
Create an S3 bucket using all the default settings.
If you want to use S3 for static file storage too, you will need to allow public access on your bucket. Once you have switched off the setting on the bucket to 'block all public access', apply this policy:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
If you are just hosting private media files, the S3 bucket you create does not need public access. Access to it is controlled via the access keys given to Django.
To create an IAM user with the right access to this bucket, you need to:
- Create an IAM user with Programmatic access
- Add this policy inline to the IAM user (make sure to change
my-bucket-name
to your bucket):1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObjectAcl", "s3:GetObject", "s3:ListBucket", "s3:DeleteObject", "s3:PutObjectAcl" ], "Resource": [ "arn:aws:s3:::my-bucket-name/*", "arn:aws:s3:::my-bucket-name" ] } ] }
- Make sure to keep the access key and secret access key safe as you'll need these.
Packages
First, you need to set the app up to use the storages package to handle the file transfer. To do this, you need to update two files:
Update the packages used
You'll need to install django-storages and boto3:
1 |
|
Make sure to include them in your requirements.in
file too:
1 2 3 4 5 6 |
|
and re-compile your requirements.txt
document using
1 |
|
Include storages
within your list of INSTALLED_APPS in the Django settings.
1 2 3 4 5 6 |
|
Environment variables
Environment Variables
If you are running it locally, you will need to export them to your current environment. This will create a temporary environment variable. It will only exist for that terminal session, but is available immediately.
1 2 3 4 |
|
Check it's working by using:
1 |
|
On Windows, you'll need to restart the command prompt for this change to to effect. If using the terminal in VSCode, this will include restarting VSCode itself.
1 2 3 4 |
|
Check it's working by using:
1 |
|
If this doesn't work, you can use the set
command instead of setx
and this will create a temporary environment variable. It will only exist for that terminal session, but is available immediately.
1 2 3 4 |
|
Add these to your .ebextensions/environment.config
file:
1 2 3 4 |
|
Giving Django access
In your settings.py
file, you now nees to update the settings to make use of these variables, and get the app ready to go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Media Files
Storage Backends
Private Media
To handle private media, we need to create a Storage backend. To do this, create a file in app/app/storage_backends.py
with the following:
Create a private storage backend
1 2 3 4 5 6 7 8 |
|
Files in this bucket will be accessible via an expirable link (the expiry time is defined within settings).
Static Files
To handle public static files, a slightly different backend is required. In the same storage_backends.py
file, you need to include:
Create a public storage backend
1 2 3 4 5 |
|
Django settings.py
Private Media
Change the media settings in your app/app/settings.py
file to use this new backend.
You'll need to update the MEDIA_URL setting, and add the storage backend:
1 2 3 4 |
|
Static Files
Change the static settings in your app/app/settings.py
file to use the new backend.
You'll need to update add the storage backend:
1 2 3 |
|
Usage
Collect Static Files
To collect and upload all static files to S3, you will need to run:
1 |
|
Django’s collect static can be very slow when you will use S3 as storage. For that, you can use Collectfast to make this faster. Install it using pip install Collectfast
. Then update the app/app/settings.py
like this:
1 2 3 4 5 6 7 8 |
|
Remember to include Collectfast in your requirements-dev.txt
file if you are using it.
Add file storage as a field
In your models.py
file that contains the model you want to add the field to, add the PrivateMediaStorage import:
1 2 3 |
|
Then you can add it as a field, using the FileField built into Django:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Applying these changes by running:
1 2 |
|
Testing
You can check that everything has connected as expected by using the admin panel to upload a file. Simply run the Django server and visit http://127.0.0.1:8000/admin.
API access
You can also use Django Rest Framework to create an API quickly that returns you the url of the required media.
Backend api
1 2 3 4 5 6 7 8 9 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
All results would be returned here by visiting http://127.0.0.1:8000/api/v1/exampleData
or a single one using http://127.0.0.1:8000/api/v1/exampleData?id=1
.
The response is a timed URL (the timeout is based on the setting AWS_QUERYSTRING_EXPIRE).
Sample Response
1 2 3 4 5 6 |
|