django-storagesとAmazon S3を使用してDjangoプロジェクトをセットアップする方法はありますが、静的ファイルとメディアファイル用に異なるフォルダーを使用していますか?


92

アプリの静的ファイル(STATIC_ROOT)とユーザーがアップロードしたファイル(MEDIA_ROOT)を保存するためにサーバーファイルシステムを使用していたDjangoプロジェクトを構成しています。

これで、AmazonのS3ですべてのコンテンツをホストする必要があるため、このためのバケットを作成しました。ストレージバックエンドを使用django-storagesして、boto収集した統計をS3バケットにアップロードできました。

MEDIA_ROOT = '/media/'
STATIC_ROOT = '/static/'

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
AWS_ACCESS_KEY_ID = 'KEY_ID...'
AWS_SECRET_ACCESS_KEY = 'ACCESS_KEY...'
AWS_STORAGE_BUCKET_NAME = 'bucket-name'
STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'

次に、問題が発生しました:MEDIA_ROOTSTATIC_ROOTはバケット内で使用されていないため、バケットのルートには静的ファイルとユーザーがアップロードしたパスの両方が含まれています。

だから私は設定できます:

S3_URL = 'http://s3.amazonaws.com/%s' % AWS_STORAGE_BUCKET_NAME
STATIC_URL = S3_URL + STATIC_ROOT
MEDIA_URL = 'S3_URL + MEDIA_ROOT

テンプレートでこれらの設定を使用しますが、を使用してS3に保存する場合、静的/メディアファイルの区別はありませんdjango-storages

これはどのように行うことができますか?

ありがとう!


8
バケット名(AWS_STORAGE_BUCKET_NAME)を指定する設定は1つしかなく、それSTATICFILES_STORAGEはで指定されたクラスのインスタンスがインスタンス化されるときに使用される設定だからです。
アルマンドペレスマルケス2012年

回答:


126

以下はうまくいくはずで、Mandxの方法よりも単純ですが、非常に似ています。

s3utils.pyファイルを作成します。

from storages.backends.s3boto import S3BotoStorage

StaticRootS3BotoStorage = lambda: S3BotoStorage(location='static')
MediaRootS3BotoStorage  = lambda: S3BotoStorage(location='media')

次にあなたのsettings.py

DEFAULT_FILE_STORAGE = 'myproject.s3utils.MediaRootS3BotoStorage'
STATICFILES_STORAGE = 'myproject.s3utils.StaticRootS3BotoStorage'

異なるが関連する例(実際にテストしました)は、こちらの2つのexample_ファイルにあります


1
私のバージョンよりも明らかにシンプルで優れています。私はこれをテストしていませんが、これもうまくいくと思います。ありがとう!私はあなたのdjango-s3storageリポジトリもチェックしています。プロジェクトがS3のみを使用している場合、非常に軽量なソリューションのようです。
アルマンドペレスマルケス

1
そして、もっとパッケージングが好きなら、django-s3-folder-storageをチェックしてください。私はそれを見つけたばかりですが、これがまったく同じソリューションであるかどうかはわかりませんが、パッケージ化されています。
アルマンドペレスマルケス2012

4
これは私からは機能しません。メディアファイルはs3バケットの/にアップロードされます。場所の設定が尊重されていないようです。django-storages == 1.1.6、django-extensions == 1.1.1、django = 1.4
Nathan Keller

3
それは私にとっては、別々のバケツを持っているより多くの意味を成していたと私は私の解決策はこのように見てしまったので、私の設定モジュールのコンフィグ外のようなものではないんgist.github.com/antonagestam/6075199
antonagestam

1
私が言えることから、この解決策は機能しません。これはアプローチである必要があります:gist.github.com/defrex/82680e858281d3d3e6e4
defrex

8

私は現在、このコードを別のs3utilsモジュールで使用しています。

from django.core.exceptions import SuspiciousOperation
from django.utils.encoding import force_unicode

from storages.backends.s3boto import S3BotoStorage


def safe_join(base, *paths):
    """
    A version of django.utils._os.safe_join for S3 paths.

    Joins one or more path components to the base path component intelligently.
    Returns a normalized version of the final path.

    The final path must be located inside of the base path component (otherwise
    a ValueError is raised).

    Paths outside the base path indicate a possible security sensitive operation.
    """
    from urlparse import urljoin
    base_path = force_unicode(base)
    paths = map(lambda p: force_unicode(p), paths)
    final_path = urljoin(base_path + ("/" if not base_path.endswith("/") else ""), *paths)
    # Ensure final_path starts with base_path and that the next character after
    # the final path is '/' (or nothing, in which case final_path must be
    # equal to base_path).
    base_path_len = len(base_path) - 1
    if not final_path.startswith(base_path) \
       or final_path[base_path_len:base_path_len + 1] not in ('', '/'):
        raise ValueError('the joined path is located outside of the base path'
                         ' component')
    return final_path


