fix: Сохранять файл фото ДО запуска Celery task

При асинхронной обработке фото нужно сначала сохранить файл в БД,
потом запустить Celery task. Иначе task не найдет файл.

Изменения:
- BasePhoto.save() теперь сохраняет файл перед запуском task
- Исправлена проблема 'Photo has no image file' в Celery worker

🤖 Generated with Claude Code
This commit is contained in:
2025-11-15 11:11:08 +03:00
parent a03f4c3047
commit 0791ebb13b
11 changed files with 968 additions and 67 deletions

View File

@@ -0,0 +1,9 @@
"""
=8F80;870F8O Celery 4;O 02B><0B8G5A:>9 703@C7:8 ?@8 AB0@B5 Django.
-B> 30@0=B8@C5B GB> Celery app 1C45B 8=8F80;878@>20= 2 <><5=B 70?CA:0 Django,
GB> ?>72>;O5B @shared_task 45:>@0B>@C @01>B0BL ?@028;L=>.
"""
from .celery import app as celery_app
__all__ = ('celery_app',)

View File

@@ -0,0 +1,34 @@
"""
Celery configuration for myproject with django-tenants support.
IMPORTANT: В мультитенантной среде все задачи должны:
1. Получать schema_name в параметрах
2. Активировать нужную схему через connection.set_schema()
3. Это гарантирует изоляцию данных по тенантам
"""
import os
import logging
from celery import Celery
from django.conf import settings
logger = logging.getLogger(__name__)
# Указываем Django settings module
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
# Создаем Celery app
app = Celery('myproject')
# Загружаем конфигурацию из Django settings с префиксом CELERY_
app.config_from_object('django.conf:settings', namespace='CELERY')
# Автоматическое обнаружение tasks.py в приложениях
# Это позволяет использовать @shared_task в любом приложении
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
@app.task(bind=True, ignore_result=True)
def debug_task(self):
"""Тестовая задача для проверки работы Celery"""
print(f'Request: {self.request!r}')
logger.info('Celery is working!')

View File

@@ -56,6 +56,9 @@ SHARED_APPS = [
# Accounts должен быть в shared для CustomUser (используется в админке)
'accounts',
# Celery results (для сохранения статуса асинхронных задач)
'django_celery_results',
]
# Tenant apps: создаются в отдельной схеме для каждого тенанта (изолированные данные)
@@ -357,3 +360,42 @@ TENANT_ADMIN_NAME = env('TENANT_ADMIN_NAME')
# ============================================
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# ============================================
# CELERY CONFIGURATION
# ============================================
# Redis broker и backend для хранения результатов
CELERY_BROKER_URL = f'redis://{env("REDIS_HOST", default="localhost")}:{env("REDIS_PORT", default="6379")}/{env("REDIS_DB", default="0")}'
CELERY_RESULT_BACKEND = 'django-db' # Сохраняем результаты в БД (совместимо с мультитенантностью)
# Сериализация
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = TIME_ZONE
# Task routing для разделения нагрузки
CELERY_TASK_ROUTES = {
'products.tasks.process_product_photo_async': {'queue': 'photo_processing'},
'products.tasks.process_multiple_photos_async': {'queue': 'photo_processing'},
}
# Worker настройки для обработки длительных задач
CELERY_WORKER_PREFETCH_MULTIPLIER = 1 # Worker берет по одной задаче за раз
CELERY_TASK_ACKS_LATE = True # Подтверждаем задачу только после успешного выполнения
CELERY_WORKER_MAX_TASKS_PER_CHILD = 50 # Перезапускаем worker после 50 задач (защита от утечек памяти PIL)
# Timeouts
CELERY_TASK_TIME_LIMIT = 300 # 5 минут максимум на одну задачу
CELERY_TASK_SOFT_TIME_LIMIT = 240 # 4 минуты - мягкий лимит перед жестким
# Результаты и события
CELERY_RESULT_EXPIRES = 3600 # Результаты хранятся 1 час (достаточно для отслеживания прогресса)
CELERY_WORKER_SEND_TASK_EVENTS = True # Отправляем события для мониторинга
CELERY_TASK_SEND_SENT_EVENT = True
# Retry настройки
CELERY_TASK_DEFAULT_MAX_RETRIES = 3
CELERY_TASK_DEFAULT_RETRY_DELAY = 60 # Повторить через 60 секунд при ошибке