Django 仅在生产环境中使用私有 S3 存储

fin*_*ngu 7 python django amazon-s3 django-storage django-rest-framework

我已将 django REST API 设置为在调试模式下使用本地存储,在生产环境中使用 S3 存储。这适用于公共文件,因为我覆盖了DEFAULT_FILE_STORAGE这样的:

if IS_DEBUG:
    DEFAULT_FILE_STORAGE = 'api.storage_backends.PublicMediaStorage'
Run Code Online (Sandbox Code Playgroud)

每个人都会FileField自动使用它。现在我想以同样的方式使用私有 S3 存储,但是因为我必须显式定义存储 ( FileField(storage=PrivateMediaStorage())),所以总是使用 S3 存储。

在调试模式下如何使用本地存储而不是 S3 存储?

PS:我已经考虑过FileField根据 DEBUG 模式将模型更改为使用或不使用显式存储。这并没有完全解决我的问题,因为我的迁移是在 DEBUG 模式下创建的,因此始终包含没有私有存储类的模型。

更新: 我正在寻找一种解决方案,它可以在两种环境中共享相同的迁移,并且仅在运行时延迟实例化实际存储类。就像 djangoDEFAULT_FILE_STORAGE已经处理了一样。

Tho*_*cki 7

听起来这里的棘手部分是在一个项目中同时拥有公共私人媒体存储。

下面的示例假设您使用的是django storages,但无论如何该技术都应该有效。

通过扩展S3BotoStorage类定义私有存储。

如果使用 S3,将私有公共公共存储在不同的 S3 存储桶中可能是谨慎的。此自定义存储允许您通过设置指定此参数。

# yourapp.custom_storage.py

from django.conf import settings
from django.core.files.storage import get_storage_class
from storages.backends.s3boto import S3BotoStorage

class S3PrivateStorage(S3BotoStorage):
    """
    Optional   
    """
    default_acl = "private"               # this does the trick

    def __init__(self):
        super(S3PrivateStorage, self).__init__()
        self.bucket_name = settings.S3_PRIVATE_STORAGE_BUCKET_NAME


# important
private_storage_class = get_storage_class(settings.PRIVATE_FILE_STORAGE)

private_storage = private_storage_class() # instantiate the storage
Run Code Online (Sandbox Code Playgroud)

重要的部分是该文件的最后 2 行 - 它声明private_storage用于您的FileField

# yourapp.custom_storage.py

from django.conf import settings
from django.core.files.storage import get_storage_class
from storages.backends.s3boto import S3BotoStorage

class S3PrivateStorage(S3BotoStorage):
    """
    Optional   
    """
    default_acl = "private"               # this does the trick

    def __init__(self):
        super(S3PrivateStorage, self).__init__()
        self.bucket_name = settings.S3_PRIVATE_STORAGE_BUCKET_NAME


# important
private_storage_class = get_storage_class(settings.PRIVATE_FILE_STORAGE)

private_storage = private_storage_class() # instantiate the storage
Run Code Online (Sandbox Code Playgroud)

最后,在你的设置文件中,应该做这样的事情。

# settings.py

if DEBUG:
    # In debug mode, store everything on the filestystem
    DEFAULT_FILE_STORAGE = 'django.files.storage.FileSystemStorage'
    PRIVATE_FILE_STORAGE = 'django.files.storage.FileSystemStorage'
else:
    # In production store public things using S3BotoStorage and private things
    # in a custom storage
    DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
    PRIVATE_FILE_STORAGE = 'yourapp.custom_storage.S3PrivateStorage'

Run Code Online (Sandbox Code Playgroud)

作为最后一条不请自来的建议:将存储设置与调试模式分离并允许在环境变量中指定上述所有参数通常很有用。在某些时候,您可能希望使用类似生产的存储配置在调试模式下运行您的应用程序。

  • 还有两件事:1.我必须将“django.files.storage.FileSystemStorage”替换为“django.core.files.storage.FileSystemStorage”。2. 显然,延迟实例化并不能解决迁移问题。当我创建迁移时,存储类是“FileSystemStorage”或“S3PrivateStorage”,但绝不是“private_storage”。因此我无法在远程系统上应用本地迁移。 (2认同)

JPG*_*JPG 5

最好的解决方案是在FileField没有显式storage类的情况下使用。

# settings.py

if DEBUG:
    DEFAULT_FILE_STORAGE = 'api.storage_backends.PublicMediaStorage'
else:
    DEFAULT_FILE_STORAGE = 'api.storage_backends.PrivateMediaStorage'


# models.py
class Foo(models.Model):
    file = models.FileField() # without storage
Run Code Online (Sandbox Code Playgroud)

在文件上传过程中,Django 会DEFAULT_FILE_STORAGE惰性方式调用该类。

笔记

这些设置不会创建带有storage参数的迁移文件


更新-1

如果您想更好地控制存储,请创建您自己的自定义文件字段并在模型中进行连接

def get_storage():
    """
    Change this function to whatever way as you need
    """
    from api.storage_backends import PublicMediaStorage, PrivateMediaStorage
    if DEBUG:
        return PublicMediaStorage()
    else:
        return PrivateMediaStorage()


class CustomFileField(models.FileField):
    def __init__(self, *args, **kwargs):
        kwargs['storage'] = get_storage() # calling external function
        super().__init__(*args, **kwargs)


class Foo(models.Model):
    file = CustomFileField() # use custom filefield here
Run Code Online (Sandbox Code Playgroud)

  • @finngu 检查***`UPDATE-1`*** 部分 (2认同)