import json import os from io import StringIO from pathlib import Path from django.conf import settings from django.core.management import BaseCommand, CommandError, call_command class Command(BaseCommand): help = "生成供 Swagger UI 和 ReDoc 使用的静态 Swagger JSON" def add_arguments(self, parser): parser.add_argument( "--output", help="输出路径,默认使用 settings.SWAGGER_SCHEMA_PATH", ) parser.add_argument( "--url", help="文档中的 API 根地址,默认使用 settings.BASE_URL", ) def handle(self, *args, **options): target = Path(options["output"] or settings.SWAGGER_SCHEMA_PATH) if not target.is_absolute(): target = Path(settings.BASE_DIR) / target target = target.resolve() target.parent.mkdir(parents=True, exist_ok=True) temporary = target.with_name(f".{target.name}.{os.getpid()}.tmp") try: output = StringIO() call_command( "generate_swagger", "-", format="json", api_url=options["url"] or settings.BASE_URL, mock=True, verbosity=0, stdout=output, ) content = output.getvalue() schema = json.loads(content) if schema.get("swagger") != "2.0" or not schema.get("paths"): raise CommandError("生成的 Swagger 文档缺少版本或接口路径") content = json.dumps( schema, ensure_ascii=False, separators=(",", ":"), ) temporary.write_text(content, encoding="utf-8") os.replace(temporary, target) except Exception as exc: if isinstance(exc, CommandError): raise raise CommandError(f"生成 Swagger 文档失败:{exc}") from exc finally: temporary.unlink(missing_ok=True) operation_count = sum( method.lower() in {"get", "post", "put", "patch", "delete"} for path in schema["paths"].values() for method in path ) self.stdout.write( self.style.SUCCESS( f"Swagger文档已生成:{target} " f"({len(schema['paths'])}个路径,{operation_count}个操作)" ) )