- Добавлена папка ДОКУМЕНТАЦИЯ с централизованным хранением всех руководств - Перенесены утилитарные скрипты в myproject/scripts/ - Удалены временные файлы (current_settings.txt, old_settings.txt, nul) - Добавлены celerybeat-schedule файлы в .gitignore - Обновлен .env.example (удалены устаревшие настройки PLATFORM_SUPPORT) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
125 lines
4.5 KiB
Python
125 lines
4.5 KiB
Python
"""
|
||
Скрипт для очистки застрявших записей фото в БД.
|
||
Используется когда файлы были удалены вручную до обработки 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}")
|