Konfiguruję projekt Django, który korzystał z systemu plików serwera do przechowywania plików statycznych aplikacji ( STATIC_ROOT
) i plików przesłanych przez użytkownika ( MEDIA_ROOT
).
Muszę teraz hostować całą tę zawartość na Amazon S3, więc stworzyłem do tego wiadro. Korzystając django-storages
z boto
zaplecza pamięci, udało mi się przesłać zebrane statystyki do zasobnika 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'
Następnie pojawił się problem: MEDIA_ROOT
i STATIC_ROOT
nie są używane w zasobniku, więc katalog główny zasobnika zawiera zarówno pliki statyczne, jak i ścieżki przesłane przez użytkownika.
Mogłem więc ustawić:
S3_URL = 'http://s3.amazonaws.com/%s' % AWS_STORAGE_BUCKET_NAME
STATIC_URL = S3_URL + STATIC_ROOT
MEDIA_URL = 'S3_URL + MEDIA_ROOT
I użyj tych ustawień w szablonach, ale nie ma rozróżnienia między plikami statycznymi / multimedialnymi podczas przechowywania w S3 z django-storages
.
Jak to zrobić?
Dzięki!
źródło
AWS_STORAGE_BUCKET_NAME
) i jest ono używane, gdy tworzona jest instancja klasy określonej wSTATICFILES_STORAGE
.Odpowiedzi:
Myślę, że poniższe powinno działać i być prostsze niż metoda Mandxa, chociaż jest bardzo podobna:
Utwórz
s3utils.py
plik:from storages.backends.s3boto import S3BotoStorage StaticRootS3BotoStorage = lambda: S3BotoStorage(location='static') MediaRootS3BotoStorage = lambda: S3BotoStorage(location='media')
Następnie w
settings.py
:DEFAULT_FILE_STORAGE = 'myproject.s3utils.MediaRootS3BotoStorage' STATICFILES_STORAGE = 'myproject.s3utils.StaticRootS3BotoStorage'
Inny, ale pokrewny przykład (który faktycznie przetestowałem) można zobaczyć w dwóch
example_
plikach tutaj .źródło
Obecnie używam tego kodu w oddzielnym
s3utils
module: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)
Następnie w moim module ustawień:
DEFAULT_FILE_STORAGE = 'myproyect.s3utils.MediaRootS3BotoStorage' STATICFILES_STORAGE = 'myproyect.s3utils.StaticRootS3BotoStorage'
Musiałem przedefiniować
_normalize_name()
metodę prywatną, aby używać „ustalonej” wersjisafe_join()
funkcji, ponieważ oryginalny kod daje miSuspiciousOperation
wyjątki dla legalnych ścieżek.Przesyłam to do rozważenia, jeśli ktoś może udzielić lepszej odpowiedzi lub poprawić tę, będzie to bardzo mile widziane.
źródło
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
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
I biegnij:
python manage.py collectstatic
źródło
storages.py
zamiastcustom_storages.py
Będziesz chciał użyćfrom __future__ import absolute_import
Myślę, że odpowiedź jest dość prosta i domyślna. To działa dla mnie na AWS Elastic Beanstalk z Django 1.6.5 i Boto 2.28.0:
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']
Klucze AWS są przekazywane z pliku konfiguracyjnego kontenera i nie mam ich wcale
STATIC_ROOT
lub nie mam ich wcaleSTATIC_URL
. Nie ma też potrzeby posiadanias3utils.py
pliku. Te szczegóły są obsługiwane automatycznie przez system magazynowania. Sztuczka polega na tym, że musiałem poprawnie i dynamicznie odnosić się do tej nieznanej ścieżki w moich szablonach. Na przykład:<link rel="icon" href="{% static "img/favicon.ico" %}">
W ten sposób zwracam się do mojej ulubionej ikony, która znajduje się lokalnie (przed wdrożeniem) w
~/Projects/my_app/project/my_app/static/img/favicon.ico
.Oczywiście mam osobny
local_settings.py
plik umożliwiający dostęp do tych rzeczy lokalnie w środowisku deweloperskim i ma on ustawienia STATIC i MEDIA. Musiałem dużo eksperymentować i czytać, aby znaleźć to rozwiązanie i działa konsekwentnie bez błędów.Rozumiem, że potrzebujesz separacji statycznej i głównej, a biorąc pod uwagę, że możesz zapewnić tylko jeden zasobnik, zwróciłbym uwagę, że ta metoda bierze wszystkie foldery w moim środowisku lokalnym
~/Projects/my_app/project/my_app/static/
i tworzy folder w katalogu głównym zasobnika (tj .: S3bucket / img / jak w powyższym przykładzie). Więc otrzymujesz oddzielenie plików. Na przykład możesz miećmedia
folder wstatic
folderze i uzyskać do niego dostęp za pomocą szablonu z tym:{% static "media/" %}
Mam nadzieję, że to pomoże. Przyszedłem tutaj, szukając odpowiedzi, i naciskałem nieco bardziej na znalezienie prostszego rozwiązania niż rozszerzenie systemu pamięci masowej. Zamiast tego przeczytałem dokumentację dotyczącą zamierzonego użycia Boto i odkryłem, że wiele z tego, czego potrzebowałem, było domyślnie wbudowanych. Twoje zdrowie!
źródło
Jeśli chcesz mieć podfoldery nawet przed rozdzieleniami multimedialnymi lub statycznymi, możesz użyć AWS_LOCATION nad odpowiedzią bradenm. Źródła: https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#usage
AWS_STORAGE_BUCKET_NAME = 'bucket_name' AWS_LOCATION = 'path1/path2/'
źródło