Django serving static files - django

I'm trying to configure my django setup to serve static files in debug and production mode. I want in production mode to be from S3 and in debug mode to be from the local installation. I have the settings as below:
COMPRESS_ENABLED = not DEBUG
COMPRESS_PARSER = 'compressor.parser.LxmlParser'
COMPRESS_CSS_FILTERS = ['compressor.filters.cssmin.CSSMinFilter']
COMPRESS_JS_FILTERS = ['compressor.filters.jsmin.JSMinFilter']
COMPRESS_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
DEFAULT_FILE_STORAGE = 'libs.storages.S3Storage.S3Storage'
AWS_ACCESS_KEY_ID = # Key
AWS_SECRET_ACCESS_KEY = # Secret
AWS_STORAGE_BUCKET_NAME = # Bucket
AWS_QUERYSTRING_AUTH = False
MEDIA_URL = '/media/'
if COMPRESS_ENABLED:
from boto.s3.connection import SubdomainCallingFormat
AWS_S3_CALLING_FORMAT = SubdomainCallingFormat()
COMPRESS_OFFLINE = True
STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
# Path for all static files
STATIC_URL = 'https://%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME
STATIC_ROOT = STATIC_URL
else:
STATIC_ROOT = path.join(PROJECT_ROOT, '..', 'assets')
STATIC_URL = '/static/'
I'm able to get everything to work except one tiny thing which I'm not able to figure out. Often times my CSS files will have a background url as /images/logo.png. The folder /images/ exists in S3 but for my local it needs to be /static/images/. I tried to set the S3 URL as /static/ at the end but it wasn't working. Is there anything else i'm missing? How do I get the CSS image urls to redirect to /static/?

I read through the source code of s3boto.py. You can set the following property:
AWS_LOCATION = "static"
That variable creates a static folder in your s3. The other properties should be:
STATIC_URL = 'https://%s.s3.amazonaws.com/static/' % AWS_STORAGE_BUCKET_NAME
STATIC_ROOT = STATIC_URL

Related

Django storages using incorrect urls for s3 static files

I am using Django storages to host my static files (css, js, images) on s3. When I load my webpage django keeps pointing to the incorrect url of my s3 public bucket. For example, it keeps returning https://mysite.amazonaws.com/assets/images/cat.png despite the correct public url for the file being https://mysite.s3-ap-southeast-2.amazonaws.com/assets/images/cat.png
settings.py
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_ACCESS_KEY_ID = os.environ.get('aws_access_key_id')
AWS_SECRET_ACCESS_KEY = os.environ.get('aws_secret_key')
AWS_STORAGE_BUCKET_NAME = 'mysite'
AWS_DEFAULT_ACL = "private"
AWS_S3_SIGNATURE_VERSION = "s3v4"
AWS_S3_REGION_NAME = 'ap-southeast-2'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
STATIC_URL = 'https://mysite.s3-ap-southeast-2.amazonaws.com/static/'
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
Try setting a s3 custom domain.
AWS_S3_CUSTOM_DOMAIN = 'mysite.s3-ap-southeast-2.amazonaws.com'

How to load static files from separate S3 bucket when hosting site on Elastic Beanstalk Aws React Django

Setup: React+Django hosted on Elastic Beanstalk. Static files hosted on separate S3 Bucket.
I'm trying to load an image using src="/static/images/logo.png".
In development it works perfectly, but in production it sends a request to XXX.elasticbeanstalk.com/static/images/logo.png while it should be requesting Bucket.amazonaws.com/static/images/logo.png.
Meanwhile the user uploaded media is working perfectly for both POST and GET requests, images are stored and fetched from the Bucket /media/ path.
I want to avoid conditionally coding the absolute url path depending on the environment.
I have a django production_settings.py file:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_ROOT = os.path.join(BASE_DIR, "..", "www", "static")
STATIC_URL = '/static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
AWS_STORAGE_BUCKET_NAME = '****************'
AWS_S3_REGION_NAME = '****************'
AWS_ACCESS_KEY_ID = '****************'
AWS_SECRET_ACCESS_KEY = '****************'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
STATICFILES_LOCATION = 'static'
STATICFILES_STORAGE = 'custom_storages.StaticStorage'
MEDIAFILES_LOCATION = 'media'
DEFAULT_FILE_STORAGE = 'custom_storages.MediaStorage'
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
Thanks
It seems like your STATIC_URL set incorrectly. It refers to your web app project path instead of S3 bucket URL. You can try using this below:
STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, STATICFILES_LOCATION)
You have already extended S3Boto3Storage and you could see there is multiple parameters, you could set additional env key for static storage class:
class StaticStorage(S3Boto3Storage):
location = settings.STATICFILES_LOCATION
custom_domain = setting('AWS_S3_CUSTOM_DOMAIN_STATIC')

django's collectstatic collects into unexpected directory

