from django import forms from django.contrib import admin from django.utils.html import format_html from import_export.admin import ImportExportModelAdmin from article.models import NoticeReceipt, Notice, Article, Carousel, ArticleAttachment from base.models import MainMenu from users.models import User # —————— 板块 —————— # @admin.register(ArticleSection) # class SectionAdmin(admin.ModelAdmin): # list_display = ('display_name', 'code', 'parent', 'is_enabled', 'order') # list_filter = ('parent', 'enabled') # search_fields = ('name', 'code') # ordering = ('order', 'name') # list_editable = ('order',) # fieldsets = ( # ('基本信息', { # 'fields': ('name', 'code', 'parent', 'enabled', 'order') # }), # ) # # def display_name(self, obj): # """在列表中显示层级缩进""" # if obj.is_sub: # return f"  └─ {obj.name}" # 使用全角空格+符号模拟缩进 # return obj.name # # display_name.short_description = '板块名称' # display_name.admin_order_field = 'name' # # def is_enabled(self, obj): # return obj.enabled # # is_enabled.boolean = True # is_enabled.short_description = '启用' # # def formfield_for_foreignkey(self, db_field, request, **kwargs): # """限制 parent 只能选择主板块(无父级的板块)""" # if db_field.name == "parent": # kwargs["queryset"] = ArticleSection.objects.filter(parent__isnull=True) # return super().formfield_for_foreignkey(db_field, request, **kwargs) # # def get_queryset(self, request): # """优化查询,避免 N+1""" # return super().get_queryset(request).select_related('parent') # # —————— 轮播图 —————— @admin.register(Carousel) class CarouselAdmin(ImportExportModelAdmin): list_display = ('title', 'image_thumb', 'link_url', 'order', 'active') list_editable = ('order', 'active') ordering = ('order',) def image_thumb(self, obj): if obj.image: return format_html('', obj.image.url) return "无图" image_thumb.short_description = "预览" # --- 附件内联配置 --- class ArticleAttachmentInline(admin.TabularInline): model = ArticleAttachment extra = 1 # 默认显示 1 个空白的附件上传框 verbose_name = "附件" verbose_name_plural = "附件列表" # 如果附件较多,可以限制每页显示数量 # max_num = 5 # 1. 定义自定义表单 class ArticleForm(forms.ModelForm): class Meta: model = Article fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 核心代码:过滤 section 字段,只保留有父级的分类(即二级分类) # 这里的 queryset 会直接作用于 Admin 的下拉选择框 self.fields['section'].queryset = MainMenu.objects.filter(parent__isnull=False, visible=True) # 可选:优化显示格式,让用户知道是哪个主板块下的子板块 # 例如显示:"[新闻中心] 国内新闻" self.fields['section'].label_from_instance = lambda obj: f"[{obj.parent.title}] {obj.title}" # —————— 文章 —————— @admin.register(Article) class ArticleAdmin(ImportExportModelAdmin): form = ArticleForm # <--- 关联自定义表单 list_display = ('title', 'section', 'author', 'is_published', 'created_at', 'attachment_count') list_filter = ('section', 'is_published', 'created_at') search_fields = ('title', 'content') date_hierarchy = 'created_at' autocomplete_fields = ('author',) # 需要 UserAdmin 支持搜索 # 核心配置:将附件内联注册到文章管理中 inlines = [ArticleAttachmentInline] fieldsets = ( ('基本信息', {'fields': ('title', 'section', 'cover')}), ('内容', {'fields': ('content',)}), ('发布设置', {'fields': ('is_published',)}), ) # 2. 定义显示附件数量的方法 def attachment_count(self, obj): # 使用 count() 方法统计数量 count = obj.attachments.count() if count == 0: return "-" # 没有附件显示横杠 return f"📎 {count}" # 有附件显示图标和数量 # 3. 设置该列在后台的标题 attachment_count.short_description = "附件" # —————— 通知(带签收) —————— @admin.register(Notice) class NoticeAdmin(ImportExportModelAdmin): list_display = ( 'title', 'publisher', 'require_sign', 'is_published', 'signed_count', 'total_target_users', 'created_at' ) list_filter = ('require_sign', 'is_published', 'created_at', 'target_departments') search_fields = ('title', 'content') filter_horizontal = ('target_departments',) readonly_fields = ('created_at', 'updated_at', 'publisher') date_hierarchy = 'created_at' fieldsets = ( ('基本信息', { 'fields': ('title', 'content', 'is_published') }), ('签收设置', { 'fields': ('require_sign', 'target_departments'), 'description': '勾选“是否需要签收”后,可指定接收部门。未指定则默认全员可见但不可签收。' }), ('时间信息', { 'fields': ('created_at', 'updated_at'), 'classes': ('collapse',) }), ) def save_model(self, request, obj, form, change): if not change: obj.publisher = request.user super().save_model(request, obj, form, change) def signed_count(self, obj): return obj.receipts.count() signed_count.short_description = "已签收人数" def total_target_users(self, obj): if obj.target_departments.exists(): count = User.objects.filter( dept__in=obj.target_departments.all(), is_active=True ).count() else: count = User.objects.filter(is_active=True).count() return count total_target_users.short_description = "应签收人数" # —————— 签收记录 —————— @admin.register(NoticeReceipt) class NoticeReceiptAdmin(ImportExportModelAdmin): list_display = ('notice', 'user', 'signed_at') list_filter = ('signed_at', 'notice__title') search_fields = ('user__cname', 'user__username', 'notice__title') readonly_fields = ('notice', 'user', 'signed_at') def has_add_permission(self, request): return False # 禁止手动添加