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.htmlanderror.htmland clicksave.Go to
permissionsand thenCORS configurations.Enter one in JSON format. Such as:
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "POST", "PUT" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [], "MaxAgeSeconds": 3000 } ]Go to the
Bucket policytab 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 
ARNfrom the bucket. Add statementGenerate 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 Listtab and set the list objects permissions selected foreveryoneand save.In a new tab go to
IAMin AWS.Select
Groupsand create a new one called whatever you want, preferably related to our project.Click through and finally
Create group.Click on
Policieson the meny on the left, and thenCreate policy.Select the JSON tab and click on
Import managed policy.Search and import
AmazonS3FullAccessspolicy.Replace
"*"with your ARN followed by the ARN/*, such as:{ "Resource": [ "your_ARN", "your_ARN/*" ] }Click on
Review Policyand 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 accessunderAccess type, click onNext: permissionsand select the group created previously, clicking through the end until clickingCreate user.Download the
.csvfile which contains the keys necessary (that's the only opportunity to save that information!) and click onclose.
On the code editor
Install
boto3anddjango-storageswith pip.add
storagesto 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_LOCATIONContinuing 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 locationAdd in heroku the variable
DISABLE_COLLECTSTATIC=1so it won't collect it automatically. You can manually collect it withheroku run bashandpython manage.py collectstatic. This prevents collecting static on each deploy.Run
collectstaticas 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.pyshould 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)