Coverage for extras/management/commands/populate_image_sizes.py: 0%

37 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 18:35 +0000

1from django.core.management.base import BaseCommand 

2 

3from extras.models import ImageAttachment 

4 

5# Number of records to read and write per batch 

6BATCH_SIZE = 100 

7 

8 

9class Command(BaseCommand): 

10 help = "Populate the image_size field for ImageAttachments that predate it" 

11 

12 def handle(self, *args, **options): 

13 verbosity = options['verbosity'] 

14 

15 # Only consider attachments whose size has not yet been cached. This is safe to re-run: rows whose file 

16 # was unreadable on a previous run remain NULL and will be retried. 

17 queryset = ImageAttachment.objects.filter(image_size__isnull=True) 

18 total = queryset.count() 

19 

20 if not total: 

21 if verbosity: 

22 self.stdout.write(self.style.SUCCESS("No image attachments require updating.")) 

23 return 

24 

25 if verbosity: 

26 self.stdout.write(f"Populating image_size for {total} image attachment(s)...") 

27 

28 updated = 0 

29 skipped = 0 

30 batch = [] 

31 

32 for processed, attachment in enumerate(queryset.iterator(chunk_size=BATCH_SIZE), start=1): 

33 # These rows have image_size=NULL, so the size property reads the file from storage (returning None if 

34 # it's inaccessible) rather than returning a cached value. 

35 size = attachment.size 

36 if size is None: 

37 # File is inaccessible; leave image_size NULL so a future run can retry. 

38 skipped += 1 

39 else: 

40 attachment.image_size = size 

41 batch.append(attachment) 

42 

43 if len(batch) >= BATCH_SIZE: 

44 ImageAttachment.objects.bulk_update(batch, ['image_size']) 

45 updated += len(batch) 

46 batch = [] 

47 

48 # Reading each file's size may issue a request to the storage backend, so emit periodic progress 

49 # for large tables rather than going silent until completion. 

50 if verbosity and processed % BATCH_SIZE == 0: 

51 self.stdout.write(f" Processed {processed}/{total}...") 

52 

53 if batch: 

54 ImageAttachment.objects.bulk_update(batch, ['image_size']) 

55 updated += len(batch) 

56 

57 if verbosity: 

58 self.stdout.write(self.style.SUCCESS(f"Updated {updated} image attachment(s).")) 

59 if skipped: 

60 self.stdout.write(self.style.WARNING( 

61 f"Skipped {skipped} inaccessible file(s); re-run this command to retry them." 

62 ))