[Django]-Django migrations and FileSystemStorage depending on settings

11πŸ‘

βœ…

There is a solution involving a custom @deconstructible subclass of FileSystemStorage:

import os
from urlparse import urljoin

from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.utils.deconstruct import deconstructible

@deconstructible
class MyFileSystemStorage(FileSystemStorage):
    def __init__(self, subdir):
        self.subdir = subdir
        super(MyFileSystemStorage, self).__init__(location=os.path.join(settings.MEDIA_ROOT, self.subdir), base_url=urljoin(settings.MEDIA_URL, self.subdir))

    def __eq__(self, other):
        return self.subdir == other.subdir

Then I can initialize the storage like this:

import os
from urlparse import urljoin

from django.conf import settings
from django.core.files.storage import FileSystemStorage

gen_files_storage = MyFileSystemStorage('generated/')

This way Django migrations won’t notice changes in my settings. Is there a better way though?

πŸ‘€geckon

8πŸ‘

Upgrading to Django 3.1+ fixes this: https://docs.djangoproject.com/en/3.2/releases/3.1/#file-storage

Just pass a callable into the storage argument.

from django.db import models
from django.conf import settings
from django.core.files.storage import get_storage_class


def _get_storage():
    storage_class = get_storage_class(settings.MY_STORAGE_CLASS)  # ie. 'django.core.files.storage.FileSystemStorage'
    return storage_class()

class MyModel(models.Model):
    myfile = models.FileField(max_length=255, blank=True, storage=_get_storage)
πŸ‘€swinters

2πŸ‘

The solution is to never run makemigrations on production. Run migrate all you want on production servers, but ignore warnings about running makemigrations if they pertain to this issue.

Think about it: makemigrations generates Python code, so running it on production would be the same as developing on that server. Depending on your server setup, your production site will likely serve those files correctly regardless of the makemigrations warning.

πŸ‘€Chris Conlan

2πŸ‘

My problem was related, but slightly different. The storage class used by the field can change based on settings: the default locally, remote storage in production. I implemented a subclass of FileField that ignores the storage kwarg when deconstructing the field for migration generation.

from django.db.models import FileField

class VariableStorageFileField(FileField):
    """
    Disregard the storage kwarg when creating migrations for this field
    """

    def deconstruct(self):
        name, path, args, kwargs = super(VariableStorageFileField, self).deconstruct()
        kwargs.pop('storage', None)
        return name, path, args, kwargs

It can be used like this:

class MyModel(models.Model):
    storage = get_storage_class(getattr(settings, 'LARGE_FILE_STORAGE', None))()

    file = VariableStorageFileField(blank=True, null=True, storage=storage)
πŸ‘€JSTL

Leave a comment