# Рефакторинг структуры хранения медиа-файлов **Дата**: 5 марта 2026 **Статус**: ✅ Завершено ## 🎯 Цели Переход от хеш-структуры django-filer к читаемой структуре с организацией по датам. ## 📁 Было (deprecated) ``` media/filer_public/ ├── 07/28/07286222-01c2-415c-ad2f-fc0ee3eb13f0/ ← Нечитаемая хеш-структура ├── 2a/cd/2acdaf8f-b428-42d6-aaf6-b9b63353e836/ └── ... ``` **Проблемы старой структуры:** - ❌ Невозможно найти файл вручную - ❌ Сложно организовать архивацию - ❌ Нет ясности по периодам хранения - ❌ Усложняет бэкапы по датам ## 📁 Стало (new) ``` media/ ├── documents/ │ ├── public/2026/03/05/ ← Публичные документы по дате │ ├── private/2026/03/05/ ← Приватные документы по дате │ ├── public_thumbnails/ ← Превью публичных документов │ └── private_thumbnails/ ← Превью приватных документов ├── avatars/ ← Аватарки пользователей ├── chat_attachments/2026/03/05/ ← Вложения мессенджера └── temp/ ← Временные файлы ``` **Преимущества новой структуры:** - ✅ Читаемые пути: `documents/public/2026/03/05/contract.pdf` - ✅ Простая архивация: перемещение старых папок (например, `2024/`) - ✅ Удобные бэкапы: легко выбрать период - ✅ Естественная организация: по дате загрузки ## 🔧 Изменения в коде ### 1. Django settings.py **Было:** ```python FILER_STORAGES = { 'public': { 'UPLOAD_TO': 'filer.utils.generate_filename.randomized', 'UPLOAD_TO_PREFIX': '%Y/%m', # Не работало правильно } } ``` **Стало:** ```python FILER_STORAGES = { 'public': { 'main': { 'ENGINE': 'filer.storage.PublicFileSystemStorage', 'OPTIONS': { 'location': os.path.join(MEDIA_ROOT, 'documents/public/'), 'base_url': '/media/documents/public/', }, 'UPLOAD_TO': 'filer.utils.generate_filename.by_date', # ← Ключевое изменение }, 'thumbnails': { 'ENGINE': 'filer.storage.PublicFileSystemStorage', 'OPTIONS': { 'location': os.path.join(MEDIA_ROOT, 'documents/public_thumbnails/'), 'base_url': '/media/documents/public_thumbnails/', }, }, }, 'private': { 'main': { 'ENGINE': 'filer.storage.PrivateFileSystemStorage', 'OPTIONS': { 'location': os.path.join(MEDIA_ROOT, 'documents/private/'), 'base_url': '/smedia/documents/private/', }, 'UPLOAD_TO': 'filer.utils.generate_filename.by_date', # ← Ключевое изменение }, 'thumbnails': { 'ENGINE': 'filer.storage.PrivateFileSystemStorage', 'OPTIONS': { 'location': os.path.join(MEDIA_ROOT, 'documents/private_thumbnails/'), 'base_url': '/smedia/documents/private_thumbnails/', }, }, }, } ``` **Ключевые изменения:** - `randomized` → `by_date` - функция генерации путей - Организация: `YYYY/MM/DD/filename.ext` - Раздельные пути для public/private ### 2. URL конфигурация (urls.py) **Было:** ```python ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ``` **Стало:** ```python if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ``` **Причина:** В production статика раздается через Nginx, не Django. ### 3. .gitignore **Добавлено:** ```gitignore backend/media/* !backend/media/.gitkeep !backend/media/*/ !backend/media/*/.gitkeep !backend/media/*/*/ !backend/media/*/*/.gitkeep ``` **Результат:** Git отслеживает структуру папок (через .gitkeep), но игнорирует загруженные файлы. ## 🧪 Тестирование ### Тест создания файла ```python from django.core.files.base import ContentFile from filer.models import File test_content = ContentFile(b"Test document content", name="test_document.txt") test_file = File.objects.create( original_filename="test_document.txt", file=test_content ) print(test_file.file.path) # Результат: /backend/media/documents/private/2026/03/05/test_document.txt print(test_file.file.url) # Результат: /smedia/documents/private/2026/03/05/test_document.txt ``` ### Результаты ✅ **Путь корректный**: `documents/private/2026/03/05/test_document.txt` ✅ **Дата в пути**: `2026/03/05` (YYYY/MM/DD) ✅ **URL правильный**: `/smedia/documents/private/2026/03/05/test_document.txt` ✅ **Django check**: 0 ошибок ## 📋 Management команда Создана команда для миграции старых файлов: ```bash python manage.py migrate_documents_storage --dry-run # Предпросмотр python manage.py migrate_documents_storage # Выполнить миграцию ``` **Статус миграции:** - Проверено: 34 записи в БД - Физически файлов: 0 (старые файлы уже удалены) - Миграция не требуется **Файл:** `backend/documents/management/commands/migrate_documents_storage.py` ## 📚 Документация Создана документация со структурой и инструкциями: - **Файл:** `backend/media/README.md` - **Разделы:** - Структура директорий - Настройка FILER_STORAGES - Инструкции по миграции - Безопасность (public vs private) - Конфигурация Nginx для production ## 🧹 Очистка Удалены устаревшие структуры: ```bash rm -rf backend/media/filer_public/ # Старая хеш-структура rm -rf backend/media/documents/private/%Y/ # Тестовые папки ``` **До очистки:** - 47 директорий - 10 тестовых файлов в filer_public/ **После очистки:** - 14 директорий (чистая структура) - .gitkeep файлы во всех пустых папках ## 🎯 Итоги ### ✅ Выполнено 1. ✅ Настроена структура `documents/public/` и `documents/private/` 2. ✅ Изменен генератор путей на `by_date` (YYYY/MM/DD) 3. ✅ Обновлена конфигурация Django (settings.py) 4. ✅ Исправлен URL routing для production 5. ✅ Настроен .gitignore для media/ 6. ✅ Создана management команда миграции 7. ✅ Написана документация (README.md) 8. ✅ Протестировано создание файлов 9. ✅ Удалены старые структуры 10. ✅ Созданы .gitkeep файлы ### 📊 Метрики - **Файлов изменено:** 4 (`settings.py`, `urls.py`, `.gitignore`, `README.md`) - **Файлов создано:** 2 (management команда + отчет) - **Директорий очищено:** 33 (из 47 осталось 14) - **Ошибок Django check:** 0 - **TypeScript ошибок:** 0 (не затронуто) ### 🚀 Готовность к production - ✅ Nginx конфигурация описана в README.md - ✅ URL routing работает корректно - ✅ Безопасность (public/private разделение) - ✅ Структура масштабируемая - ✅ Бэкапы легко организовать по датам ## 📌 Следующие шаги (опционально) 1. **Настройка Nginx** - для раздачи static/media в production 2. **Автоочистка** - удаление старых файлов (например, старше 1 года) 3. **S3 интеграция** - для облачного хранения (если требуется масштабирование) 4. **Мониторинг** - отслеживание размера media/ директории ## 🔗 Связанные документы - [backend/media/README.md](../../backend/media/README.md) - Руководство по структуре - `backend/documents/management/commands/migrate_documents_storage.py` - Команда миграции - `backend/eusrr_backend/settings.py` - Конфигурация FILER_STORAGES