django-storages позволяет Django хранить загружаемые файлы (FileField, ImageField) и статику в объектном хранилище. Примеры рассчитаны на Django 4.2 и новее.
Установка
pip install "django-storages[s3]>=1.14" "boto3>=1.36"
Настройка
Передайте ключи через переменные окружения (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) и добавьте настройки хранилища в settings.py. В этом примере пользовательские файлы закрыты, а статика лежит в папке static/ с публичным доступом:
from botocore.config import Config
S3_OPTIONS = {
"bucket_name": "my-bucket",
"endpoint_url": "https://s3.uzcstorage.uz",
"region_name": "default",
"client_config": Config(
signature_version="s3v4",
s3={"addressing_style": "path"},
request_checksum_calculation="when_required",
response_checksum_validation="when_required",
),
}
STORAGES = {
# Uploaded files: private, accessed by temporary links
"default": {
"BACKEND": "storages.backends.s3.S3Storage",
"OPTIONS": {
**S3_OPTIONS,
"location": "media",
"default_acl": None,
"querystring_auth": True,
"querystring_expire": 3600,
"file_overwrite": False,
},
},
# Static files: public, permanent links
"staticfiles": {
"BACKEND": "storages.backends.s3.S3Storage",
"OPTIONS": {
**S3_OPTIONS,
"location": "static",
"default_acl": None,
"querystring_auth": False,
"custom_domain": "s3.uzcstorage.uz/my-bucket",
},
},
}
Откройте доступ на чтение к папке static/ политикой бакета (в примере замените public/* на static/*) и загрузите статику:
python manage.py collectstatic --noinput
Модели и файлы
Поля моделей работают как обычно — файлы сохраняются в бакет:
from django.db import models
class Document(models.Model):
title = models.CharField(max_length=200)
file = models.FileField(upload_to="documents/%Y/%m/")
# In a view:
doc = Document.objects.create(title="Report", file=request.FILES["file"])
doc.file.url # temporary link, valid for querystring_expire seconds
Работа с хранилищем напрямую:
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
path = default_storage.save("exports/report.csv", ContentFile(b"id,name\n1,Demo\n"))
default_storage.exists(path)
with default_storage.open(path) as f:
data = f.read()
default_storage.delete(path)
Несколько хранилищ
Чтобы хранить часть файлов в отдельном бакете (например, публичные изображения), создайте класс хранилища и передайте его полю:
from storages.backends.s3 import S3Storage
class PublicMediaStorage(S3Storage):
bucket_name = "my-public-bucket"
querystring_auth = False
custom_domain = "s3.uzcstorage.uz/my-public-bucket"
class Product(models.Model):
image = models.ImageField(upload_to="products/", storage=PublicMediaStorage())
Параметры подключения (endpoint_url, region_name, client_config) для такого класса берутся из переменных AWS_S3_ENDPOINT_URL, AWS_S3_REGION_NAME и AWS_S3_CLIENT_CONFIG в settings.py.
Совет
Для прямой работы с S3 из кода на Python (мультипарт-загрузка, ссылки на загрузку) см. Python (boto3).