# Storage 4.2: how to subclass default

**URL:** <https://forum.djangoproject.com/t/storage-4-2-how-to-subclass-default/29190>\
**Category:** Deployment\
**Created:** [March 17, 2024, 4:49pm UTC](https://forum.djangoproject.com/t/storage-4-2-how-to-subclass-default/29190 "2024-03-17T16:49:57Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Swannbm](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/swannbm/32/19983_2.png) [@Swannbm](https://forum.djangoproject.com/u/Swannbm)\
**Post date:** [March 17, 2024, 4:49pm UTC](https://forum.djangoproject.com/t/storage-4-2-how-to-subclass-default/29190/1 "2024-03-17T16:49:57Z")

</div>

Hello fellowes,

I am discovering new Django 4.2 storages settings. I want to have a main file system (default) then I would like to customise media folder on top of it (add media subfolder to all uploaded files and public-read ACL).

Before, I would have made a custom storage class inheriting of S3Boto… But it doesn’t work anymore (myabe I am doing something wrong).

I’ve endup doing below code but I don’t find that elegant.

Do you see another way to do it ?

```auto
DEFAULT_S3_STORAGE_OPTIONS = {
    "access_key": env.str("OVH_ACCESS_KEY"),
    "secret_key": env.str("OVH_SECRET_KEY"),
    "bucket_name": env.str("BUCKET_NAME"),
    "region_name": env.str("BUCKET_REGION"),
    "endpoint_url": "https://s3.gra.io.cloud.ovh.net",
    "custom_domain": f"{env.str('BUCKET_NAME')}.s3.gra.io.cloud.ovh.net",
}
STORAGES = {
    "default": {
        "BACKEND": "storages.backends.s3.S3Storage",
        "OPTIONS": {
            **DEFAULT_S3_STORAGE_OPTIONS,
        },
    },
    "public_media": {
        "BACKEND": "storages.backends.s3.S3Storage",
        "OPTIONS": {
            **DEFAULT_S3_STORAGE_OPTIONS,
            "location": "media",
            "default_acl": "public-read",
            "file_overwrite": True,
            "querystring_auth": False,
        },
    },
    "staticfiles": {
        "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
    },
}

```

++  
Swann

---

<div class="post-metadata">

**Author:** ![leandrodesouzadev](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/leandrodesouzadev/32/8981_2.png) [@leandrodesouzadev](https://forum.djangoproject.com/u/leandrodesouzadev)\
**Post date:** [March 18, 2024, 10:34am UTC](https://forum.djangoproject.com/t/storage-4-2-how-to-subclass-default/29190/2 "2024-03-18T10:34:21Z")

</div>

Hey there!  
I don’t know if i got your question right, but i will show up how i configured public/private media on s3 with django-storages.

Versions used:

```auto
Django==4.2
django-storages==1.13.2

```

```python
## my settings.py (partial)
# Storages
DEFAULT_FILE_STORAGE_BACKEND = env(
    "DEFAULT_FILE_STORAGE_BACKEND",
    default="django.core.files.storage.FileSystemStorage",
)
STORAGES = {
    "default": {"BACKEND": DEFAULT_FILE_STORAGE_BACKEND},
    "public_media": {"BACKEND": DEFAULT_FILE_STORAGE_BACKEND},
    "staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"},
}
AWS_ACCESS_KEY_ID = env.str("AWS_ACCESS_KEY_ID", default=None)
AWS_SECRET_ACCESS_KEY = env.str("AWS_SECRET_ACCESS_KEY", default=None)
AWS_REGION_NAME = env.str("AWS_REGION_NAME", default=None)

if DEFAULT_FILE_STORAGE_BACKEND == "app.ext.storage.aws_s3.PrivateMediaStorage":
    if AWS_ACCESS_KEY_ID is None or AWS_SECRET_ACCESS_KEY is None:
        raise ImproperlyConfigured("Missing AWS credentials")

    STORAGES["public_media"].update(BACKEND="app.ext.storage.aws_s3.PublicMediaStorage")
    INSTALLED_APPS.append("storages")
    AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME", str)
    AWS_S3_CUSTOM_DOMAIN = f"{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com"
    AWS_S3_OBJECT_PARAMETERS = {
        "CacheControl": "max-age=86400",
    }
    AWS_MEDIA_LOCATION = "media/"

```

And storages:

```python
## app/ext/storage/aws_s3.py

from django.conf import settings
from storages.backends.s3boto3 import S3Boto3Storage

class PrivateMediaStorage(S3Boto3Storage):
    """Stores privately on Aws S3"""

    location = getattr(settings, "AWS_MEDIA_LOCATION", "media")
    default_acl = "private"
    file_overwrite = False
    custom_domain = False

class PublicMediaStorage(S3Boto3Storage):
    """Stores public on Aws S3"""

    location = getattr(settings, "AWS_MEDIA_LOCATION", "media")
    default_acl = "public-read"
    file_overwrite = False
    custom_domain = False
    querystring_auth = False

```

The way it works for me is, set the default storage backend only, in my case i set it to use the private media by default, that being `app.ext.storage.aws_s3.PrivateMediaStorage` specifically. So when i need to use the public\_media i use this function on the file/image fields:

```python
## app/ext/storage/utils.py

from django.core.files.storage import storages

def get_public_media_storage():
    return storages["public_media"]

```

Like so:

```python
class SomeModel(models.Model):
    picture = models.ImageField(
        verbose_name=_("Picture"),
        upload_to="events/events_pictures",
        storage=get_public_media_storage,
    )

```

---

<div class="post-metadata">

**Author:** ![Swannbm](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/swannbm/32/19983_2.png) [@Swannbm](https://forum.djangoproject.com/u/Swannbm)\
**Post date:** [March 20, 2024, 9:08pm UTC](https://forum.djangoproject.com/t/storage-4-2-how-to-subclass-default/29190/3 "2024-03-20T21:08:40Z")

</div>

Yes, thank you for replying leandrodesouzadev. It was also my way to do it before 4.2 and the STORAGES settings.

In documentation it says “A dictionary containing the settings for all storages to be used with Django.” which I emphasis as we shouldn’t have to create PrivateMediaStorage or PublicMediaStorage classes somewhere else. The idea, as far as I understand, is to get all storages in one place (in the settings, which sound very good).

Moreover, I think prefixed AWS\_… settings should be avoided if possible. It’s better because there are plenty of other S3 providers nowadays and the naming could be confusing. This makes the STORAGES settings pretty good to me.

But I really don’t like not being DRY and I can’t see another way to do it than the solution I posted, which doesn’t look very good to me right now. But I don’t see how to do it differently…

I imaginated, instead of making new class, would be to reference a previously defined storage in the BACKEND option.

Example:

```auto
DEFAULT_S3_STORAGE_OPTIONS = {
    
}
STORAGES = {
    "default": {
        "BACKEND": "storages.backends.s3.S3Storage",
        "OPTIONS": {
            "access_key": env.str("OVH_ACCESS_KEY"),
            "secret_key": env.str("OVH_SECRET_KEY"),
            "bucket_name": env.str("BUCKET_NAME"),
            "region_name": env.str("BUCKET_REGION"),
            "endpoint_url": "https://s3.gra.io.cloud.ovh.net",
            "custom_domain": f"{env.str('BUCKET_NAME')}.s3.gra.io.cloud.ovh.net",
        },
    },
    "public_media": {
        "BACKEND": "default", # reuse previously defined default with same options
        "OPTIONS": {
            # add or override options defined in default
            "location": "media",
            "default_acl": "public-read",
            "file_overwrite": True,
            "querystring_auth": False,
        },
    },
    "staticfiles": {
        "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
    },
}

```

Do you think it would make sense ?  
Or do you have some other idea ?

---

<div class="post-metadata">

**Author:** ![leandrodesouzadev](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/leandrodesouzadev/32/8981_2.png) [@leandrodesouzadev](https://forum.djangoproject.com/u/leandrodesouzadev)\
**Post date:** [March 21, 2024, 1:41pm UTC](https://forum.djangoproject.com/t/storage-4-2-how-to-subclass-default/29190/4 "2024-03-21T13:41:04Z")

</div>

Well, if you to go that way, then i believe you can get around doing this:

```python

default_storage_backend = "storages.backends.s3.S3Storage"
default_storage_options = {
    "access_key": env.str("OVH_ACCESS_KEY"),
    "secret_key": env.str("OVH_SECRET_KEY"),
    "bucket_name": env.str("BUCKET_NAME"),
    "region_name": env.str("BUCKET_REGION"),
    "endpoint_url": "https://s3.gra.io.cloud.ovh.net",
    "custom_domain": f"{env.str('BUCKET_NAME')}.s3.gra.io.cloud.ovh.net",
}
STORAGES = {
    "default": {
        "BACKEND": default_storage_backend,
        "OPTIONS": default_storage_options,
    },
    "public_media": {
        "BACKEND": default_storage_backend,
        "OPTIONS": {
            # add or override options defined in default
            **default_storage_options,
            "location": "media",
            "default_acl": "public-read",
            "file_overwrite": True,
            "querystring_auth": False,
        },
    },
    "private_media": {
        "BACKEND": default_storage_backend,
        "OPTIONS": {
            **default_storage_options,
            "location": "private_media",
        } 
    },
    "staticfiles": {
        "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
    },
}

```

---

<div class="post-metadata">

**Author:** ![ashwanthbalakrishnan](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/ashwanthbalakrishnan/32/21069_2.png) [@ashwanthbalakrishnan](https://forum.djangoproject.com/u/ashwanthbalakrishnan)\
**Post date:** [May 5, 2024, 7:43am UTC](https://forum.djangoproject.com/t/storage-4-2-how-to-subclass-default/29190/5 "2024-05-05T07:43:13Z")

</div>

> <https://github.com/jschneier/django-storages/issues/1393#issue-2279035816>
>
> settings.py :
> \`\`\`
> STORAGES = {
> "default": {
> "BACKEND": "storages….backends.s3.S3Storage",
> "OPTIONS": {
> "access\_key": env("AWS\_ACCESS\_KEY\_ID"),
> "secret\_key": env("AWS\_SECRET\_ACCESS\_KEY"),
> "bucket\_name": env("AWS\_STORAGE\_BUCKET\_NAME"),
> "region\_name": "ap-south-1",
> "file\_overwrite": False,
> },
> },
> "PublicMediaStorage": {
> "BACKEND": "storages.backends.s3.S3Storage",
> "OPTIONS": {
> "access\_key": env("AWS\_ACCESS\_KEY\_ID"),
> "secret\_key": env("AWS\_SECRET\_ACCESS\_KEY"),
> "bucket\_name": env("AWS\_STORAGE\_BUCKET\_NAME"),
> "region\_name": "ap-south-1",
> "default\_acl": "public-read",
> "location": "media/public",
> "querystring\_auth": False,
> "file\_overwrite": False,
> },
> },
> "staticfiles": {
> "BACKEND": "storages.backends.s3.S3Storage",
> "OPTIONS": {
> "access\_key": env("AWS\_ACCESS\_KEY\_ID"),
> "secret\_key": env("AWS\_SECRET\_ACCESS\_KEY"),
> "bucket\_name": env("AWS\_STORAGE\_STATIC\_BUCKET\_NAME"),
> "region\_name": "ap-south-1",
> "default\_acl": "public-read",
> "location": "staticfiles",
> "querystring\_auth": False,
> },
> },
> }
> \`\`\`
> Models.py
> \`\`\`
> from django.db import models
> from django.core.files.storage import storages
> 
> class DemoModel(models.Model):
> file = models.FileField(
> upload\_to="demo/",
> storage=storages\["PublicMediaStorage"\],
> )
> \`\`\`
> Migration file:
> !\[image\](https://github.com/jschneier/django-storages/assets/91041406/1f38514f-b357-4455-bfb9-da3d2ea148d5)
> 
> 
> Is this the correct way of passing the storage to FileField ?

Can anyone check if my configuration in this issues is correct  
Or am I doing anything wrong ?

---

<div class="post-metadata">

**Author:** ![Swannbm](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/swannbm/32/19983_2.png) [@Swannbm](https://forum.djangoproject.com/u/Swannbm)\
**Post date:** [May 5, 2024, 9:44am UTC](https://forum.djangoproject.com/t/storage-4-2-how-to-subclass-default/29190/6 "2024-05-05T09:44:35Z")

</div>

Yes, it is.

But migrations seems to have trouble generating correctly. You should edit file manually.
