Search code examples
pythondjangodjango-migrations

Django migrations and FileSystemStorage depending on settings


In my Django app I use a FileSystemStorage for generated files. I initialize it like this:

import os
from urlparse import urljoin

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

gen_files_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'generated/'), base_url=urljoin(settings.MEDIA_URL, 'generated/'))

When I want to create a new file I use:

from django.core.files.base import ContentFile
from django.db import models

def next_number():
    # num = ...
    return num

gen_file = models.FileField(storage=gen_files_storage)
gen_file.save('file%s.txt' % next_number(), ContentFile(''))

That works fine. The only problem is that the FileSystemStorage's path is "hardcoded" in the Django migration. Because I use different settings for development (which changes) and production, often the manage.py makemigrations command generates a migration only because the path changed, although everything stays the same in the database.

I know there is a solution using a subclass of FileSystemStorage (see my answer below), but is there a better solution?


Solution

  • 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?