Миграции Django и FileSystemStorage в зависимости от настроек

В моем приложении Django я использую FileSystemStorage для сгенерированных файлов. Я инициализирую его следующим образом:

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/'))

Когда я хочу создать новый файл, я использую:

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(''))

Это прекрасно работает. Единственная проблема заключается в том, что путь FileSystemStorage "жестко закодирован" в миграции Django. Поскольку я использую разные настройки для разработки (изменения) и производства, часто команда manage.py makemigrations генерирует перенос только потому, что путь изменился, хотя все остается в базе данных одинаковым.

Я знаю, что есть решение, использующее подкласс FileSystemStorage (см. мой ответ ниже), но есть ли лучшее решение?

Ответ 1

Существует решение с пользовательским подклассом @deconstructible 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

Затем я могу инициализировать хранилище следующим образом:

import os
from urlparse import urljoin

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

gen_files_storage = MyFileSystemStorage('generated/')

Таким образом, миграции Django не будут замечать изменения в моих настройках. Есть ли лучший способ?

Ответ 2

Решение состоит в том, чтобы никогда не запускать makemigrations на производстве. Запустите migrate всего, что вы хотите на рабочих серверах, но игнорируйте предупреждения о запуске makemigrations если они относятся к этой проблеме.

Подумайте об этом: makemigrations генерирует код Python, поэтому его запуск на производстве будет таким же, как разработка на этом сервере. В зависимости от настройки вашего сервера ваш производственный сайт, скорее всего, будет обслуживать эти файлы, независимо от предупреждения о makemigrations.

Ответ 3

Моя проблема была связана, но немного другая. Класс хранения, используемый полем, может изменяться в зависимости от настроек: локально локально, удаленное хранилище по умолчанию. Я реализовал подкласс FileField который игнорирует хранилище kwarg при деконструировании поля для генерации миграции.

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

Его можно использовать следующим образом:

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

    file = VariableStorageFileField(blank=True, null=True, storage=storage)