AWS S3 setup
On AWS
Create a new bucket on AWS S3.
Uncheck
Block all public access
.Click
Create bucket
.Go to the bucket's properties tab and select
Static website hosting
(Use this bucket to host a website).Enter the defaults
index.html
anderror.html
and clicksave
.Go to
permissions
and thenCORS configurations
.Enter one in JSON format. Such as:
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "POST", "PUT" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [], "MaxAgeSeconds": 3000 } ]
Go to the
Bucket policy
tab and selectPolicy generator
:- Select Type of Policy:
S3 Bucket Policy
- Principal:
*
- AWS Service:
Amazon S3
- Actions:
Get object
- Amazon Resource Name (ARN): copy and paste the
ARN
from the bucket. Add statement
Generate Policy
- Copy the policy and paste it in the bucket policy tab, making sure to add a
/*
at the end of the resource key
- Select Type of Policy:
Go to the
Access Controls List
tab and set the list objects permissions selected foreveryone
and save.In a new tab go to
IAM
in AWS.Select
Groups
and create a new one called whatever you want, preferably related to our project.Click through and finally
Create group
.Click on
Policies
on the meny on the left, and thenCreate policy
.Select the JSON tab and click on
Import managed policy
.Search and import
AmazonS3FullAccesss
policy.Replace
"*"
with your ARN followed by the ARN/*, such as:{ "Resource": [ "your_ARN", "your_ARN/*" ] }
Click on
Review Policy
and give it a name and description, then clickCreate policy
.Click on gourps, select your previously created group, click
Attach policy
, search for the just created policy, select it and click onAttach policy
.Go to
Users
, add an user and give it a related name.Give it
Programmatic access
underAccess type
, click onNext: permissions
and select the group created previously, clicking through the end until clickingCreate user
.Download the
.csv
file which contains the keys necessary (that's the only opportunity to save that information!) and click onclose
.
On the code editor
Install
boto3
anddjango-storages
with pip.add
storages
to your installed apps.in settings.py, add:
if "USE_AWS" in os.environ: AWS_S3_OBJECT_PARAMETERS = { "Expires": "Thu, 31 Dec 2099 20:00:00 GMT", "CacheControl": "max-age=94608000", } AWS_STORAGE_BUCKET_NAME = "your_bucket_name" AWS_S3_REGION_NAME = "eu-central-1" # or your timezone AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID") # to be added to Heroku - take from .csv AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY") # to be added to Heroku - take from .csv AWS_S3_CUSTOM_DOMAIN = f"{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com"
Add the two keys to Heroku's variables, plus
USE_AWS=True
.Create a fille at root level in the app called
custom_storages.py
:from django.conf import settings from storages.backends.s3boto3 import S3Boto3Storage class StaticStorage(S3Boto3Storage): location = settings.STATICFILES_LOCATION class MediaStorage(S3Boto3Storage): location = settings.MEDIAFILES_LOCATION
Continuing on settings.py:
STATICFILES_STORAGE = "custom_storages.StaticStorage" # uses the class just created STATICFILES_LOCATION = "static" DEFAULT_FILE_STORAGE = "custom_storages.MediaStorage" # uses the class just created MEDIAFILES_LOCATION = "media" STATIC_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/{STATICFILES_LOCATION}/" # explicitly declares and overrides location MEDIA_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/{MEDIAFILES_LOCATION}/" # explicitly declares and overrides location
Add in heroku the variable
DISABLE_COLLECTSTATIC=1
so it won't collect it automatically. You can manually collect it withheroku run bash
andpython manage.py collectstatic
. This prevents collecting static on each deploy.Run
collectstatic
as instructed in 7, and check that AWS created the static folder.Add optional setting on
settings.py
:AWS_S3_OBJECT_PARAMETERS = { "Expires": "Thu, 31 Dec 2099 20:00:00 GMT", "CacheControl": "max-age=94608000", }
Back in AWS, open the bucket and create a folder called
media
.Copy to it all media files present in your project.
If prompted, grant public permissions to the files to be uploaded.
Add email variables to settings.py and Heroku in order to receive emails related to account management:
if "DEBUG" in os.environ: EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" DEFAULT_FROM_EMAIL = "brachetta@me.com" else: EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" EMAIL_USE_TLS = True EMAIL_PORT = 587 EMAIL_HOST = "smtp.strato.com" EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASS") DEFAULT_FROM_EMAIL = os.environ.get("EMAIL_HOST_USER")
The resulting settings in
settings.py
should be:if "USE_AWS" in os.environ: AWS_S3_OBJECT_PARAMETERS = { "Expires": "Thu, 31 Dec 2099 20:00:00 GMT", "CacheControl": "max-age=94608000", } AWS_STORAGE_BUCKET_NAME = "your_bucket" AWS_S3_REGION_NAME = "eu-central-1" AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID") AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY") AWS_S3_CUSTOM_DOMAIN = f"{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com" STATICFILES_STORAGE = "custom_storages.StaticStorage" STATICFILES_LOCATION = "static" DEFAULT_FILE_STORAGE = "custom_storages.MediaStorage" MEDIAFILES_LOCATION = "media" STATIC_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/{STATICFILES_LOCATION}/" MEDIA_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/{MEDIAFILES_LOCATION}/" STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") STATIC_URL = "/static/" STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),) MEDIA_URL = "/media/" MEDIA_ROOT = os.path.join(BASE_DIR, "media") MESSAGE_TAGS = {messages.ERROR: "danger"} if "DEVELOPMENT" in os.environ: EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" DEFAULT_FROM_EMAIL = "brachetta@me.com" else: EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" EMAIL_USE_TLS = True EMAIL_PORT = 587 EMAIL_HOST = "smtp.strato.com" # your email host here EMAIL_HOST_USER = os.environ.get("EMAIL_HOST_USER") EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASS") DEFAULT_FROM_EMAIL = os.environ.get("EMAIL_HOST_USER")
Remember to add the static folder location to the main
urls.py
:urlpatterns = [ ... ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)