""" Скрипт для очистки застрявших записей фото в БД. Используется когда файлы были удалены вручную до обработки Celery. Использование: docker exec -it mix_web python manage.py shell < cleanup_stuck_photos.py Или интерактивно: docker exec -it mix_web python manage.py shell >>> exec(open('cleanup_stuck_photos.py').read()) """ from django.db import connection from products.models import ProductPhoto, ProductKitPhoto, ProductCategoryPhoto, PhotoProcessingStatus from django.core.files.storage import default_storage # Укажите schema_name вашего тенанта SCHEMA_NAME = 'mixflowers' # Активируем схему connection.set_schema(SCHEMA_NAME) print(f"✓ Activated schema: {SCHEMA_NAME}") # Функция для проверки и очистки фото def cleanup_photo_model(model_class, model_name): """Очищает записи с несуществующими файлами для указанной модели""" print(f"\n{'='*60}") print(f"Checking {model_name}...") print(f"{'='*60}") photos = model_class.objects.all() total = photos.count() print(f"Total {model_name} records: {total}") if total == 0: print(f"No {model_name} records found.") return missing_files = [] for photo in photos: if photo.image: file_path = photo.image.name exists = default_storage.exists(file_path) if not exists: missing_files.append({ 'id': photo.id, 'path': file_path, 'entity': photo.get_entity(), 'photo': photo }) print(f" ✗ Photo #{photo.id}: File NOT found: {file_path}") else: print(f" ✓ Photo #{photo.id}: File exists: {file_path}") if missing_files: print(f"\n{'='*60}") print(f"Found {len(missing_files)} {model_name} with missing files:") print(f"{'='*60}") for item in missing_files: print(f"\nPhoto ID: {item['id']}") print(f" Entity: {item['entity']}") print(f" Missing file: {item['path']}") # Спрашиваем подтверждение print(f"\n{'='*60}") response = input(f"Delete these {len(missing_files)} {model_name} records? (yes/no): ").strip().lower() if response == 'yes': deleted_count = 0 for item in missing_files: try: item['photo'].delete() deleted_count += 1 print(f" ✓ Deleted Photo #{item['id']}") except Exception as e: print(f" ✗ Error deleting Photo #{item['id']}: {e}") print(f"\n✓ Deleted {deleted_count} {model_name} records") else: print(f"\nSkipped deletion for {model_name}") else: print(f"\n✓ All {model_name} files exist. No cleanup needed.") # Очищаем каждую модель cleanup_photo_model(ProductPhoto, "ProductPhoto") cleanup_photo_model(ProductKitPhoto, "ProductKitPhoto") cleanup_photo_model(ProductCategoryPhoto, "ProductCategoryPhoto") # Проверяем застрявшие статусы обработки print(f"\n{'='*60}") print(f"Checking PhotoProcessingStatus...") print(f"{'='*60}") stuck_statuses = PhotoProcessingStatus.objects.filter( status__in=['pending', 'processing'] ).order_by('-created_at') if stuck_statuses.exists(): print(f"Found {stuck_statuses.count()} stuck processing statuses:") for status in stuck_statuses: print(f"\n Photo ID: {status.photo_id}") print(f" Model: {status.photo_model}") print(f" Status: {status.status}") print(f" Created: {status.created_at}") print(f" Task ID: {status.task_id}") response = input(f"\nMark these as 'failed'? (yes/no): ").strip().lower() if response == 'yes': updated = stuck_statuses.update( status='failed', error_message='Marked as failed during cleanup (file was deleted before processing)' ) print(f"\n✓ Updated {updated} processing statuses to 'failed'") else: print("\nSkipped updating processing statuses") else: print("✓ No stuck processing statuses found") print(f"\n{'='*60}") print("Cleanup complete!") print(f"{'='*60}")