Feat: replace drf yasg with drf spectacular - #39108
Open
Faraz32123 wants to merge 12 commits into
Open
Faraz32123 wants to merge 12 commits into
Faraz32123 wants to merge 12 commits into
Conversation
Faraz32123
force-pushed
the
feat/replace_drf_yasg_with_drf_spectacular
branch
3 times, most recently
from
September 16, 2026 08:45
c232307 to
b9f6467
Compare
Faraz32123
marked this pull request as ready for review
September 16, 2026 10:32
Faraz32123
requested review from
Abdul-Muqadim-Arbisoft,
feanil and
taimoor-ahmed-1
September 16, 2026 10:32
feanil
reviewed
Sep 16, 2026
Faraz32123
marked this pull request as draft
September 17, 2026 14:33
Faraz32123
force-pushed
the
feat/replace_drf_yasg_with_drf_spectacular
branch
2 times, most recently
from
September 17, 2026 14:54
dedc96a to
4bf390f
Compare
Faraz32123
marked this pull request as ready for review
September 17, 2026 15:27
feanil
reviewed
Sep 21, 2026
Converts @swagger_auto_schema to @extend_schema in the five modules that import drf_yasg directly, following the drf-spectacular migration guide: https://drf-spectacular.readthedocs.io/en/latest/drf_yasg.html Structured openapi.Schema objects become inline_serializer so they get named components in the generated schema; untyped ones become OpenApiTypes.OBJECT. openapi.Parameter becomes OpenApiParameter. The other four drf_yasg users go through edx-api-doc-tools and will be migrated with it.
Converts @apidocs.schema to @extend_schema across openedx/core, replacing the parameter helpers with OpenApiParameter and string response descriptions with OpenApiResponse. bookmarks/serializers.py inlines is_schema_request, which has no drf-spectacular equivalent, and extends it to recognise drf-spectacular's swagger_fake_view alongside drf-yasg's format=openapi.
Converts @apidocs.schema and @Schema to @extend_schema across the lms app, excluding instructor. Parameter helpers become OpenApiParameter and string response descriptions become OpenApiResponse. discussion/rest_api/views.py also drops its remaining direct drf_yasg import, which was interleaved with the apidocs decorators.
Converts the 26 @apidocs.schema decorators in the instructor v1 and v2 APIs to @extend_schema. The course_id, problem, and exam_id path parameters were repeated verbatim across 29 decorators; those are now module-level constants.
Converts the remaining @apidocs.schema decorators across contentstore and modulestore_migrator to @extend_schema. The three class-level @apidocs.schema_for decorators become @extend_schema_view, splitting each docstring into summary and description as schema_for did. Files that already imported drf-spectacular for the FC-0118 work have their import lines merged rather than duplicated.
Six serializers subclass BaseSerializer, which has no `fields` attribute, so drf-spectacular raises AttributeError when generating a schema that covers them. Each extension declares the type its serializer produces. Registered from CommonInitializationConfig.ready() so they load in both services regardless of which schema is being generated.
Replaces make_docs_urls with SpectacularAPIView, SpectacularSwaggerView and SpectacularRedocView, preserving the swagger.json, swagger.yaml, api-docs/ and swagger/ routes and their URL names. The UI views reverse their schema URL without arguments, so api-docs/schema/ is registered alongside the format-suffixed routes. /api-docs serves the full API surface via custom_settings, leaving SPECTACULAR_SETTINGS to the narrower Authoring and Enrollment schemas the SDK consumes. Also removes drf_yasg from INSTALLED_APPS, drops SWAGGER_SETTINGS, and converts the docs security definitions to OpenAPI 3 form. `make swagger` now runs `manage.py lms spectacular`, since generate_swagger came from drf_yasg; docs_settings applies the same unfiltered configuration as /api-docs so the generated file still covers the whole surface. edx-api-doc-tools and drf-yasg remain installed as transitive dependencies of openedx-authz and django-user-tasks respectively.
- restore server-side caching on the OpenAPI schema endpoints, which edx-api-doc-tools provided via SchemaView.as_cached_view - point the api-docs test at /api-docs/schema/ so it exercises schema generation again, and add the CMS equivalent - serve Swagger UI and ReDoc assets from drf-spectacular-sidecar instead of drf-spectacular's unpinned jsdelivr CDN defaults - correct the /api-docs comment: edx-api-doc-tools was /api/-only, so this widens the documented surface rather than being "the opposite" - drop the LMS-only claim about API_ACCESS_MANAGER_EMAIL, which lives in openedx/envs/common.py and is shared by both services - point the schema_extensions docstring at CommonInitializationConfig, the actual registration site - remove the dead format=openapi branch from is_schema_request, since nothing in the platform serves drf-yasg any more - delete the now-empty Django Rest Framework banner in lms/envs/common.py
Faraz32123
force-pushed
the
feat/replace_drf_yasg_with_drf_spectacular
branch
from
September 22, 2026 12:40
4bf390f to
ed0c4e9
Compare
cache_page pickles the whole Response, and this schema is over memcached's 1MB item limit. CACHES['default'] has ignore_exc set, so the oversized set failed silently and Django dropped the key — the endpoint looked cached while regenerating on every request. cached_schema_view() stores the zlib-compressed body instead, keyed on the path and the negotiated representation, which brings it to about a tenth of the limit. add_never_cache_headers restores the no-store that drf-yasg sent, and a falsy OPENAPI_CACHE_TIMEOUT still goes straight to the view so devstack is unchanged.
feanil
reviewed
Sep 22, 2026
The cached branch stored anything that came back 200, so DRF's OPTIONS metadata document landed under the GET key and was served to the next GET. cache_page only ever stored GET and HEAD; do the same. drf-spectacular renders the document under whatever language is active, and LocaleMiddleware sets that from the request before the view runs, so one locale's document could be served to another's. Add the active language to the key. Adds a test to both suites for the two cases. The suite runs with OPENAPI_CACHE_TIMEOUT = 0 and a DummyCache, so it overrides both to reach the cached branch at all.
Faraz32123
force-pushed
the
feat/replace_drf_yasg_with_drf_spectacular
branch
from
September 23, 2026 10:25
7b27cd3 to
f7499b5
Compare
feanil
reviewed
Sep 28, 2026
feanil
left a comment
Contributor
There was a problem hiding this comment.
one last thing and then we're good to land this I think
The LMS and the CMS serve this view at the same path, under the same Accept and language, and share one cache by default, so both landed on the same key and whichever generated first served its document at the other's URL. cache_page avoided this by hashing build_absolute_uri(), host included. Add ROOT_URLCONF to the key.
feanil
reviewed
Sep 30, 2026
feanil
left a comment
Contributor
There was a problem hiding this comment.
One more test to add and then I think this is good to merge.
LMS and CMS serve /api-docs/schema/ at the same path and share one cache, so the key must include ROOT_URLCONF. The existing cache test runs under a single URLconf and could not detect that line being dropped; this test serves the view from a second URLconf and fails without it.
feanil
approved these changes
Oct 2, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR must be merged before the schema generation PR: #39025
Replace drf-yasg with drf-spectacular
Follow-up to the FC-0118 API standardization work. Migrates all platform API
documentation off
drf-yasgandedx-api-doc-toolsontodrf-spectacular,including the
/api-docssite itself.Slack Discussion thread, the goal was to drop both dependencies as part
of the drf-spectacular conversion, replacing the
edx-api-doc-tools/api-docsendpoint with the drf-spectacular equivalent.
What changed
~400 decorator call sites across 43 files, following the
drf-yasg migration guide:
@swagger_auto_schema/@apidocs.schema→@extend_schema@apidocs.schema_for→@extend_schema_viewapidocs.string_parameter/query_parameter/path_parameterandopenapi.Parameter→OpenApiParameter401: "Not authenticated.") →OpenApiResponseopenapi.Schemaobjects →inline_serializerwhere they had structure,OpenApiTypeswhere they didn't/api-docsnow served bySpectacularAPIView/SpectacularSwaggerView/SpectacularRedocViewCommits
Reviewable one at a time, each independently handled:
drf_yasgusers (5 files)openedx/core(10 files)lms, excluding instructor (4 files)instructor(2 files, ~120 sites)cms(22 files)BaseSerializersubclasses/api-docsswap and dependency removalThree things worth knowing
The dependencies don't actually disappear.
edx-api-doc-toolsis stillrequired by
openedx-authz, anddrf-yasgbydjango-user-tasks. Both remainin
base.txtas transitive dependencies. What this PR removes is the platform'sown dependency on them —
pyproject.toml,INSTALLED_APPS, and every import.Fully dropping them needs those upstream packages to migrate first.
Commit 6 exists because
/api-docsis unfiltered. Six serializers subclassBaseSerializer, which has nofieldsattribute, so drf-spectacular raisedAttributeErrorwhen generating a schema covering the whole surface. This neversurfaced before because the existing drf-spectacular schemas
(
/authoring-api/,/lms-api/) are filtered down to a handful of endpoints.Each serializer now has an
OpenApiSerializerExtensiondeclaring the type itactually produces.
/api-docsdocuments more than it used to.edx-api-doc-tools'ApiSchemaGeneratorkept only paths under/api/; drf-spectacular documentsevery DRF endpoint in the service — 633 LMS paths versus the 293 in the
committed
docs/lms-openapi.yaml. That file also becomes an OpenAPI 3 documentrather than Swagger 2.0, with untrimmed paths. Flagging it so the widening is a
recorded decision rather than a side effect.
Verification
Tested against a running Tutor instance:
/api-docs/api-docs/authoring-api/schema//lms-api/schema/The last two matter most:
/api-docsusescustom_settingsrather than theglobal
SPECTACULAR_SETTINGS, so the narrow SDK-facing schemas are untouched —same paths, same prefix trimming. The SDK's generated client is unaffected.
Swagger UI and ReDoc confirmed rendering on both services.
swagger.json,swagger.yaml,api-docs/andswagger/keep their existing paths and URLnames;
api-docs/schema/is new, because the Swagger and ReDoc views reversetheir schema URL without arguments.
Schema caching is preserved: with
OPENAPI_CACHE_TIMEOUTat its 1-hour default,the first request to
/api-docs/schema/took 5.69s and the second 0.08s.Notes for reviewers
instructor/views/api_v2.pyhad 91string responses and 46 parameters, so the repetitive conversions were
scripted. Spot-checking won't catch a systematic miss — I verified by
extracting every response and parameter from both versions and diffing the
sets. Same method used on commits 1–3 and 5 to confirm nothing was dropped.
docs/docs_settings.pysecurity definitions moved from Swagger 2.0(
type: basic) to OpenAPI 3 (type: http, scheme: basic). This is the onechange I couldn't exercise locally — it only takes effect in the Sphinx docs
build.
drf-spectacular-sidecar, so theystay self-hosted and version-pinned as the drf-yasg bundles were, rather than
drf-spectacular's default unpinned
@latestCDN URLs. This is a newdependency — a
uv syncor container rebuild is needed on this branch.serializer_class,unresolvable authenticators, two
CourseEnrollmentcomponents with clashingnames). They don't block generation and are out of scope here.