Як об’єднати Amazon S3 із Django

7 хв. читання

Це стаття-посібник, що допоможе вам навчитись використовувати сервіс Amazon S3 для обробки статичних ресурсів та файлів, завантажених користувачем, тобто медіа-ресурсів.

Спершу я розповім про базові поняття, встановлення та налаштування. Потім ви побачите три розділи, що розкривають:

Залежності (dependencies)

Вам знадобляться дві бібліотеки Python:

  • boto3
  • django-storages

Бібліотека boto3 – клієнт публічного API для доступу до послуг Amazon Web Services (AWS), наприклад Amazon S3. Це офіційний дистрибутив, що підтримується Amazon.

Django-storages – бібліотека з відкритим кодом для керування сховищами на бекенді, такими як Dropbox, OneDrive та Amazon S3. Вона підключає вбудоване API самого Django для роботи зі сховищами. Інакше кажучи, ця бібліотека спростить вам життя, бо не змінюватиме спосіб вашої взаємодії зі статикою та медіа ресурсами. Нам лише треба додати кілька параметрів у конфігурацію і вона зробить за нас усю важку роботу.

Встановлення Amazon S3

Перш ніж ми перейдемо до Django, розберемося з частиною S3. Ми маємо створити користувача із доступом до наших ресурсів S3. Зайшовши до AWS, знайдемо у списку послуг IAM. Воно має бути під Security, Identity & Compliance:

Як об’єднати Amazon S3 із Django

Перейдіть на вкладку Users та натисніть на кнопку Add user, щоб додати користувача:

Як об’єднати Amazon S3 із Django

Дайте користувачу ім'я (user name) та виберіть опцію програмного доступу (Access type: programmatic access).

Як об’єднати Amazon S3 із Django

Натисніть next, щоб перейти до дозволів. На цій стадії нам треба створити нову групу з правильними правами доступу до S3, і додати до неї нашого нового користувача. Натисніть на кнопку Create group, щоб створити групу:

Як об’єднати Amazon S3 із Django

Визначтесь з ім'ям для групи та знайдіть одну з «політик» (колонка Policy name), що надає повний доступ. Вона називається AmazonS3FullAccess:

Як об’єднати Amazon S3 із Django

Натисніть "Create group" щоб завершити процес створення групи. На наступній сторінці з'явиться щойно створена група. Вона одразу матиме галочку, залиште її такою та натисніть Next: Review внизу:

Як об’єднати Amazon S3 із Django

Перевірте інформацію та, якщо все вірно, рухайтесь далі, щоб створити нового користувача. Ви маєте побачити наступну інформацію:

Як об’єднати Amazon S3 із Django

Запишіть поля User, Access key ID та Secret access key. Вони знадобляться вам у майбутньому. Натисніть на кнопку Close і давайте йти далі. Тепер час створити наш перший кошик!

Кошиком ми називаємо контейнер сховищ у S3. Ми можемо працювати з кількома кошиками в рамках одного проекту Django. Однак для більшості випадків Вам знадобиться лише один кошик для одного сайту.

Відкрийте меню Services та пошукайте там S3. Воно знаходиться під пунктом Storage. Якщо ви бачите щось таке як на цьому скріншоті – значить, ви там де треба:

Як об’єднати Amazon S3 із Django

Натисніть на + Create bucket, щоб почати процес. Встановіть для вашого кошика DNS-сумісне ім'я. Воно використовуватиметься щоб ідентифікувати ваші ресурси. У моєму випадку це буде sibtc-static, тож шлях до моїх ресурсів виглядатиме якось так: https://sibtc-static.s3.amazonaws.com/static/.

Як об’єднати Amazon S3 із Django

Нехай усі інші налаштування залишаться як є, переходьте до наступних кроків зі значеннями за замовчанням, і нарешті натисніть кнопку Create bucket. Ви маєте побачити щось таке, як на скріншоті внизу:

Як об’єднати Amazon S3 із Django

Залишимо це як воно є і почнемо працювати над нашою Django-частиною.

Встановлення бібліотек

Найпростіший спосіб - встановити їх через pip:

pip install boto3
pip install django-storages

Додамо storages до ваших встановлених застосунків – INSTALLED_APPS всередині файлу settings.py:

settings.py

INSTALLED_APPS = [
'django.contrib.auth', '
django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',

'storages',
]

Робота виключно зі статикою

Це найпростіший випадок. Усе працює «з коробки» із мінімальними налаштуваннями. Отак виглядають усі налаштування всередині модуля settings.py:

settings.py

AWS_ACCESS_KEY_ID = 'AKIAIT2Z5TDYPX3ARJBA'
AWS_SECRET_ACCESS_KEY = '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'
AWS_STORAGE_BUCKET_NAME = 'sibtc-static'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
  'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
STATICFILES_DIRS = [
  os.path.join(BASE_DIR, 'mysite/static'),
]
STATIC_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

