from django.contrib import admin from django.utils.translation import gettext_lazy as _ from django.urls import reverse from django.utils.html import format_html from ajaxdatatable.admin import AjaxDatatable from apps.library.models import * @admin.register(Book) class BookAdmin(AjaxDatatable): list_display = ('title', 'slug', 'status', 'pin', 'file_type', 'view_count', 'created_at') list_filter = ('status', 'pin', 'file_type', 'created_at', 'updated_at') search_fields = ('title', 'slug', 'summary', 'description') # autocomplete_fields = ('categories', 'collections', ) fieldsets = ( (None, { 'fields': ('title', 'slug', 'summary', 'description', 'thumbnail', 'pages_count') }), (_('Status'), { 'fields': ('status', 'pin') }), (_('File Information'), { 'fields': ('file_type', 'book_file') }), (_('Relations'), { 'fields': ('categories', 'collections') }), (_('Statistics'), { 'fields': ('view_count',) }), ) class BookCollectionAdminBase(AjaxDatatable): """Base admin class for all book collection types""" list_display = ('get_title', 'status', 'order', 'count_books') list_filter = ('status',) search_fields = ('title',) autocomplete_fields = ('books',) ordering = ('order',) fieldsets = ( (None, { 'fields': ('title', 'summary', 'status', 'order') }), (_('Books'), { 'fields': ('books',) }), ) exclude = ('display_position',) def get_title(self, obj): return str(obj.title) get_title.short_description = _('Title') @admin.display(description=_('Number of Books')) def count_books(self, obj): count = obj.books.count() if count > 0: url = reverse('admin:library_book_changelist') + f'?collections__id__exact={obj.id}' return format_html('{}', url, count) return count @admin.register(PinnedBookCollection) class PinnedBookCollectionAdmin(BookCollectionAdminBase): """Admin for pinned book collections only""" def get_queryset(self, request): # Only show pinned collections return super().get_queryset(request).filter(display_position=BookCollection.DisplayPosition.PINNED) def save_model(self, request, obj, form, change): # Ensure the display_position is always set to PINNED obj.display_position = BookCollection.DisplayPosition.PINNED super().save_model(request, obj, form, change) @admin.register(MiddleBookCollection) class MiddleBookCollectionAdmin(BookCollectionAdminBase): """Admin for middle section book collections only""" def get_queryset(self, request): # Only show middle section collections return super().get_queryset(request).filter(display_position=BookCollection.DisplayPosition.MIDDLE) def has_add_permission(self, request): # Check if a middle collection already exists exists = BookCollection.objects.filter(display_position=BookCollection.DisplayPosition.MIDDLE).exists() # Only allow adding if no middle collection exists return not exists def has_delete_permission(self, request, obj=None): # Prevent deletion of the middle collection return False def save_model(self, request, obj, form, change): # Ensure the display_position is always set to MIDDLE obj.display_position = BookCollection.DisplayPosition.MIDDLE super().save_model(request, obj, form, change) def changelist_view(self, request, extra_context=None): # Check if a middle collection exists try: # Try to get the first (and should be only) middle collection obj = self.get_queryset(request).first() if obj: # If it exists, redirect to the change view for this object from django.http import HttpResponseRedirect from django.urls import reverse url = reverse( 'admin:%s_%s_change' % (obj._meta.app_label, obj._meta.model_name), args=[obj.pk] ) return HttpResponseRedirect(url) except Exception: # If any error occurs, just show the changelist view as usual pass # If no object exists or there was an error, show the default changelist view return super().changelist_view(request, extra_context) @admin.register(BottomBookCollection) class BottomBookCollectionAdmin(BookCollectionAdminBase): """Admin for bottom section book collections only""" def get_queryset(self, request): # Only show bottom section collections return super().get_queryset(request).filter(display_position=BookCollection.DisplayPosition.BOTTOM) def has_add_permission(self, request): # Check if a bottom collection already exists exists = BookCollection.objects.filter(display_position=BookCollection.DisplayPosition.BOTTOM).exists() # Only allow adding if no bottom collection exists return not exists def has_delete_permission(self, request, obj=None): # Prevent deletion of the bottom collection return False def save_model(self, request, obj, form, change): # Ensure the display_position is always set to BOTTOM obj.display_position = BookCollection.DisplayPosition.BOTTOM super().save_model(request, obj, form, change) def changelist_view(self, request, extra_context=None): # Check if a bottom collection exists try: # Try to get the first (and should be only) bottom collection obj = self.get_queryset(request).first() if obj: # If it exists, redirect to the change view for this object from django.http import HttpResponseRedirect from django.urls import reverse url = reverse( 'admin:%s_%s_change' % (obj._meta.app_label, obj._meta.model_name), args=[obj.pk] ) return HttpResponseRedirect(url) except Exception: # If any error occurs, just show the changelist view as usual pass # If no object exists or there was an error, show the default changelist view return super().changelist_view(request, extra_context) @admin.register(Category) class CategoryAdmin(AjaxDatatable): list_display = ('title', 'slug', 'status', 'count_books', 'created_at') list_filter = ('status', 'created_at', 'updated_at') search_fields = ('title', 'slug') # autocomplete_fields = ('books',) @admin.display(description=_('Number of Books')) def count_books(self, obj): count = obj.books_count if count > 0: url = reverse('admin:library_book_changelist') + f'?categories__id__exact={obj.id}' return format_html('{}', url, count) return count