From 7e63fae613b2f92eb010de0ab77b931841c1089b Mon Sep 17 00:00:00 2001 From: erjemin Date: Mon, 8 Jun 2026 00:47:57 +0300 Subject: [PATCH] =?UTF-8?q?mod:=20django-filer=20=D0=BD=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=B9=D0=BA=D0=B0=20(07)=20=D1=80=D0=B0=D0=B7?= =?UTF-8?q?=D0=BC=D0=B5=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=84=D0=B0=D0=B9?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lpon_site/frontend/apps.py | 38 ++++++++------- lpon_site/lpon_site/settings.py | 86 ++++++++++++--------------------- 2 files changed, 51 insertions(+), 73 deletions(-) diff --git a/lpon_site/frontend/apps.py b/lpon_site/frontend/apps.py index 3dc0bcb..214f178 100644 --- a/lpon_site/frontend/apps.py +++ b/lpon_site/frontend/apps.py @@ -6,7 +6,7 @@ from django.apps import AppConfig from django.core.files.base import ContentFile from PIL import Image as PILImage -from lpon_site.settings import THUMBNAIL_WEBP_QUALITY +from lpon_site.settings import DEBUG, THUMBNAIL_WEBP_QUALITY # Получаем логгер для текущего модуля logger = logging.getLogger(__name__) @@ -28,17 +28,24 @@ class CustomFilerConfig(AppConfig): name = 'filer' verbose_name = 'Медиафайлы' - @staticmethod - def generate_upload_path_flr(instance, filename): - from filer.utils.generate_filename import randomized - base_path = randomized(instance, filename) - return f'flr/{base_path}' - - @staticmethod - def generate_upload_path_flrm(instance, filename): - from filer.utils.generate_filename import randomized - base_path = randomized(instance, filename) - return f'flrm/{base_path}' + # ======================================================================== + # Конфигурация Django-Filer, которая читается во время выполнения + # ======================================================================== + FILER_ENABLE_PERMISSIONS = DEBUG + FILER_MAX_UPLOAD_SIZE = 100 * 1024 * 1024 + FILER_WHITELIST_FOR_PATH_ACCESS = ( + '.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', + '.doc', '.docx', '.pdf', '.txt', '.xls', '.xlsx', '.csv', + ) + MIME_TYPE_WHITELIST = ( + 'image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp', + 'application/pdf', 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'text/plain', 'application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'text/csv', + ) + FILE_VALIDATORS = {} @staticmethod def _convert_to_webp_if_needed(name: str, content): @@ -52,7 +59,7 @@ class CustomFilerConfig(AppConfig): buffer = BytesIO() img.save(buffer, format="WEBP", quality=THUMBNAIL_WEBP_QUALITY) buffer.seek(0) - new_name = name.rsplit(original_ext, 1)[0] + ".webp" + new_name = os.path.splitext(name)[0] + ".webp" logger.info(f"Successfully converted '{name}' to '{new_name}' (WebP).") return ContentFile(buffer.read()), new_name, True except Exception: @@ -80,11 +87,8 @@ class CustomFilerConfig(AppConfig): new_content.seek(0) self_instance.instance._file_size = len(file_bytes) self_instance.instance.sha1 = hashlib.sha1(file_bytes).hexdigest() + return original_save(self_instance, new_name, new_content, save) MultiStorageFieldFile.save = patched_save logger.info("MultiStorageFieldFile.save() patched successfully.") - -# Создаем псевдонимы на уровне модуля для функций, чтобы их мог найти Django -generate_upload_path_flr = CustomFilerConfig.generate_upload_path_flr -generate_upload_path_flrm = CustomFilerConfig.generate_upload_path_flrm diff --git a/lpon_site/lpon_site/settings.py b/lpon_site/lpon_site/settings.py index b3c58d5..14009ec 100644 --- a/lpon_site/lpon_site/settings.py +++ b/lpon_site/lpon_site/settings.py @@ -66,7 +66,7 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', # Django-filer и его зависимости # Порядок важен! polymorphic должен быть ДО filer, easy_thumbnails тоже ДО filer - 'polymorphic', + # 'polymorphic', 'easy_thumbnails', # 'filer', # Кастомная надстройка над filer для переопределения verbose_name (и других настроек) @@ -161,82 +161,56 @@ STATICFILES_DIRS = [PUBLIC_DIR.joinpath('static')] STATIC_ROOT = PUBLIC_DIR.joinpath('staticfiles') # ============================================================================ -# Django-Filer Configuration - WebP Conversion Storage +# Django-Filer & Easy-Thumbnails Configuration # ============================================================================ -# Настройка хранилища filer для автоматического преобразования изображений в WebP -# ВАЖНО: эта конфигурация должна быть в settings.py (не в class attribute), -# т.к. Django-filer загружает её ДО инициализации app configs -# ВАЖНО: location должна быть равна MEDIA_ROOT, иначе Django не сможет -# сгенерировать правильные URL для файлов (URL генерируется как MEDIA_URL + relative_path) -# -# Структура папок: -# public/media/ -# ├── flr/ <- основные загруженные файлы (картинки) -# ├── flrm/ <- миниатюры (thumbnails) -# ├── filer_public/ <- старая структура (больше не используется) -# └── filer_public_thumbnails/ <- старая структура (больше не используется) FILER_STORAGES = { 'public': { 'main': { - # Используем стандартное хранилище Django. Логика конвертации в apps.py - 'ENGINE': 'django.core.files.storage.FileSystemStorage', + 'ENGINE': 'filer.storage.PublicFileSystemStorage', 'OPTIONS': { - 'location': str(MEDIA_ROOT), + 'location': MEDIA_ROOT / 'flr', + 'base_url': MEDIA_URL + 'flr/', }, - # UPLOAD_TO функция добавляет 'flr/' префикс для более компактных путей в шаблонах - 'UPLOAD_TO': 'frontend.apps.generate_upload_path_flr', + 'UPLOAD_TO': 'filer.utils.generate_filename.randomized', 'UPLOAD_TO_PREFIX': '', }, 'thumbnails': { - 'ENGINE': 'django.core.files.storage.FileSystemStorage', + 'ENGINE': 'filer.storage.PublicFileSystemStorage', 'OPTIONS': { - 'location': str(MEDIA_ROOT), - }, - # Миниатюры идят в папку flrm через UPLOAD_TO функцию - 'UPLOAD_TO': 'frontend.apps.generate_upload_path_flrm', - 'THUMBNAIL_OPTIONS': { - 'base_dir': 'flrm', + 'location': MEDIA_ROOT / 'flrm', + 'base_url': MEDIA_URL + 'flrm/', + # 'location': os.path.join(MEDIA_ROOT, 'flrm'), + # 'base_url': os.path.join(MEDIA_URL, 'flrm/'), }, + # 'UPLOAD_TO': 'filer.utils.generate_filename.randomized', + # 'UPLOAD_TO_PREFIX': '_', }, }, } -# ============================================================================ -# Easy-Thumbnails Configuration - WebP Generation -# ============================================================================ -# Настройка генерирования миниатюр в формате WebP вместо JPEG/PNG -THUMBNAIL_PRESERVE_FORMAT = False # Не сохранять оригинальный формат для миниатюр -THUMBNAIL_FORMAT = 'WEBP' # Конвертировать все миниатюры в WebP -THUMBNAIL_QUALITY = 80 # Качество WebP (достаточно 75-85 для миниатюр) -# Кастомная настройка для встроенного конвертора загружаемых файлов в WebP (см. apps.py) -THUMBNAIL_WEBP_QUALITY = 80 # Качество для WebP -FILER_ENABLE_PERMISSIONS = DEBUG -FILER_WHITELIST_FOR_PATH_ACCESS = ( - '.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', - '.doc', '.docx', '.pdf', '.txt', '.xls', '.xlsx', '.csv', -) -FILER_MAX_UPLOAD_SIZE = 100 * 1024 * 1024 -MIME_TYPE_WHITELIST = ( - 'image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp', - 'application/pdf', 'application/msword', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'text/plain', 'application/vnd.ms-excel', - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'text/csv', -) -THUMBNAIL_ENGINE = 'easy_thumbnails.engines.pil_engine.PilEngine' +THUMBNAIL_PRESERVE_FORMAT = False +THUMBNAIL_FORMAT = 'WEBP' THUMBNAIL_DEBUG = DEBUG -FILE_VALIDATORS = {} - -# Размеры миниатюр для разных использований +THUMBNAIL_WEBP_QUALITY = 80 +THUMBNAIL_ENGINE = 'easy_thumbnails.engines.pil_engine.PilEngine' THUMBNAIL_ALIASES = { '': { - # Для админ-интерфейса 'admin_thumbnail': {'size': (64, 64), 'crop': True}, - # Для фронтенда 'small': {'size': (256, 256), 'crop': True}, 'medium': {'size': (512, 512), 'crop': True}, 'large': {'size': (1024, 1024), 'crop': 'smart'}, }, -} \ No newline at end of file +} +FILER_UPLOADER_MAX_FILES = 3 +FILER_UPLOADER_MAX_FILE_SIZE = 100 * 1024 * 1024 +FILER_MAX_IMAGE_PIXELS = 4096 * 4096 + +# Настройки для "умной" обрезки изобращений +THUMBNAIL_PROCESSORS = ( + 'easy_thumbnails.processors.colorspace', + 'easy_thumbnails.processors.autocrop', + #'easy_thumbnails.processors.scale_and_crop', + 'filer.thumbnail_processors.scale_and_crop_with_subject_location', + 'easy_thumbnails.processors.filters', +) \ No newline at end of file