I want to upload my staticfiles to amazon s3 storage, but I can't stop django from just putting them in a directory staticfiles in the project root. I have boto3 in my requirements.txt and have set
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
I have successfully set up s3 with media files, to the same bucket (under different directory).
Collectstatic also seems disregards the STATIC_ROOT variable in settings.py when I comment out the s3-settings. When running python3 manage.py collectstatic I expect it to gather static files into STATIC_ROOT as it says in the docs here https://docs.djangoproject.com/en/2.1/ref/settings/ (under static_root). But even if I give a different value to STATIC_ROOT, collectstatic always collects into a directory on the root called staticfiles.
STATIC_ROOT = os.path.join(BASE_DIR, 'this_is_not_used_by_collectstatic')
The rest of my s3 settings:
# Amazon S3
AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY']
AWS_STORAGE_BUCKET_NAME = 'my_bucket_name'
AWS_S3_REGION_NAME = 'eu-north-1'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
}
AWS_DEFAULT_ACL = None
AWS_LOCATION = 'static'
STATIC_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
# media
DEFAULT_FILE_STORAGE = 'korvir.storage_backends.MediaStorage'
If you're using django-heroku and and you have the following in your code, as per the instructions:
django_heroku.settings(locals())
... then it will overwrite your STATIC_ROOT setting. If you want to preserve your own STATIC_ROOT setting, then make sure you set STATIC_ROOT after calling django_heroku.settings.

Django: serve admin static files with whitenoise and site's files from S3

I have a working Django site where I've been using whitenoise to serve static files.
I've then switched to S3 storage for both site static and media. And it works really well.
I have one problem with this setup though: collecting the admin static files (select2, tinyMCE, etc) takes lots of times and regularly fails in Heroku.
So here's my ideal scenario:
keep everything as is BUT
DO NOT upload the admin static files to S3 (during collectsatic) and serve them from the app server using whitenoise
So how do I achieve that?
Here's the relevant excerpt from my settings file
ENVIRONMENT = os.environ.get('DJANGO_ENVIRONMENT', 'dev')
IS_PROD = ENVIRONMENT == 'production'
IS_DEV = not IS_PROD
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DEBUG = bool(os.environ.get('DJANGO_DEBUG', IS_DEV))
# Static files (CSS, JavaScript, Images) and media
STATIC_URL = '/static/'
STATIC_ROOT = '.static'
if IS_PROD:
AWS_STORAGE_BUCKET_NAME = os.environ.get('S3_BUCKET', '')
AWS_S3_REGION_NAME = os.environ.get('S3_REGION', '')
AWS_ACCESS_KEY_ID = os.environ.get('S3_KEY_ID', '')
AWS_SECRET_ACCESS_KEY = os.environ.get('S3_SECRET', '')
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
AWS_S3_OBJECT_PARAMETERS = {
'Expires': 'Thu, 31 Dec 2100 20:00:00 GMT',
'CacheControl': 'max-age=94608000',
}
STATICFILES_LOCATION = 'static'
STATICFILES_STORAGE = 'custom_storages.StaticStorage'
MEDIAFILES_LOCATION = 'media'
DEFAULT_FILE_STORAGE = 'custom_storages.MediaStorage'
MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{MEDIAFILES_LOCATION}/'
TINYMCE_JS_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/static/tiny_mce/tiny_mce.js'
else:
MEDIA_URL = '/media/'
MEDIA_ROOT = '.media'
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
]

Django. Alternate between local & remote staticfiles

After collecting my staticfiles and storing them in an Amazon Bucket (AWS S3), when I run the project locally it still uses the staticfiles stored online, this is a problem cause when I want to make a change on a css file for ex, I have to run collectstatic or manually upload the file to Amazon. I tried adding a new setting variable "LOCAL_STATICFILES" like this:
settings.py
LOCAL_STATICFILES = False
if not LOCAL_STATICFILES:
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
AWS_ACCESS_KEY_ID = os.environ['AWSAccessKeyId']
AWS_SECRET_ACCESS_KEY = os.environ['AWSSecretKey']
AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME']
S3_URL = 'http://%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME
STATIC_URL = S3_URL
STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
if LOCAL_STATICFILES:
STATIC_URL = '/static/'
STATIC_ROOT = '/'
But when I turn LOCAL_STATICFILES to True and runserver, django can't find them.
The project's folders look like this:
project
app
app
static
css
js
img
templates
What am I doing wrong?
First of all: Ensure you have a way to distinguish whether you are, or not, in an environment supporting the Amazon bucket configuration. This means, usually this will be your production environment, where you already configured the amazon bucket settings.
So you will:
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# this is the base project path
if 'AWSAccessKeyId' in os.environ:
# assume the presence of this key will determine whether
# we are, or not, in the bucket-supporting environment
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
AWS_ACCESS_KEY_ID = os.environ['AWSAccessKeyId']
AWS_SECRET_ACCESS_KEY = os.environ['AWSSecretKey']
AWS_STORAGE_BUCKET_NAME = os.environ['AWS_STORAGE_BUCKET_NAME']
STATIC_URL = 'http://%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME
# static url will be the re
STATIC_ROOT = None
# STATIC_ROOT doesn't matter since you will not invoke
# `manage.py collectstatic` from this environment. You
# can safely let it to None, or anything like:
# os.path.join(BASE_DIR, 'static')
else:
STATIC_URL = '/static/'
# the static files url will point to your local,
# development, server
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
# here, STATIC_ROOT matters, since you will invoke
# `manage.py collectstatic` from your local environment.