"""
替换词 Admin 配置
Requirements: 6.1, 3.6
"""
from typing import Any, ClassVar
from django.contrib import admin
from django.db.models import QuerySet
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from apps.documents.models import Placeholder
def _get_placeholder_usage_service() -> Any:
from apps.documents.services.placeholders.placeholder_usage_service import PlaceholderUsageService
return PlaceholderUsageService()
def _get_code_placeholder_catalog_service() -> Any:
from apps.documents.services.code_placeholders.catalog_service import CodePlaceholderCatalogService
return CodePlaceholderCatalogService()
def _get_placeholder_admin_service() -> Any:
from ..services import PlaceholderAdminService
return PlaceholderAdminService()
class PlaceholderUsageFilter(admin.SimpleListFilter):
title = _("用途")
parameter_name: str = "usage"
def lookups(self, request: Any, model_admin: Any) -> Any:
return (
("contract", _("合同文件")),
("case", _("案件文件")),
("both", _("合同+案件")),
("unused", _("未使用")),
)
def queryset(self, request: Any, queryset: Any) -> Any:
value = self.value()
if not value:
return queryset
usage_map = getattr(self, "_usage_map_cache", None)
if usage_map is None:
usage_map = _get_placeholder_usage_service().get_usage_map()
self._usage_map_cache = usage_map
return _get_placeholder_admin_service().filter_by_usage(queryset, value, usage_map)
class PlaceholderAdmin(admin.ModelAdmin):
"""
替换词管理
提供替换词的 CRUD 操作,支持搜索和过滤.
"""
list_display: tuple[Any, ...] = (
"key",
"usage_display",
"example_value_display",
"is_active",
)
list_filter: tuple[Any, ...] = (
"is_active",
PlaceholderUsageFilter,
)
search_fields: tuple[Any, ...] = (
"key",
"display_name",
"description",
)
ordering: tuple[Any, ...] = ("key",)
fieldsets: tuple[Any, ...] = (
(None, {"fields": ("key", "display_name")}),
(_("示例和说明"), {"fields": ("example_value", "description")}),
(_("状态"), {"fields": ("is_active",)}),
)
actions: ClassVar = ["activate_placeholders", "deactivate_placeholders"]
def has_add_permission(self, request: Any) -> Any:
return False
def _catalog_cache(self) -> Any:
if not hasattr(self, "_cached_code_placeholder_catalog"):
catalog = _get_code_placeholder_catalog_service()
definitions = {d.key: d for d in catalog.list_definitions()}
self._cached_code_placeholder_catalog = definitions
return self._cached_code_placeholder_catalog
def _usage_map_cache(self, request: Any) -> Any:
if request is not None and getattr(request, "_placeholder_usage_map_cached", None) is not None:
return request._placeholder_usage_map_cached
usage_map = _get_placeholder_usage_service().get_usage_map()
if request is not None:
request._placeholder_usage_map_cached = usage_map
self._usage_map_for_changelist = usage_map
return usage_map
def _ensure_code_placeholders(self, request: Any) -> None:
if getattr(request, "_code_placeholders_synced", False):
return
definitions = self._catalog_cache()
service = _get_placeholder_admin_service()
service.ensure_code_placeholders(definitions)
request._code_placeholders_synced = True
def get_queryset(self, request: Any) -> QuerySet[Any]:
self._ensure_code_placeholders(request)
self._usage_map_cache(request)
qs = super().get_queryset(request)
code_keys = list(self._catalog_cache().keys())
service = _get_placeholder_admin_service()
return service.get_filtered_queryset(qs, code_keys)
@admin.display(description=_("用途"))
def usage_display(self, obj: Any) -> Any:
usage_map = getattr(self, "_usage_map_for_changelist", None)
if usage_map is None:
usage_map = _get_placeholder_usage_service().get_usage_map()
types = usage_map.get(obj.key, set()) or set()
parts: list[Any] = []
if "contract" in types:
parts.append(str(_("合同文件")))
if "case" in types:
parts.append(str(_("案件文件")))
if not parts:
return format_html('{}', "-")
return " / ".join(parts)
@admin.display(description=_("来源服务"))
def code_service_display(self, obj: Any) -> Any:
definition = self._catalog_cache().get(obj.key)
return definition.source if definition else ""
@admin.display(description=_("分类"))
def code_category_display(self, obj: Any) -> Any:
definition = self._catalog_cache().get(obj.key)
return definition.category if definition else ""
@admin.display(description=_("示例值"))
def example_value_display(self, obj: Any) -> Any:
"""显示示例值"""
if obj.example_value:
# 截断过长的示例值
value = obj.example_value
if len(value) > 50:
value = value[:50] + "..."
return format_html(
'{}', obj.example_value, value
)
return format_html('{}', "-")
@admin.action(description=_("启用选中的替换词"))
def activate_placeholders(self, request: Any, queryset: Any) -> None:
"""批量启用替换词"""
updated = queryset.filter(is_active=False).update(is_active=True)
self.message_user(request, _("已启用 %(count)d 个替换词") % {"count": updated})
@admin.action(description=_("禁用选中的替换词"))
def deactivate_placeholders(self, request: Any, queryset: Any) -> None:
"""批量禁用替换词"""
updated = queryset.filter(is_active=True).update(is_active=False)
self.message_user(request, _("已禁用 %(count)d 个替换词") % {"count": updated})
@admin.action(description=_("复制选中的替换词"))
def duplicate_placeholders(self, request: Any, queryset: QuerySet[Any]) -> None:
"""复制替换词"""
service = _get_placeholder_admin_service()
count: int = 0
for placeholder in queryset:
service.duplicate_placeholder(placeholder)
count += 1
self.message_user(request, _("已复制 %(count)d 个替换词") % {"count": count})
def get_readonly_fields(self, request: Any, obj: Any = None) -> Any:
"""编辑时 key 字段只读"""
if obj: # 编辑现有对象
return ("key",)
return ()