Django迁移和FileSystemStorage取决于设置

时间:2015-09-02 09:36:32

标签: python django django-migrations

在我的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子类的解决方案(请参阅下面的答案),但有更好的解决方案吗?

4 个答案:

答案 0 :(得分:7)

有一个涉及@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迁移不会发现我的设置发生变化。有更好的方法吗?

答案 1 :(得分:3)

升级到 Django 3.1+ 解决了这个问题:https://docs.djangoproject.com/en/3.2/releases/3.1/#file-storage

只需将一个可调用对象传递到存储参数中即可。

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)

答案 2 :(得分:0)

解决方案是永远不要在生产环境中运行makemigrations。在生产服务器上全部运行migrate,但如果与此问题相关,则忽略有关运行makemigrations的警告。

考虑一下:makemigrations生成Python代码,因此在生产环境中运行它与在该服务器上进行开发相同。根据服务器的设置,您的生产站点可能会正确地投放这些文件,而不考虑makemigrations警告。

答案 3 :(得分:0)

我的问题与之相关,但略有不同。该字段使用的存储类别可以根据以下设置进行更改:默认本地,生产中的远程存储。我实现了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)