95 votes

Comment configurer un projet Django avec django-storages et Amazon S3, mais avec des dossiers différents pour les fichiers statiques et les fichiers multimédias?

Je suis à la configuration d'un projet Django qui ont été en utilisant le serveur de système de fichiers pour stocker les applications des fichiers statiques (STATIC_ROOT) et de l'utilisateur des fichiers téléchargés (MEDIA_ROOT).

J'ai besoin maintenant d'accueillir tout le contenu sur Amazon S3, j'ai donc créé un seau pour cela. À l'aide de django-storages avec l' boto backend de stockage, j'ai réussi à télécharger recueillies statique dans le compartiment 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'

Ensuite, j'ai eu un problème: l' MEDIA_ROOT et STATIC_ROOT ne sont pas utilisés dans le seau, de sorte que le seau de la racine contient à la fois les fichiers statiques et utilisateur de télécharger des chemins.

Alors je pourrais le définir:

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

Et d'utiliser ces paramètres dans les modèles, mais il n'y a pas de distinction de statique/fichiers multimédias lors du stockage dans S3 avec django-storages.

Comment cela peut être fait?

Merci!

127voto

bradenm Points 1208

Je pense que ce qui suit devrait fonctionner et être plus simple que la méthode de Mandx, même si elle est très similaire:

Créez un fichier s3utils.py :

 from storages.backends.s3boto import S3BotoStorage

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

Puis dans votre settings.py :

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

Un exemple différent mais lié (que j'ai réellement testé) peut être vu dans les deux fichiers example_ ici .

8voto

Mandx Points 1403

Je suis actuellement en utilisant ce code dans un séparé s3utils module de:

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)

Ensuite, dans mes paramètres de module:

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

Je suis arrivé à redéfinir l' _normalize_name() privé de la méthode à utiliser un "fixe" version de l' safe_join() fonction, depuis le code d'origine est de me donner SuspiciousOperation des exceptions pour les voies légales.

Je poste ceci pour examen, si quelqu'un peut donner une meilleure réponse ou d'améliorer celui-ci, il sera très bienvenue.

2voto

e.thompsy Points 91

Je pense que la réponse est assez simple et fait par défaut. Cela fonctionne pour moi sur AWS Elastic Beanstalk avec Django 1.6.5 et 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']

AWS clés sont passé à partir du conteneur de fichier de config et je n'ai pas d' STATIC_ROOT ou STATIC_URL ensemble du tout. Aussi, pas besoin de l' s3utils.py le fichier. Ces données sont traitées par le système de stockage automatique. Le truc c'est que j'avais besoin de faire référence à ce chemin inconnu dans mes modèles correctement et de façon dynamique. Par exemple:

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

C'est la façon dont je m'adresse à mon favicon qui vit sur place (avant le déploiement) en ~/Projects/my_app/project/my_app/static/img/favicon.ico.

Bien sûr, j'ai séparé local_settings.py le fichier pour accéder à ce genre de choses localement dans l'environnement de dev et il ne STATIQUE et paramètres des MÉDIAS. J'ai dû faire beaucoup d'expériences et de lecture pour trouver cette solution, et il fonctionne toujours sans erreurs.

Je comprends que vous avez besoin de la statique et de la racine de la séparation et en considérant que vous ne pouvez fournir un seau je tiens à souligner que cette méthode prend tous les dossiers dans mon environnement local en vertu de l' ~/Projects/my_app/project/my_app/static/et crée un dossier dans le seau de la racine (c'est à dire: S3bucket/img/ comme dans l'exemple ci-dessus). Si vous n'obtenez pas de séparation de fichiers. Par exemple, vous pourriez avoir un media le dossier dans l' static le dossier et d'y accéder via les templates avec ceci:

{% static "media/" %}

J'espère que cette aide. Je suis venu ici à la recherche de la réponse et a poussé un peu plus difficile de trouver une solution plus simple que d'étendre le système de stockage. Au lieu de cela, j'ai lu la documentation sur l'utilisation prévue de Boto et j'ai trouvé que beaucoup de ce que je devais était intégré par défaut. Cheers!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X