class StaticRootS3BotoStorage(S3BotoStorage):
    def __init__(self, *args, **kwargs):
        super(StaticRootS3BotoStorage, self).__init__(*args, **kwargs)
        self.location = kwargs.get('location', '')
        self.location = 'static/' + self.location.lstrip('/')

    def _normalize_name(self, name):
        try:
            return safe_join(self.location, name).lstrip('/')
        except ValueError:
            raise SuspiciousOperation("Attempted access to '%s' denied." % name)


class MediaRootS3BotoStorage(S3BotoStorage):
    def __init__(self, *args, **kwargs):
        super(MediaRootS3BotoStorage, self).__init__(*args, **kwargs)
        self.location = kwargs.get('location', '')
        self.location = 'media/' + self.location.lstrip('/')

    def _normalize_name(self, name):
        try:
            return safe_join(self.location, name).lstrip('/')
        except ValueError:
            raise SuspiciousOperation("Attempted access to '%s' denied." % name)

次に、私の設定モジュールで:

DEFAULT_FILE_STORAGE = 'myproyect.s3utils.MediaRootS3BotoStorage'
STATICFILES_STORAGE = 'myproyect.s3utils.StaticRootS3BotoStorage'

元のコードでは正当なパスの例外が発生しているため_normalize_name()safe_join()関数の「修正」バージョンを使用するようにプライベートメソッドを再定義する必要がSuspiciousOperationありました。

私はこれを検討のために投稿しています。もし誰かがより良い答えを与えることができるか、これを改善できるなら、それは大歓迎です。


7

ファイル:PROJECT_NAME / custom_storages.py

from django.conf import settings
from storages.backends.s3boto import S3BotoStorage

class StaticStorage(S3BotoStorage):
    location = settings.STATICFILES_LOCATION

class MediaStorage(S3BotoStorage):
    location = settings.MEDIAFILES_LOCATION

ファイル:PROJECT_NAME / settings.py

STATICFILES_LOCATION = 'static'
MEDIAFILES_LOCATION = 'media'

if not DEBUG:
    STATICFILES_STORAGE = 'PROJECT_NAME.custom_storages.StaticStorage'
    DEFAULT_FILE_STORAGE = 'PROJECT_NAME.custom_storages.MediaStorage'
    AWS_ACCESS_KEY_ID = 'KEY_XXXXXXX'
    AWS_SECRET_ACCESS_KEY = 'SECRET_XXXXXXXXX'
    AWS_STORAGE_BUCKET_NAME = 'BUCKET_NAME'
    AWS_HEADERS = {'Cache-Control': 'max-age=86400',}
    AWS_QUERYSTRING_AUTH = False

そして実行します: python manage.py collectstatic


あなたがこのファイルのstorages.py代わりにこのファイルに名前を付けcustom_storages.pyた場合from __future__ import absolute_import
Aaron McMillin

2

答えはかなり単純で、デフォルトで行われると思います。これは、Django 1.6.5とBoto 2.28.0を使用したAWS Elastic Beanstalkで私のために働いています:

STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)

TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_KEY']

AWSキーはコンテナー構成ファイルから渡され、私は何も持っていないSTATIC_ROOTSTATIC_URL、まったく設定していません。また、s3utils.pyファイルは必要ありません。これらの詳細は、ストレージシステムによって自動的に処理されます。ここでの秘訣は、テンプレートでこの不明なパスを正しく動的に参照する必要があることです。例えば:

<link rel="icon" href="{% static "img/favicon.ico" %}">

これは、でローカルに配置される(展開前)ファビコンに対処する方法~/Projects/my_app/project/my_app/static/img/favicon.icoです。

もちろんlocal_settings.py、開発環境でローカルにこのものにアクセスするための個別のファイルがあり、静的およびメディア設定があります。私はこの解決策を見つけるために多くの実験と読書をしなければならず、それはエラーなしで一貫して機能します。

静的とルートの分離が必要で、バケットは1つしか提供できないことを考慮して、この方法ではローカル環境のすべてのフォルダを~/Projects/my_app/project/my_app/static/取得し、バケットルートにフォルダを作成します(例:S3bucket / img /)。上記の例のように)。したがって、ファイルを分離することができます。たとえば、mediaフォルダ内にstaticフォルダを作成し、テンプレートを使用してそれにアクセスできます。

{% static "media/" %}

これがお役に立てば幸いです。私は答えを探してここに来て、ストレージシステムを拡張するよりも簡単な解決策を見つけることを少し難しくしました。代わりに、Botoの使用目的に関するドキュメントを読んだところ、必要なものの多くがデフォルトで組み込まれていることがわかりました。乾杯!


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.