""" Admin для упрощенной системы уведомлений """ from django.contrib import admin from django.utils.html import format_html from django.utils.safestring import mark_safe from .models import Notification, UserChannelPreferences @admin.register(Notification) class NotificationAdmin(admin.ModelAdmin): """Admin для модели Notification""" list_display = [ 'id', 'get_actor_display', 'verb', 'get_recipient_display', 'unread', 'public', 'deleted', 'emailed', 'timestamp', ] list_filter = [ 'unread', 'public', 'deleted', 'emailed', 'verb', 'timestamp', ] search_fields = [ 'recipient__username', 'recipient__first_name', 'recipient__last_name', 'recipient__email', 'verb', 'description', ] readonly_fields = [ 'get_actor_info', 'get_action_object_info', 'get_target_info', 'timestamp', 'timestamp_read', ] fieldsets = ( ('Основная информация', { 'fields': ( 'recipient', 'verb', 'description', 'action_url', ) }), ('Связанные объекты', { 'fields': ( 'get_actor_info', 'get_action_object_info', 'get_target_info', ), 'classes': ('collapse',), }), ('Дополнительные данные', { 'fields': ( 'data', ), 'classes': ('collapse',), }), ('Статусы', { 'fields': ( 'unread', 'timestamp_read', 'public', 'deleted', 'emailed', ) }), ('Временные метки', { 'fields': ( 'timestamp', ) }), ) date_hierarchy = 'timestamp' actions = [ 'mark_as_read', 'mark_as_unread', 'mark_as_deleted', 'mark_as_not_deleted', ] def get_actor_display(self, obj): """Отображение актора в списке""" if obj.actor: return format_html( '{}', obj.actor_content_type.model, str(obj.actor)[:50] ) return '-' get_actor_display.short_description = 'Актор' def get_recipient_display(self, obj): """Отображение получателя""" return format_html( '{}', obj.recipient.id, obj.recipient.get_short_name() ) get_recipient_display.short_description = 'Получатель' def get_actor_info(self, obj): """Подробная информация об акторе""" if not obj.actor: return '-' info = f""" Тип: {obj.actor_content_type.model}
ID: {obj.actor_object_id}
Объект: {obj.actor} """ return mark_safe(info) get_actor_info.short_description = 'Информация об акторе' def get_action_object_info(self, obj): """Подробная информация об объекте действия""" if not obj.action_object: return '-' info = f""" Тип: {obj.action_object_content_type.model}
ID: {obj.action_object_object_id}
Объект: {obj.action_object} """ return mark_safe(info) get_action_object_info.short_description = 'Объект действия' def get_target_info(self, obj): """Подробная информация о целевом объекте""" if not obj.target: return '-' info = f""" Тип: {obj.target_content_type.model}
ID: {obj.target_object_id}
Объект: {obj.target} """ return mark_safe(info) get_target_info.short_description = 'Целевой объект' # === Actions === @admin.action(description='Отметить как прочитанные') def mark_as_read(self, request, queryset): """Отметить выбранные уведомления как прочитанные""" count = 0 for notification in queryset: if notification.unread: notification.mark_as_read() count += 1 self.message_user(request, f'Отмечено как прочитанные: {count} уведомлений') @admin.action(description='Отметить как непрочитанные') def mark_as_unread(self, request, queryset): """Отметить выбранные уведомления как непрочитанные""" count = 0 for notification in queryset: if not notification.unread: notification.mark_as_unread() count += 1 self.message_user(request, f'Отмечено как непрочитанные: {count} уведомлений') @admin.action(description='Пометить как удаленные') def mark_as_deleted(self, request, queryset): """Пометить как удаленные (мягкое удаление)""" count = queryset.filter(deleted=False).update(deleted=True) self.message_user(request, f'Помечено как удаленные: {count} уведомлений') @admin.action(description='Восстановить удаленные') def mark_as_not_deleted(self, request, queryset): """Восстановить удаленные""" count = queryset.filter(deleted=True).update(deleted=False) self.message_user(request, f'Восстановлено: {count} уведомлений') @admin.register(UserChannelPreferences) class UserChannelPreferencesAdmin(admin.ModelAdmin): """Admin для настроек каналов пользователя""" list_display = [ 'user', 'web_enabled', 'email_enabled', 'push_enabled', 'email_frequency', 'dnd_enabled', 'get_disabled_verbs_count', ] list_filter = [ 'web_enabled', 'email_enabled', 'push_enabled', 'email_frequency', 'dnd_enabled', ] search_fields = [ 'user__username', 'user__first_name', 'user__last_name', 'user__email', ] fieldsets = ( ('Пользователь', { 'fields': ('user',) }), ('Каналы доставки', { 'fields': ( 'web_enabled', 'email_enabled', 'push_enabled', ) }), ('Email настройки', { 'fields': ( 'email_frequency', ) }), ('Режим "Не беспокоить"', { 'fields': ( 'dnd_enabled', 'dnd_start_time', 'dnd_end_time', ) }), ('Фильтры', { 'fields': ( 'disabled_verbs', ), 'classes': ('collapse',), }), ('Метаданные', { 'fields': ( 'created_at', 'updated_at', ), 'classes': ('collapse',), }), ) readonly_fields = ['created_at', 'updated_at'] def get_disabled_verbs_count(self, obj): """Количество отключенных типов""" count = len(obj.disabled_verbs) if count == 0: return '-' return format_html( '{} типов', ', '.join(obj.disabled_verbs), count ) get_disabled_verbs_count.short_description = 'Отключено типов'