website/photos/admin.py
from django.contrib import admin, messages
from django.db import transaction
from django.db.models import Count
from django.utils.translation import gettext_lazy as _
from .forms import AlbumForm
from .models import Album, Like, Photo
from .tasks import process_album_upload
@admin.register(Album)
class AlbumAdmin(admin.ModelAdmin):
"""Model for Album admin page."""
list_display = (
"title",
"date",
"num_photos",
"hidden",
"is_processing",
"shareable",
)
fields = (
"title",
"slug",
"date",
"event",
"hidden",
"is_processing",
"shareable",
"album_archive",
"_cover",
)
readonly_fields = ("is_processing",)
search_fields = ("title", "date")
list_filter = ("hidden", "shareable")
date_hierarchy = "date"
prepopulated_fields = {
"slug": (
"date",
"title",
)
}
form = AlbumForm
def get_fields(self, request, obj=None):
fields = list(super().get_fields(request, obj))
if obj is None:
fields.remove("_cover")
return fields
def get_queryset(self, request):
"""Get Albums and add the amount of photos as an annotation."""
return Album.objects.annotate(photos_count=Count("photo"))
def num_photos(self, obj):
"""Pretty-print the number of photos."""
return obj.photos_count
num_photos.short_description = _("Number of photos")
num_photos.admin_order_field = "photos_count"
def save_model(self, request, obj, form, change):
"""Save the new Album by extracting the archive."""
super().save_model(request, obj, form, change)
archive = form.cleaned_data.get("album_archive")
if archive is not None:
obj.is_processing = True
obj.save()
# Schedule a celery task to unpack the upload in the background. In local development
# (when to Redis queue is set up) these tasks are run immediately as if it is a normal
# function call. In a real deployment, it has to be called only when the current db
# transaction is committed,
transaction.on_commit(
process_album_upload.s(
archive.temporary_upload.upload_id,
obj.id,
uploader_id=request.user.id,
).delay
)
self.message_user(
request,
"Album is being processed, is_processing will become False when it is ready.",
messages.INFO,
)
def get_deleted_objects(self, objs, request):
(
deleted_objects,
model_count,
perms_needed,
protected,
) = super().get_deleted_objects(objs, request)
# Drop any missing delete permissions. If the user has `delete_album` permission,
# they should automatically be allowed to cascade e.g. related pushnotifications.
return deleted_objects, model_count, set(), protected
class LikeInline(admin.StackedInline):
model = Like
extra = 0
@admin.register(Photo)
class PhotoAdmin(admin.ModelAdmin):
"""Model for Photo admin page."""
list_display = (
"__str__",
"album",
"num_likes",
)
search_fields = ("file",)
list_filter = ("album",)
exclude = ("_digest",)
inlines = [
LikeInline,
]
def get_deleted_objects(self, objs, request):
(
deleted_objects,
model_count,
perms_needed,
protected,
) = super().get_deleted_objects(objs, request)
return deleted_objects, model_count, set(), protected