Зверніть увагу, що у нас тут є певна чутлива інформація, наприклад AWS_ACCESS_KEY_ID та AWS_SECRET_ACCESS_KEY. Ви не повинні вставляти це просто у ваш файл settings.py або відправляти до публічного репозиторія. Замість цього використовуйте змінні середовища або пітонівську бібліотеку Python Decouple. Керівництво з Python Decouple (англ.) можна знайти тут.

Для зрозумілості я створив мінімальний проект Django:

mysite/
 |-- mysite/
 |    |-- static/
 |    |    |-- css/
 |    |    |    +-- app.css
 |    |    +-- img/
 |    |         +-- thumbs-up.png
 |    |-- templates/
 |    |    +-- home.html
 |    |-- __init__.py
 |    |-- settings.py
 |    |-- urls.py
 |    +-- wsgi.py
 +-- manage.py

Як бачите, обробка статичних файлів має проходити без проблем:

home.html

{% load static %}<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>S3 Example Static Only</title>
  <link rel="stylesheet" type="text/css" href="{% static 'css/app.css' %}">
</head>
<body>
  <header>
    <h1>S3 Example Static Only</h1>
  </header>
  <main>
    <img src="{% static 'img/thumbs-up.png' %}">
    <h2>It's working!</h2>
  </main>
  <footer>
    <a href="https://simpleisbetterthancomplex.com">www.SimpleIsBetterThanComplex.com</a>
  </footer>
</body>
</html>

Навіть не дивлячись на використання нами локальної машини, ми мусимо запустити команду collectstatic, бо наш код посилатиметься на віддалене розташування:

python manage.py collectstatic

Як об’єднати Amazon S3 із Django

Ви помітите, що процес копіювання займе більше часу ніж зазвичай. Це нормально. Я видалив зі списку INSTALLED_APPS застосунок Django Admin, тож це «чистий» приклад. Та якщо ви спробуєте робити це локально – побачите безліч файлів, що копіюються до вашого кошика S3.

Якщо тепер ми перевіримо сайт AWS, то побачимо там наші статичні ресурси:

Як об’єднати Amazon S3 із Django

І, нарешті, результат:

Як об’єднати Amazon S3 із Django

Як ви можете бачити, сховище на бекенді бере на себе переклад тегу шаблона {% static 'img/thumbs-up.png' %} у https://sitbtc-static.s3.amazonaws.com/static/img/thumbs-up.png і віддає картинку з кошика S3.

У наступному прикладі ви навчитесь працювати зі статичними та медіаресурсами.

Робота зі статикою та медіа

Для цього прикладу я створив новий кошик на ім'я sibtc-assets.

Як об’єднати Amazon S3 із Django

Налаштування у settings.py будуть дуже простими. От тільки ми розширимо storages.backends.s3boto3.S3Boto3Storage, додавши кілька власних параметрів, щоб мати змогу зберігати файли користувачів (медіаресурси) в іншому місці, а також щоб сказати нашому S3 не перезаписувати файли з однаковими іменами.

Що мені зазвичай подобається робити, то це створювати файл storage_backends.py у тій же директорії що й мій settings.py, і тоді ви можете визначити новий Storage Backend отаким чином:

storage_backends.py

from storages.backends.s3boto3 import S3Boto3Storage

class MediaStorage(S3Boto3Storage):
    location = 'media'
    file_overwrite = False

Тепер у settings.py ми мусимо додати цей новий бекенд до опції DEFAULT_FILE_STORAGE:

settings.py

STATICFILES_DIRS = [
   os.path.join(BASE_DIR, 'mysite/static'),
]

AWS_ACCESS_KEY_ID = 'AKIAIT2Z5TDYPX3ARJBA'
AWS_SECRET_ACCESS_KEY = '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'
AWS_STORAGE_BUCKET_NAME = 'sibtc-assets'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME

AWS_S3_OBJECT_PARAMETERS = {
   'CacheControl': 'max-age=86400',
}

AWS_LOCATION = 'static'
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)

DEFAULT_FILE_STORAGE = 'mysite.storage_backends.MediaStorage'  # <-- тут ми на нього посилаємось

Щоб продемонструвати завантаження файлу, я створив джанго-застосунок із назвою core та визначив для нього наступну модель:

models.py

from django.db import models

class Document(models.Model):
    uploaded_at = models.DateTimeField(auto_now_add=True)
    upload = models.FileField()

А тепер погляньте, як виглядатиме мій view:

views.py

from django.contrib.auth.decorators import login_required
from django.views.generic.edit import CreateView
from django.urls import reverse_lazy

from .models import Document


class DocumentCreateView(CreateView):
    model = Document
    fields = ['upload', ]
    success_url = reverse_lazy('home')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        documents = Document.objects.all()
        context['documents'] = documents
        return context

Ось шаблон document_form.html:

