Coverage for website/photos/admin.py: 82.14%

52 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2025-08-14 10:31 +0000

1from django.contrib import admin, messages 

2from django.db import transaction 

3from django.db.models import Count 

4from django.utils.translation import gettext_lazy as _ 

5 

6from .forms import AlbumForm 

7from .models import Album, Like, Photo 

8from .tasks import process_album_upload 

9 

10 

11@admin.register(Album) 

12class AlbumAdmin(admin.ModelAdmin): 

13 """Model for Album admin page.""" 

14 

15 list_display = ( 

16 "title", 

17 "date", 

18 "num_photos", 

19 "hidden", 

20 "is_processing", 

21 "shareable", 

22 ) 

23 fields = ( 

24 "title", 

25 "slug", 

26 "date", 

27 "event", 

28 "hidden", 

29 "is_processing", 

30 "shareable", 

31 "album_archive", 

32 "_cover", 

33 ) 

34 readonly_fields = ("is_processing",) 

35 search_fields = ("title", "date") 

36 list_filter = ("hidden", "shareable") 

37 date_hierarchy = "date" 

38 prepopulated_fields = { 

39 "slug": ( 

40 "date", 

41 "title", 

42 ) 

43 } 

44 form = AlbumForm 

45 

46 def get_fields(self, request, obj=None): 

47 fields = list(super().get_fields(request, obj)) 

48 if obj is None: 48 ↛ 51line 48 didn't jump to line 51 because the condition on line 48 was always true

49 fields.remove("_cover") 

50 

51 return fields 

52 

53 def get_queryset(self, request): 

54 """Get Albums and add the amount of photos as an annotation.""" 

55 return Album.objects.annotate(photos_count=Count("photo")) 

56 

57 def num_photos(self, obj): 

58 """Pretty-print the number of photos.""" 

59 return obj.photos_count 

60 

61 num_photos.short_description = _("Number of photos") 

62 num_photos.admin_order_field = "photos_count" 

63 

64 def save_model(self, request, obj, form, change): 

65 """Save the new Album by extracting the archive.""" 

66 super().save_model(request, obj, form, change) 

67 

68 archive = form.cleaned_data.get("album_archive") 

69 if archive is not None: 69 ↛ 70line 69 didn't jump to line 70 because the condition on line 69 was never true

70 obj.is_processing = True 

71 obj.save() 

72 # Schedule a celery task to unpack the upload in the background. In local development 

73 # (when to Redis queue is set up) these tasks are run immediately as if it is a normal 

74 # function call. In a real deployment, it has to be called only when the current db 

75 # transaction is committed, 

76 transaction.on_commit( 

77 process_album_upload.s( 

78 archive.temporary_upload.upload_id, 

79 obj.id, 

80 uploader_id=request.user.id, 

81 ).delay 

82 ) 

83 

84 self.message_user( 

85 request, 

86 "Album is being processed, is_processing will become False when it is ready.", 

87 messages.INFO, 

88 ) 

89 

90 def get_deleted_objects(self, objs, request): 

91 ( 

92 deleted_objects, 

93 model_count, 

94 perms_needed, 

95 protected, 

96 ) = super().get_deleted_objects(objs, request) 

97 

98 # Drop any missing delete permissions. If the user has `delete_album` permission, 

99 # they should automatically be allowed to cascade e.g. related pushnotifications. 

100 return deleted_objects, model_count, set(), protected 

101 

102 

103class LikeInline(admin.StackedInline): 

104 model = Like 

105 extra = 0 

106 

107 

108@admin.register(Photo) 

109class PhotoAdmin(admin.ModelAdmin): 

110 """Model for Photo admin page.""" 

111 

112 list_display = ( 

113 "__str__", 

114 "album", 

115 "num_likes", 

116 ) 

117 search_fields = ("file",) 

118 list_filter = ("album",) 

119 exclude = ("_digest",) 

120 

121 inlines = [ 

122 LikeInline, 

123 ] 

124 

125 def get_deleted_objects(self, objs, request): 

126 ( 

127 deleted_objects, 

128 model_count, 

129 perms_needed, 

130 protected, 

131 ) = super().get_deleted_objects(objs, request) 

132 

133 return deleted_objects, model_count, set(), protected