Миграции 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)