from pathlib import Path from django.conf import settings from django.core.management.base import BaseCommand, CommandError from django.db.models import Count class Command(BaseCommand): help = "Render a PrintingOrder into a .docx template using real DB data (pick the most 'full' order by default)." def add_arguments(self, parser): parser.add_argument( "--order-id", type=int, default=None, help="指定 PrintingOrder.id;不传则自动选择 PrintingJob 明细最多的一单", ) parser.add_argument( "--template", type=str, default=str(Path(settings.BASE_DIR) / "print-template.docx"), help="docx 模板路径(默认 BASE_DIR/print-template.docx)", ) parser.add_argument( "--out", type=str, default="", help="输出 docx 路径(默认 .tmp_docx/printing-order-{id}.docx)", ) parser.add_argument( "--printer", type=str, default="系统自动生成", help="填充 {打印人}", ) parser.add_argument( "--keep-unresolved", action="store_true", help="不清空未命中的占位符(默认会清空)", ) def handle(self, *args, **options): order_id = options.get("order_id") template = str(options.get("template") or "") out = str(options.get("out") or "") printer = str(options.get("printer") or "") keep_unresolved = bool(options.get("keep_unresolved")) tp = Path(template) if not tp.exists(): raise CommandError(f"模板文件不存在:{template}") if tp.suffix.lower() != ".docx": raise CommandError("template 必须是 .docx 文件") from printing.models import PrintingOrder qs = PrintingOrder.objects.all() if order_id: order = ( qs.select_related("customer", "merchant", "process") .prefetch_related("printing_jobs__product") .filter(id=int(order_id)) .first() ) if not order: raise CommandError(f"PrintingOrder 不存在:id={order_id}") else: order = ( qs.annotate(job_count=Count("printing_jobs")) .filter(job_count__gt=0) .order_by("-job_count", "-id") .select_related("customer", "merchant", "process") .prefetch_related("printing_jobs__product") .first() ) if not order: raise CommandError("未找到包含 printing_jobs 的 PrintingOrder(无法做“饱满”渲染测试)") # output path default if not out: out = str(Path(settings.BASE_DIR) / ".tmp_docx" / f"printing-order-{order.id}.docx") Path(out).parent.mkdir(parents=True, exist_ok=True) from printing.docx_template import extract_docx_placeholders, render_docx_template from printing.print_docx_services import build_debug_context_summary, build_printing_order_docx_context placeholders = extract_docx_placeholders(template_path=str(tp)) ctx = build_printing_order_docx_context(order=order, placeholders=placeholders, printer_label=printer) ctx_stats = build_debug_context_summary(ctx) stats = render_docx_template( template_path=str(tp), output_path=str(out), context=ctx, clear_unresolved=(not keep_unresolved), ) job_count = order.printing_jobs.count() self.stdout.write( self.style.SUCCESS( "渲染完成:" f" order_id={order.id}" f" job_count={job_count}" f" out={out}" f" paragraphs_touched={stats.paragraphs_touched}" f" placeholders_replaced_or_cleared={stats.placeholders_replaced_or_cleared}" f" context_filled={ctx_stats['context_filled_keys']}/{ctx_stats['context_total_keys']}" ) )