/
Tikhonuk
/
veb
Обзор
Документация
Войти
/
Tikhonuk
/
veb
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
masterclass/admin.py
313 строк
10 KB
Tikhonuk
Финальная версия проекта. Завершение курсовой работы.
24 июн 2026, 06:19
24 июн 2026, 06:19
fc5eb31
Код
Авторство
О чём код?
import os import sys as _sys from django.contrib import admin from django.db import models as db_models from django.http import HttpResponse from django.template.loader import get_template from django.utils import timezone from django.utils.html import format_html from reportlab.lib.pagesizes import A4 from reportlab.lib.units import cm from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.pdfgen import canvas from .models import ( Booking, Category, Favorite, Image, MasterClass, Review, Role, Schedule, User, ) # Кросс-платформенная загрузка шрифтов: Windows → Arial, Linux/Docker → Liberation Sans _FONT_CANDIDATES = { "win32": [ ("Arial", "C:/Windows/Fonts/arial.ttf"), ("Arial-Bold", "C:/Windows/Fonts/arialbd.ttf"), ("Arial-Italic", "C:/Windows/Fonts/ariali.ttf"), ("Arial-BoldItalic", "C:/Windows/Fonts/arialbi.ttf"), ], "linux": [ ("Arial", "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf"), ("Arial-Bold", "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf"), ( "Arial-Italic", "/usr/share/fonts/truetype/liberation/LiberationSans-Italic.ttf", ), ( "Arial-BoldItalic", "/usr/share/fonts/truetype/liberation/LiberationSans-BoldItalic.ttf", ), ], } _PDF_FONT = "Helvetica" # fallback: встроенный PDF-шрифт _PDF_FONT_BOLD = "Helvetica-Bold" def _load_pdf_fonts() -> None: """Загружает шрифты для PDF-генерации, с fallback на Helvetica.""" global _PDF_FONT, _PDF_FONT_BOLD platform = "win32" if _sys.platform == "win32" else "linux" candidates = _FONT_CANDIDATES.get(platform, []) loaded = 0 for name, path in candidates: if os.path.exists(path): try: pdfmetrics.registerFont(TTFont(name, path)) loaded += 1 except Exception: pass if loaded >= 2: _PDF_FONT = "Arial" _PDF_FONT_BOLD = "Arial-Bold" _load_pdf_fonts() class ImageInline(admin.TabularInline): model = Image extra = 1 fields = ("get_image_preview", "image", "is_main", "sort_order") readonly_fields = ("get_image_preview",) @admin.display(description="Превью") def get_image_preview(self, obj): if obj.image: return format_html( '<img src="{}" width="100" height="80" style="object-fit: cover;" />', obj.image.url, ) return "—" class ScheduleInline(admin.TabularInline): model = Schedule extra = 1 fields = ("start_datetime", "end_datetime", "max_participants") @admin.register(Role) class RoleAdmin(admin.ModelAdmin): list_display = ("id", "name") list_display_links = ("name",) search_fields = ("name",) @admin.register(User) class UserAdmin(admin.ModelAdmin): list_display = ( "id", "email", "full_name", "phone", "get_avatar_preview", "role", "is_blocked", "created_at", ) list_display_links = ("email", "full_name") list_filter = ("role", "is_blocked", "is_active") search_fields = ("email", "full_name", "phone") readonly_fields = ("created_at", "get_avatar_preview") date_hierarchy = "created_at" filter_horizontal = ("groups", "user_permissions") fieldsets = ( ( "Основное", {"fields": ("email", "full_name", "phone", "avatar", "get_avatar_preview")}, ), ( "Права и статусы", {"fields": ("role", "is_blocked", "is_active", "is_staff", "is_superuser")}, ), ("Аудит", {"fields": ("created_at",), "classes": ("collapse",)}), ) @admin.display(description="Аватар") def get_avatar_preview(self, obj): if obj.avatar: return format_html( '<img src="{}" width="50" height="50" style="border-radius: 50%; object-fit: cover;" />', obj.avatar.url, ) return "—" @admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ("id", "name", "sort_order") list_display_links = ("name",) list_filter = ("sort_order",) search_fields = ("name",) ordering = ("sort_order", "name") @admin.register(MasterClass) class MasterClassAdmin(admin.ModelAdmin): list_display = ( "title", "creator", "price", "category", "is_moderated", "created_at", ) list_display_links = ("title",) list_filter = ("is_moderated", "category") search_fields = ("title", "description", "creator__email") readonly_fields = ("created_at",) raw_id_fields = ("creator", "category") date_hierarchy = "created_at" inlines = [ImageInline, ScheduleInline] actions = ["approve_masterclasses", "generate_pdf_report"] @admin.action(description="Одобрить выбранные мастер-классы") def approve_masterclasses(self, request, queryset): updated = queryset.update(is_moderated=True) self.message_user(request, f"Одобрено {updated} мастер-классов.") @admin.action(description="📄 Создать PDF-отчёт") def generate_pdf_report(self, request, queryset): """Генерация PDF-отчёта со шрифтом Arial""" response = HttpResponse(content_type="application/pdf") response["Content-Disposition"] = ( 'attachment; filename="masterclasses_report.pdf"' ) # Создаём PDF p = canvas.Canvas(response, pagesize=A4) width, height = A4 # Используем шрифты Arial (русские символы поддерживаются!) p.setFont(_PDF_FONT_BOLD, 16) p.drawString(2 * cm, height - 2 * cm, "Отчёт по мастер-классам") p.setFont(_PDF_FONT, 10) p.drawString( 2 * cm, height - 3 * cm, f"Дата: {timezone.now().strftime('%d.%m.%Y %H:%M:%S')}", ) total_count = queryset.count() total_price = queryset.aggregate(total=db_models.Sum("price"))["total"] or 0 p.drawString(2 * cm, height - 3.5 * cm, f"Всего мастер-классов: {total_count}") p.drawString(2 * cm, height - 4 * cm, f"Общая стоимость: {total_price} ₽") # Заголовки таблицы y = height - 6 * cm p.setFont(_PDF_FONT_BOLD, 10) p.drawString(1 * cm, y, "ID") p.drawString(2.5 * cm, y, "Название") p.drawString(8 * cm, y, "Создатель") p.drawString(14 * cm, y, "Цена") p.drawString(17 * cm, y, "Статус") p.setFont(_PDF_FONT, 9) y -= 0.6 * cm for mc in queryset: p.drawString(1 * cm, y, str(mc.id)) title = mc.title[:25] if len(mc.title) > 25 else mc.title p.drawString(2.5 * cm, y, title) email = ( mc.creator.email[:20] if len(mc.creator.email) > 20 else mc.creator.email ) p.drawString(8 * cm, y, email) p.drawString(14 * cm, y, f"{mc.price}") status = "Одобрен" if mc.is_moderated else "На модерации" p.drawString(17 * cm, y, status) y -= 0.6 * cm if y < 3 * cm: p.showPage() y = height - 2 * cm p.setFont(_PDF_FONT, 9) p.save() return response @admin.register(Image) class ImageAdmin(admin.ModelAdmin): list_display = ("id", "masterclass", "get_image_preview", "is_main", "sort_order") list_filter = ("is_main",) search_fields = ("masterclass__title",) raw_id_fields = ("masterclass",) readonly_fields = ("get_image_preview",) @admin.display(description="Превью") def get_image_preview(self, obj): if obj.image: return format_html( '<img src="{}" width="100" height="80" style="object-fit: cover;" />', obj.image.url, ) return "—" @admin.register(Schedule) class ScheduleAdmin(admin.ModelAdmin): list_display = ("masterclass", "start_datetime", "end_datetime", "max_participants") list_filter = ("start_datetime",) search_fields = ("masterclass__title",) raw_id_fields = ("masterclass",) date_hierarchy = "start_datetime" @admin.register(Booking) class BookingAdmin(admin.ModelAdmin): list_display = ("id", "user", "schedule", "status", "booked_at") list_filter = ("status", "booked_at") search_fields = ("user__email", "schedule__masterclass__title") raw_id_fields = ("user", "schedule") readonly_fields = ("booked_at",) date_hierarchy = "booked_at" @admin.register(Review) class ReviewAdmin(admin.ModelAdmin): list_display = ( "id", "user", "masterclass", "rating", "short_comment", "created_at", ) list_filter = ("rating", "created_at") search_fields = ("user__email", "masterclass__title", "comment") raw_id_fields = ("user", "masterclass") readonly_fields = ("created_at",) date_hierarchy = "created_at" def short_comment(self, obj): return obj.comment[:50] + "…" if len(obj.comment) > 50 else obj.comment short_comment.short_description = "Комментарий" @admin.register(Favorite) class FavoriteAdmin(admin.ModelAdmin): list_display = ("user", "masterclass", "added_at") list_filter = ("added_at",) search_fields = ("user__email", "masterclass__title") raw_id_fields = ("user", "masterclass") readonly_fields = ("added_at",) date_hierarchy = "added_at"