/
Anubis55
/
Web-Polytech
Обзор
Документация
Войти
/
Anubis55
/
Web-Polytech
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
comics/admin.py
306 строк
11 KB
Anubis55
добавлены докстринги и аннотации к методам, классам
25 июн 2026, 07:23
25 июн 2026, 07:23
060db92
Код
Авторство
О чём код?
from typing import Any, List, Optional, Tuple, Type from django.contrib import admin, messages from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from django.utils.html import format_html from django.urls import path from django.shortcuts import redirect from django.http import HttpResponse, HttpRequest, HttpResponseRedirect from django.template.loader import render_to_string from weasyprint import HTML from simple_history.admin import SimpleHistoryAdmin from import_export import resources from import_export.admin import ImportExportModelAdmin from .models import Genre, Author, Comic, Chapter, Favorite, Rating, Comment, Profile, Appeal, AppealFile class ComicResource(resources.ModelResource): """Ресурс для импорта/экспорта комиксов.""" class Meta: model = Comic fields = ('id', 'title', 'status', 'views', 'created_at', 'updated_at') class ChapterResource(resources.ModelResource): """Ресурс для импорта/экспорта глав.""" comic_title = resources.Field(attribute='comic__title', column_name='Название комикса') class Meta: model = Chapter fields = ('id', 'comic_title', 'number', 'title', 'published_at') export_order = fields class ChapterInline(admin.TabularInline): """Инлайн-редактор глав на странице комикса.""" model = Chapter extra = 1 fields = ['number', 'title', 'content', 'views', 'published_at'] readonly_fields = ['views'] @admin.register(Genre) class GenreAdmin(admin.ModelAdmin): """Администрирование жанров.""" list_display = ['id', 'name', 'slug'] list_display_links = ['id', 'name'] search_fields = ['name'] prepopulated_fields = {'slug': ('name',)} readonly_fields = ['id'] @admin.register(Author) class AuthorAdmin(admin.ModelAdmin): """Администрирование авторов.""" list_display = ['id', 'name', 'slug'] list_display_links = ['id', 'name'] search_fields = ['name'] prepopulated_fields = {'slug': ('name',)} readonly_fields = ['id'] @admin.register(Comic) class ComicAdmin(SimpleHistoryAdmin, ImportExportModelAdmin): """Администрирование комиксов.""" list_display = ['id', 'title', 'display_authors', 'status', 'views', 'created_at', 'cover_preview'] list_display_links = ['id', 'title'] list_filter = ['status', 'genres', 'created_at'] search_fields = ['title', 'authors__name'] readonly_fields = ['views', 'created_at', 'updated_at', 'cover_preview'] date_hierarchy = 'created_at' filter_horizontal = ['authors', 'genres'] inlines = [ChapterInline] prepopulated_fields = {'slug': ('title',)} actions = ['export_selected_pdf'] resource_class = ComicResource @admin.display(description='Обложка') def cover_preview(self, obj: Comic) -> str: """ Возвращает HTML-код для предпросмотра обложки. Args: obj: Объект комикса. Returns: str: HTML-код с изображением обложки или текст '(нет)'. """ if obj.cover: return format_html('<img src="{}" style="max-height: 50px;" />', obj.cover.url) return '(нет)' @admin.display(description='Авторы') def display_authors(self, obj: Comic) -> str: """ Возвращает список авторов комикса в виде строки. Args: obj: Объект комикса. Returns: str: Имена авторов через запятую. """ return ', '.join([a.name for a in obj.authors.all()]) def export_selected_pdf(self, request: HttpRequest, queryset) -> HttpResponse: """ Экспортирует выбранные комиксы в PDF-файл. Args: request: HTTP-запрос. queryset: Выбранные объекты комиксов. Returns: HttpResponse: PDF-файл. """ queryset = queryset.prefetch_related('authors', 'genres', 'chapters') context = {'comics': queryset} html_string = render_to_string('admin/comic_pdf.html', context) pdf_file = HTML(string=html_string).write_pdf() response = HttpResponse(pdf_file, content_type='application/pdf') response['Content-Disposition'] = 'attachment; filename="selected_comics.pdf"' return response export_selected_pdf.short_description = "Скачать PDF с информацией о комиксах" @admin.register(Chapter) class ChapterAdmin(SimpleHistoryAdmin, ImportExportModelAdmin): """Администрирование глав.""" list_display = ['id', 'comic', 'number', 'title', 'views', 'published_at'] list_display_links = ['id', 'title', 'comic'] list_filter = ['comic', 'published_at'] search_fields = ['title', 'comic__title'] readonly_fields = ['views'] date_hierarchy = 'published_at' autocomplete_fields = ['comic'] resource_class = ChapterResource @admin.register(Rating) class RatingAdmin(admin.ModelAdmin): """Администрирование оценок.""" list_display = ['id', 'user', 'comic', 'score', 'rated_at'] list_display_links = ['id', 'user'] list_filter = ['score', 'rated_at', 'comic'] search_fields = ['user__username', 'comic__title'] autocomplete_fields = ['user', 'comic'] readonly_fields = ['rated_at'] @admin.register(Comment) class CommentAdmin(SimpleHistoryAdmin): """Администрирование комментариев.""" list_display = ['id', 'user', 'comic', 'text_preview', 'created_at', 'is_approved'] list_display_links = ['id', 'user'] list_filter = ['is_approved', 'created_at', 'comic'] search_fields = ['user__username', 'comic__title', 'text'] autocomplete_fields = ['user', 'comic', 'parent'] readonly_fields = ['created_at'] date_hierarchy = 'created_at' actions = ['approve_comments', 'apply_censorship', 'delete_selected_comments'] @admin.display(description='Текст (кратко)') def text_preview(self, obj: Comment) -> str: """ Возвращает первые 50 символов комментария. Args: obj: Объект комментария. Returns: str: Сокращённый текст комментария. """ return obj.text[:50] + '…' if len(obj.text) > 50 else obj.text def approve_comments(self, request: HttpRequest, queryset) -> None: """ Одобряет выбранные комментарии. Args: request: HTTP-запрос. queryset: Выбранные объекты комментариев. """ queryset.update(is_approved=True) approve_comments.short_description = 'Одобрить выбранные комментарии' def apply_censorship(self, request: HttpRequest, queryset) -> None: """ Применяет цензуру к выбранным комментариям. Args: request: HTTP-запрос. queryset: Выбранные объекты комментариев. """ for comment in queryset: comment.save() self.message_user(request, f'Цензура применена к {queryset.count()} комментариям.') apply_censorship.short_description = 'Применить цензуру (пересохранить)' def get_urls(self) -> List: """ Добавляет кастомный URL для применения цензуры ко всем комментариям. Returns: List: Список URL-паттернов. """ urls = super().get_urls() my_urls = [ path('apply-censorship-all/', self.admin_site.admin_view(self.apply_censorship_all), name='apply_censorship_all'), ] return my_urls + urls def apply_censorship_all(self, request: HttpRequest) -> HttpResponseRedirect: """ Применяет цензуру ко всем комментариям. Args: request: HTTP-запрос. Returns: HttpResponseRedirect: Перенаправление на список комментариев. """ comments = Comment.objects.all() total = comments.count() for comment in comments: comment.save() self.message_user(request, f'Цензура применена ко всем {total} комментариям.', messages.SUCCESS) return redirect('admin:comics_comment_changelist') def delete_selected_comments(self, request: HttpRequest, queryset) -> None: """ Удаляет выбранные комментарии. Args: request: HTTP-запрос. queryset: Выбранные объекты комментариев. """ count = queryset.count() deleted, _ = queryset.delete() self.message_user(request, f'Удалено {deleted} комментариев.') delete_selected_comments.short_description = 'Удалить выбранные комментарии(демо)' class ProfileInline(admin.StackedInline): """Инлайн-редактор профиля на странице пользователя.""" model = Profile can_delete = False verbose_name_plural = 'Профиль' fields = ['avatar', 'bio', 'role'] class CustomUserAdmin(UserAdmin): """Расширенный администратор пользователей с профилем.""" inlines = [ProfileInline] list_display = ['username', 'email', 'first_name', 'last_name', 'is_staff', 'date_joined'] list_filter = ['is_staff', 'is_active', 'date_joined'] search_fields = ['username', 'email'] readonly_fields = ['date_joined', 'last_login'] admin.site.unregister(User) admin.site.register(User, CustomUserAdmin) @admin.register(Favorite) class FavoriteAdmin(admin.ModelAdmin): """Администрирование закладок.""" list_display = ['id', 'user', 'comic', 'status', 'added_at'] list_filter = ['status', 'added_at', 'comic'] search_fields = ['user__username', 'comic__title'] raw_id_fields = ['user', 'comic'] readonly_fields = ['added_at'] @admin.register(Appeal) class AppealAdmin(admin.ModelAdmin): """Администрирование обращений.""" list_display = ['id', 'name', 'email', 'message_preview', 'created_at'] list_display_links = ['id', 'name'] list_filter = ['created_at'] search_fields = ['name', 'email', 'message'] readonly_fields = ['created_at'] def message_preview(self, obj: Appeal) -> str: """ Возвращает первые 50 символов сообщения. Args: obj: Объект обращения. """ return obj.message[:50] + '…' if len(obj.message) > 50 else obj.message message_preview.short_description = 'Сообщение (кратко)' @admin.register(AppealFile) class AppealFileAdmin(admin.ModelAdmin): """Администрирование файлов обращений.""" list_display = ['id', 'appeal', 'file'] list_display_links = ['id', 'appeal'] list_filter = ['appeal']