from django.core.management.base import BaseCommand from observations.models import ObservationPhoto class Command(BaseCommand): help = "Génère les miniatures manquantes pour les ObservationPhoto existantes." def add_arguments(self, parser): parser.add_argument( '--force', action='store_true', help="Régénère toutes les miniatures, même celles déjà existantes.", ) def handle(self, *args, **options): force = options['force'] qs = ObservationPhoto.objects.all() if not force: qs = qs.filter(thumbnail__isnull=True) | qs.filter(thumbnail='') total = qs.count() if total == 0: self.stdout.write(self.style.SUCCESS("Aucune miniature à générer.")) return self.stdout.write(f"{total} photo(s) à traiter...") ok = 0 errors = 0 for photo in qs.iterator(): try: if not photo.photo or not photo.photo.storage.exists(photo.photo.name): self.stdout.write( self.style.WARNING(f" [SKIP] pk={photo.pk} – fichier introuvable ({photo.photo.name})") ) continue # Supprimer l'ancienne miniature si --force if force and photo.thumbnail and photo.thumbnail.storage.exists(photo.thumbnail.name): photo.thumbnail.delete(save=False) ObservationPhoto.objects.filter(pk=photo.pk).update(thumbnail='') photo.thumbnail = None photo.generate_thumbnail() ok += 1 self.stdout.write(f" [OK] pk={photo.pk}") except Exception as exc: errors += 1 self.stdout.write( self.style.ERROR(f" [ERR] pk={photo.pk} – {exc}") ) self.stdout.write( self.style.SUCCESS(f"\nTerminé : {ok} miniature(s) générée(s), {errors} erreur(s).") )