<form method="post" enctype="multipart/form-data">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Submit</button>
</form>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Uploaded at</th>
      <th>Size</th>
    </tr>
  </thead>
  <tbody>
    {% for document in documents %}
      <tr>
        <td><a href="{{ document.upload.url }}" target="_blank">{{ document.upload.name }}</a></td>
        <td>{{ document.uploaded_at }}</td>
        <td>{{ document.upload.size|filesizeformat }}</td>
      </tr>
    {% empty %}
      <tr>
        <td colspan="3">No data.</td>
      </tr>
    {% endfor %}
  </tbody>
</table>

Як бачите, я використовую в шаблоні лише вбудовані у Django можливості. Отак цей шаблон виглядає в браузері:

Як об’єднати Amazon S3 із Django

Я не збираюся закопуватись у деталі щодо завантаження файлів, ви можете прочитати про це детальніше окремо. Тепер протестуємо файли, завантажені користувачем:

Як об’єднати Amazon S3 із Django

Я створив свій шаблон, щоб виводити список завантажених файлів, тож коли юзер завантажить якесь зображення чи документ – вони з'являться у списку.

Тепер якщо ми клікнемо по посиланню, яке насправді звичайнісіньке {{ document.upload.url }} у Джанго, ми побачимо зображення з кошика на Amazon S3:

Як об’єднати Amazon S3 із Django

А якщо ми тепер зазирнемо до нашого кошика, то побачимо там директорії static та media:

Як об’єднати Amazon S3 із Django

Змішування публічних ресурсів із приватними

Використовуючи дуже схожі принципи, ви призначаєте деякі ресурси для приватного зберігання у кошику S3. Погляньте на конфігурацію внизу:

storage_backends.py

from django.conf import settings
from storages.backends.s3boto3 import S3Boto3Storage

class StaticStorage(S3Boto3Storage):
    location = settings.AWS_STATIC_LOCATION

class PublicMediaStorage(S3Boto3Storage):
    location = settings.AWS_PUBLIC_MEDIA_LOCATION
    file_overwrite = False

class PrivateMediaStorage(S3Boto3Storage):
    location = settings.AWS_PRIVATE_MEDIA_LOCATION
    default_acl = 'private'
    file_overwrite = False
    custom_domain = False

settings.py

AWS_ACCESS_KEY_ID = 'AKIAIT2Z5TDYPX3ARJBA'
AWS_SECRET_ACCESS_KEY = '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'
AWS_STORAGE_BUCKET_NAME = 'sibtc-assets'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME

AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}

AWS_STATIC_LOCATION = 'static'
STATICFILES_STORAGE = 'mysite.storage_backends.StaticStorage'
STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, AWS_STATIC_LOCATION)

AWS_PUBLIC_MEDIA_LOCATION = 'media/public'
DEFAULT_FILE_STORAGE = 'mysite.storage_backends.PublicMediaStorage'

AWS_PRIVATE_MEDIA_LOCATION = 'media/private'
PRIVATE_FILE_STORAGE = 'mysite.storage_backends.PrivateMediaStorage'

Тепер ми можемо визначити новий PrivateMediaStorage просто у визначенні моделі:

models.py

from django.db import models
from django.conf import settings
from django.contrib.auth.models import User

from mysite.storage_backends import PrivateMediaStorage

class Document(models.Model):
   uploaded_at = models.DateTimeField(auto_now_add=True)
   upload = models.FileField()

class PrivateDocument(models.Model):
   uploaded_at = models.DateTimeField(auto_now_add=True)
   upload = models.FileField(storage=PrivateMediaStorage())
   user = models.ForeignKey(User, related_name='documents')

Після завантаження приватного файлу, якщо ви спробуєте отримати URL контенту, API згенерує довгий URL із коротким терміном життя у кілька хвилин:

Як об’єднати Amazon S3 із Django

Якщо ви спробуєте пробитися до ресурсу напряму, без параметрів – отримаєте повідомлення про помилку від AWS:

Як об’єднати Amazon S3 із Django

Висновки

Сподіваюсь, цей посібник допоміг Вам зрозуміти певні базові принципи Amazon S3 та хоча б взятись до роботи. Нехай вас не лякає офіційна документація обох бібліотек, boto3 та django-storages:

Я також підготував три приклади (я їх використовував у цьому посібнику), тож ви можете досліджувати їх і взяти щось для себе з моєї імплементації: https://github.com/sibtc/simple-s3-setup.

У цьому репозиторії ви знайдете три проекти Django, кожен для свого випадку:

  • s3-example-public-and-private – публічні та приватні ресурси
  • s3-example-static-and-media – статика та медіа
  • s3-example-static-only – лише статика.

Не забувайте додати власні ключі та іншу інформацію, щоб все запрацювало! Я залишив відповідні поля пустими спеціально для цього.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.6K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація