Search code examples
pythondjangoamazon-s3django-storagedjango-media

Django Storages using s3boto ignoring MEDIA_URL


I am trying to use django-storages with s3boto in my app and trying to serve media and static files from s3.

I have the following settings in my settings file:

AWS_STORAGE_BUCKET_NAME = '<bucket_name>'
AWS_S3_ACCESS_KEY_ID = '<access_key>'
AWS_S3_SECRET_ACCESS_KEY = '<secret>'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME

STATICFILES_LOCATION = 'static'
STATICFILES_STORAGE = '<custom_storage_satic>'

MEDIAFILES_LOCATION = 'media'
DEFAULT_FILE_STORAGE = '<custom_storage_media>'

And my custom_storages.py is

from django.conf import settings
from storages.backends.s3boto import S3BotoStorage

class StaticStorage(S3BotoStorage):
    location = settings.STATICFILES_LOCATION

class MediaStorage(S3BotoStorage):
    location = settings.MEDIAFILES_LOCATION

When I create an image in django, instead of getting the relative path to my image starting with

image.url
'/media/image/<rest_of_the_path>.jpg'

I am getting the absolute url, which is something like

image.url
'https://<s3_bucket_name>.s3.amazonaws.com/media/image/original/'

When I use local storage instead of s3boto, it works as expected and gives me the relative path. Am I missing something here?


Solution

  • I struck the same issue when attempting to use the Imgix CDN for my S3 media (I suspect we're both using the same tutorial based on your use of the custom_storages.py override).

    Here is an abridged version of the S3BotoStorage class in the django-storages framework. This excerpt highlights the important properties and methods for this issue, namely the custom-domain property.

    class S3BotoStorage(Storage):
        location = setting('AWS_LOCATION', '')
        custom_domain = setting('AWS_S3_CUSTOM_DOMAIN')
    
        def url(self, name, headers=None, response_headers=None, expire=None):
            # Preserve the trailing slash after normalizing the path.
            name = self._normalize_name(self._clean_name(name))
            if self.custom_domain:
                return "%s//%s/%s" % (self.url_protocol, self.custom_domain, filepath_to_uri(name))
    

    As you can see in the url method, a URL is generated to override the STATIC_URL and MEDIA_URL Django settings. Currently the domain of the URL is created with the AWS_S3_CUSTOM_DOMAIN setting, which is why you continue to see the static S3 URL for media files.

    So first, in your Django settings file, add a setting describing your CDN's domain.

    IMGIX_DOMAIN = 'example.imgix.net'
    

    Then, similar to the override of the location property, add an override to the custom_domain property in your MediaStorage class.

    class MediaStorage(S3BotoStorage):
        location = settings.MEDIAFILES_LOCATION
        custom_domain = settings.IMGIX_DOMAIN
    

    Now the final URL to your media files should begin with your CDN's domain, followed by the relative path to your file on the S3 bucket.