From 7d42fdac9d509ac9defddf246dcd0309a1403b1d Mon Sep 17 00:00:00 2001 From: lizerk Date: Fri, 6 Mar 2026 12:21:56 +0300 Subject: [PATCH] =?UTF-8?q?feat(documents):=20=D0=B8=D0=BD=D1=82=D0=B5?= =?UTF-8?q?=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20django-filer=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D1=83=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлена конфигурация django-filer в settings.py - Настроены URL для filer admin - Обновлен admin.py для работы с FilerFileField - Обновлен индекс поиска для новой структуры --- backend/documents/admin.py | 48 +------------------------------ backend/eusrr_backend/settings.py | 45 +++++++++++++++++++++++++++++ backend/eusrr_backend/urls.py | 8 ++++-- backend/search/search_indexes.py | 2 +- 4 files changed, 53 insertions(+), 50 deletions(-) diff --git a/backend/documents/admin.py b/backend/documents/admin.py index 143e9f0e..e33f3f43 100644 --- a/backend/documents/admin.py +++ b/backend/documents/admin.py @@ -15,7 +15,6 @@ from django.contrib import admin from django.utils.translation import gettext_lazy as _ from django.utils.html import format_html, mark_safe from django.urls import reverse -from django_fsm import get_available_FIELD_transitions from .models import Document, DocumentAcknowledgement from django.contrib.auth import get_user_model @@ -61,14 +60,13 @@ class DocumentAdmin(admin.ModelAdmin): list_display = ( 'title', - 'status_badge', # FSM статус с цветовым индикатором 'file_thumbnail', 'file_info', 'uploaded_at', 'sent_to_all', 'acknowledgement_status', ) - list_filter = ('status', 'sent_to_all', 'uploaded_at', 'departments') + list_filter = ('sent_to_all', 'uploaded_at', 'departments') search_fields = ('title', 'description', 'file__name', 'file__description') filter_horizontal = ('recipients', 'departments') actions = ['send_document'] @@ -80,7 +78,6 @@ class DocumentAdmin(admin.ModelAdmin): (None, { 'fields': ( 'title', - 'status', # FSM статус 'file', # FilerFileField с drag & drop 'file_preview', # Превью файла 'description', @@ -89,10 +86,6 @@ class DocumentAdmin(admin.ModelAdmin): 'recipients' ), }), - (_('Workflow'), { - 'fields': ('available_transitions',), - 'classes': ('collapse',), - }), (_('Meta'), { 'fields': ('uploaded_by', 'uploaded_at', 'file_size', 'file_extension'), 'classes': ('collapse',), @@ -102,8 +95,6 @@ class DocumentAdmin(admin.ModelAdmin): }), ) readonly_fields = ( - 'status', # FSM поле только через transitions - 'available_transitions', 'uploaded_by', 'uploaded_at', 'file_preview', @@ -119,43 +110,6 @@ class DocumentAdmin(admin.ModelAdmin): obj.uploaded_by = request.user super().save_model(request, obj, form, change) - def status_badge(self, obj): - """Статус документа с цветовым индикатором.""" - colors = { - 'draft': '#6c757d', # серый - 'in_review': '#0dcaf0', # голубой - 'approved': '#198754', # зеленый - 'published': '#0d6efd', # синий - 'archived': '#6c757d', # серый - 'rejected': '#dc3545', # красный - } - color = colors.get(obj.status, '#6c757d') - return format_html( - '{}', - color, - obj.get_status_display() - ) - status_badge.short_description = _('Статус') - - def available_transitions(self, obj): - """Список доступных переходов (transitions) для текущего состояния.""" - if not obj.pk: - return '—' - - transitions = get_available_FIELD_transitions(obj, Document.status) - if not transitions: - return format_html('Нет доступных переходов') - - html = '' - - return mark_safe(html) - - available_transitions.short_description = _('Доступные переходы') - def file_thumbnail(self, obj): """Превью файла в списке документов.""" if not obj.file: diff --git a/backend/eusrr_backend/settings.py b/backend/eusrr_backend/settings.py index 5f98a48b..e5da09fc 100644 --- a/backend/eusrr_backend/settings.py +++ b/backend/eusrr_backend/settings.py @@ -198,6 +198,13 @@ REGISTRATION_ALLOWED_IPS = [ MEDIA_ROOT = os.path.join(BASE_DIR, "media/") MEDIA_URL = "/media/" +# Структура медиа папки: +# media/ +# ├── documents/ ← Документы (читаемая структура по годам/месяцам) +# ├── avatars/ ← Аватары пользователей +# ├── chat_attachments/ ← Вложения в чате +# └── temp/ ← Временные файлы + # Лимиты загрузки файлов # По умолчанию Django ограничивает загрузку до 2.5MB # Увеличиваем до 10MB для аватаров и документов @@ -548,6 +555,44 @@ FILER_ENABLE_PERMISSIONS = True # Включаем ACL для файлов FILER_IS_PUBLIC_DEFAULT = False # По умолчанию файлы приватные FILER_CANONICAL_URL = 'canonical/' # URL для канонических ссылок +# Настройка хранилищ для разных типов файлов +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', # Структура: 2026/03/14/filename.pdf + }, + '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', # Структура: 2026/03/14/filename.pdf + }, + 'thumbnails': { + 'ENGINE': 'filer.storage.PrivateFileSystemStorage', + 'OPTIONS': { + 'location': os.path.join(MEDIA_ROOT, 'documents/private_thumbnails/'), + 'base_url': '/smedia/documents/private_thumbnails/', + }, + }, + }, +} + # easy-thumbnails настройки для filer THUMBNAIL_PROCESSORS = ( 'easy_thumbnails.processors.colorspace', diff --git a/backend/eusrr_backend/urls.py b/backend/eusrr_backend/urls.py index 5c51d773..47d4319d 100644 --- a/backend/eusrr_backend/urls.py +++ b/backend/eusrr_backend/urls.py @@ -42,7 +42,11 @@ urlpatterns = [ path("api/", include(("api.urls", "api"), namespace="api")), # Service Worker должен быть в корне для правильного scope path("sw.js", serve_service_worker, name="sw"), - # django-filer URLs для обработки приватных файлов + # django-filer URLs для обработки приватных файлов (с проверкой прав доступа) path("", include("filer.server.urls")), path("", include("feed.urls_front", namespace="feed")), -] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +] + +# Публичные медиа файлы (без проверки прав) - только в режиме разработки +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/backend/search/search_indexes.py b/backend/search/search_indexes.py index c876fba3..19c75890 100644 --- a/backend/search/search_indexes.py +++ b/backend/search/search_indexes.py @@ -163,7 +163,7 @@ if Document: Document, adapter_cls=DocumentAdapter, fields=("title", "description", "extracted_text"), # Добавлен extracted_text! - store=("title", "uploaded_at", "status"), + store=("title", "uploaded_at"), ) # 12. Уведомления (Notifications)