ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

在 Django 项目中集成 Scalar API Reference:基于 DRF 与 drf-spectacular 的交互式 API 文档实战

在 Django 项目中集成 Scalar API Reference:基于 DRF 与 drf-spectacular 的交互式 API 文档实战 在 Django 项目中集成 Scalar API Reference基于 DRF 与 drf-spectacular 的交互式 API 文档实战【免费下载链接】scalarScalar is an open-source API platform: Modern REST API Client Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar本篇技术指南讲解如何在 Django REST FrameworkDRF项目中通过drf-spectacular生成 OpenAPI 文档、配合 Scalar 的 CDN 独立构建standalone build渲染出一套美观的交互式 API Reference。读完后你将掌握在 Django 项目中搭建 Scalar 文档页的完整步骤含可复制的scalar应用三个源文件、如何用extend_schema把django-filter的过滤条件自动转换为 OpenAPI 查询参数以及 Scalar 前端是如何通过data-*属性自动挂载文档的底层原理。适用场景DRF 与 Django Ninja 的两条路线Scalar 仓库针对 Django 生态提供了两套集成方案选型时先看清前提DRF 项目本文主题如果你用 Django REST Framework 编写 API官方文档 documentation/integrations/django.md 给出的是一套社区贡献方案由drf_spectacular负责生成 OpenAPI 文档Scalar 只负责在前端渲染。没有现成的 PyPI 包需要在项目里自建一个轻量scalar应用。Django Ninja 项目仓库内包含一等公民集成 integrations/django-ninja/PyPI 包scalar-ninja核心实现在 scalar_ninja.py只需pip install scalar-ninja并把ScalarViewer传给NinjaAPI(docs...)即可。Ninja 用户请直接参考 Django Ninja 指南不要使用本文方案。本文聚焦前者DRF drf-spectacular django-filter 技术栈下如何从零接入 Scalar。前置条件需要三个组件共同工作组件角色rest_frameworkDjango 的 REST API 框架提供 ViewSet、序列化器等drf_spectacular将 DRF 视图/序列化器/过滤器转换为标准 OpenAPI 文档并提供SpectacularAPIView等视图django_filters可选声明式查询过滤若使用则可用get_filter_parameters自动生成文档参数第一步创建scalar应用在项目顶层目录新建一个名为scalar的文件夹放置三个文件。整个方案的核心就是这三个文件先完整给出代码再逐一拆解。文件一get_filter_parameters.py—— 从 FilterSet 自动生成 OpenAPI 参数该模块把django-filter的FilterSet类自动转换为 OpenAPI 查询参数列表避免手写每个过滤参数# scalar/get_filter_parameters.py from typing import Type, List from django_filters import FilterSet from drf_spectacular.utils import OpenApiParameter from django_filters.filters import ( CharFilter, NumberFilter, DateFilter, BooleanFilter, ChoiceFilter, ModelChoiceFilter, ) from rest_framework.fields import DecimalField def get_filter_parameters(filter_class: Type[FilterSet]) - List[OpenApiParameter]: Automatically generate OpenAPI parameters from a FilterSet class. Args: filter_class: The FilterSet class to generate parameters from Returns: List of OpenApiParameter objects parameters [] for field_name, filter_instance in filter_class().filters.items(): parameter_type str # default type parameter_format None enum None # Determine parameter type based on filter type if isinstance(filter_instance, NumberFilter): parameter_type ( float if isinstance(filter_instance.field, DecimalField) else int ) elif isinstance(filter_instance, BooleanFilter): parameter_type bool elif isinstance(filter_instance, DateFilter): parameter_type str parameter_format date elif isinstance(filter_instance, ChoiceFilter): parameter_type str enum [choice[0] for choice in filter_instance.extra[choices]] elif isinstance(filter_instance, ModelChoiceFilter): parameter_type int description ( fID of related {filter_instance.field.queryset.model.__name__} ) # Get lookup expression for description lookup_expr getattr(filter_instance, lookup_expr, exact) # Build description if lookup_expr icontains: description fFilter by {field_name} (case-insensitive, partial match) elif lookup_expr gte: description fFilter by {field_name} (greater than or equal) elif lookup_expr lte: description fFilter by {field_name} (less than or equal) elif lookup_expr iexact: description fFilter by exact {field_name} (case-insensitive) else: description fFilter by {field_name} # Create parameter param OpenApiParameter( namefield_name, typeparameter_type, locationquery, descriptiondescription, requiredFalse, enumenum, ) parameters.append(param) return parameters逐段拆解其类型映射逻辑函数签名接收一个FilterSet类不是实例内部通过filter_class().filters实例化后遍历所有过滤器字段类型推断NumberFilter默认推断为int但当底层字段是 DRF 的DecimalField时改为floatBooleanFilter映射boolDateFilter映射str并附带format: dateChoiceFilter从filter_instance.extra[choices]提取枚举值列表写入enumModelChoiceFilter映射int关联模型主键描述生成读取过滤器的lookup_expr属性不存在时回退为exact对icontains、gte、lte、iexact等常见查找表达式生成语义化描述最终统一写入OpenApiParameter.description所有参数固定locationquery、requiredFalse因为查询过滤参数都是可选的。文件二scalar.py—— 文档视图与 URL 路由该文件定义了渲染 Scalar 文档页的视图和两条 URL 路由# scalar/scalar.py from django.http import HttpResponse from django.urls import path from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView def scalar_viewer(request): openapi_url /api/schema/ title Scalar Api Reference scalar_js_url https://cdn.jsdelivr.net/npm/scalar/api-reference scalar_proxy_url scalar_favicon_url /static/favicon.ico html f !DOCTYPE html html head title{title}/title meta charsetutf-8/ meta nameviewport contentwidthdevice-width, initial-scale1 link relshortcut icon href{scalar_favicon_url} style body {{ margin: 0; padding: 0; }} /style /head body noscript Scalar requires Javascript to function. Please enable it to browse the documentation. /noscript script idapi-reference >from .scalar import urlpatterns_scalar from .get_filter_parameters import get_filter_parameters __all__ [urlpatterns_scalar, get_filter_parameters]有了这个文件scalar目录就是一个可导入的 Python 包视图与 URL 配置都能从其他 app 直接引用。第二步在视图中注入过滤参数scalar应用就绪后在业务视图中通过extend_schema把自动生成的参数注入 OpenAPI 文档# products/views.py from rest_framework import viewsets from drf_spectacular.utils import extend_schema, OpenApiParameter from .models import Product from .serializers import ProductSerializer from .filters import ProductFilter from scalar.get_filter_parameters import get_filter_parameters extend_schema(tags[Products]) class ProductViewSet(viewsets.ModelViewSet): queryset Product.objects.all() serializer_class ProductSerializer filterset_class ProductFilter extend_schema( descriptionList all products, parametersget_filter_parameters(ProductFilter) ) def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs)要点类级extend_schema(tags[Products])决定该视图所有操作在 Scalar 侧边栏中的分组方法级extend_schema的parametersget_filter_parameters(ProductFilter)把ProductFilter中声明的每个过滤字段CharFilter、NumberFilter等逐一转换为查询参数最终显示在 Scalar 的 Test Request 请求面板中只覆写了list方法是因为过滤参数只对列表端点有意义retrieve、create等操作由 drf-spectacular 从序列化器自动推导。第三步挂载 URL 路由在项目根urls.py中把 Scalar 文档路由与业务路由拼在一起# myapi/urls.py from django.contrib import admin from django.urls import path, include from rest_framework.routers import DefaultRouter from scalar.scalar import urlpatterns_scalar from products.views import ProductViewSet router DefaultRouter() router.register(products, ProductViewSet) urlpatterns [ path(admin/, admin.site.urls), path(api/, include(router.urls)), ] urlpatterns_scalarurlpatterns_scalar是列表直接追加即可无需include()。此时完整路由为/api/products/业务 API、/api/schema/OpenAPI 文档、/api/docs/Scalar 文档页。运行与验证启动 Django 开发服务器后访问http://localhost:8000/api/docs/即可看到 Scalar 渲染的 API Reference 页面。验证清单侧边栏出现Products分组及其下列表、详情等操作点开list操作Test Request 面板中显示由ProductFilter自动生成的全部查询参数类型、枚举、描述与过滤器一一对应直接请求http://localhost:8000/api/schema/可拿到原始 OpenAPI JSON这是 Scalar 渲染的数据源。原理深潜一段script标签是如何挂起整个文档的scalar.py中的 HTML 模板刻意写得极简——既没有挂载容器div也没有任何初始化 JS。这一写法并非侥幸而是 Scalar standalone 构建的正式约定仓库源码可以完整印证这条链路。1. 属性解析。CDN 脚本加载后会执行 html-api.ts 中的getConfigurationFromDataAttributes它首先查找document.getElementById(api-reference)对应模板中idapi-reference的 script 标签读取其data-url属性作为 OpenAPI 文档地址并读取data-proxy-url作为proxyUrl配置// script idapi-reference>// If its a script tag, we cant mount the Vue.js app inside that tag. // We need to add a new container element before the script tag. export const createContainer (doc: Document, element?: Element | null) { let _container: Element | null null const specScriptTag getSpecScriptTag(doc) if (specScriptTag) { _container doc.createElement(div) specScriptTag?.parentNode?.insertBefore(_container, specScriptTag) } ... }随后createApiReference在该容器上createApp并挂载整个ApiReferenceVue 组件同时根据配置的darkMode为body添加dark-mode/light-mode类名html-api.ts。相关行为有完整的单元测试覆盖见 html-api.test.ts。3. 更多配置入口。data-configuration属性可以承载完整的 JSON 配置用于超出data-url/data-proxy-url的定制需求如darkMode、theme、sidebar等。解析逻辑在 html-api.tsconst configurationScriptElement doc.querySelector(#api-reference[data-configuration]) ... return { _integration: html, ...JSON.parse(configurationFromElement.split(quot;).join()), }因此如果想在 Django 文档页强制暗色模式可在模板的 script 标签上追加data-configuration{quot;darkModequot;: true}。完整的可配置项语义可参考仓库的 通用 HTML/JS 集成文档 与 配置文档。4. 样式隔离细节。standalone 构建会把全部 CSS 注入head中一个style标签源码中的retainStandaloneStyles/releaseStandaloneStyles以 document 为键做引用计数避免 SPA 式导航Turbo Drive、htmx boost 等时样式残留html-api.ts。对 Django 这类传统多页应用无感知但它说明该构建方式是为多种宿主环境设计的。实践注意事项必须启用 JavaScript模板中已内置noscript提示。Scalar 是纯前端渲染/api/schema/提供数据、/api/docs/只提供空壳 HTML。CORSdata-proxy-url留空时OpenAPI 请求与文档页同源本例均为/api/...无跨域问题若openapi_url指向外域需配置scalar_proxy_url或使用 Scalar 提供的代理。版本锁定CDN 地址不带版本号时始终取scalar/api-reference最新版行为可能随上游更新需要稳定版本时给 URL 追加版本号写法见 html-js.md 的 Version 一节。代码细节提醒get_filter_parameters中ModelChoiceFilter分支先按关联模型名生成描述但若该过滤器没有显式lookup_expr后续else分支会用Filter by {field_name}覆盖该描述如需更精确的关联模型说明可在自己的项目副本中对ModelChoiceFilter分支补上lookup_expr判断这是社区方案的细节按项目需要自行调整。选型回查如果新项目可以直接选 Django Ninja 而非 DRF官方集成 integrations/django-ninja/ 提供了带类型的ScalarConfig布局、主题、搜索热键、多文档 sources 等数十项配置维护成本更低本文方案的价值则在于让存量 DRF drf-spectacular 项目以最少代码获得同一套 Scalar 文档体验。小结整套 DRF 方案的分工非常清晰drf_spectacular负责文档从哪来/api/schema/社区scalar应用负责文档怎么显示/api/docs/ CDN standalone 构建get_filter_parameters负责补齐 drf-spectacular 不会自动生成的过滤器参数。理解 Scalar 通过data-url/data-configuration属性自动挂载的 HTML API 约定后这套模板还可以平滑移植到任何能返回一段 HTML 的后端框架中。【免费下载链接】scalarScalar is an open-source API platform: Modern REST API Client Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表