openedx.core.djangoapps.enrollments.v2 package#
Submodules#
openedx.core.djangoapps.enrollments.v2.forms module#
Forms for validating user input to the Course Enrollment v2 views.
ADR 0033 (OEP-68 parameter naming standardization) — accepts both the
preferred parameter names (course_key, course_keys) and the legacy
aliases (course_id, course_ids). When both are present, the
preferred name wins. Use legacy_param_aliases_used() from the view
layer to emit the ADR 0033 Deprecation HTTP header when a legacy alias
was sent.
Internally the cleaned_data continues to expose course_id /
course_ids (the names the queryset code reads) — the form coalesces
the preferred values onto those fields before the rest of validation runs.
- class openedx.core.djangoapps.enrollments.v2.forms.EnrollmentsAdminListForm(query_params, *args, **kwargs)#
Bases:
FormValidates the query string parameters for the v2 admin enrollments list endpoint (
GET /api/enrollment/v2/enrollments/).- MAX_INPUT_COUNT = 100#
- base_fields = {'course_id': <django.forms.fields.CharField object>, 'course_ids': <django.forms.fields.CharField object>, 'course_key': <django.forms.fields.CharField object>, 'course_keys': <django.forms.fields.CharField object>, 'email': <django.forms.fields.CharField object>, 'username': <django.forms.fields.CharField object>}#
- clean_course_id()#
Parse and validate the
course_id(or aliasedcourse_key) parameter.
- clean_course_ids()#
Split the
course_idsCSV (or aliasedcourse_keys) and enforce MAX_INPUT_COUNT.
- clean_email()#
Split the
emailCSV and enforce MAX_INPUT_COUNT.
- clean_username()#
Split the
usernameCSV, validate each entry, and enforce MAX_INPUT_COUNT.
- declared_fields = {'course_id': <django.forms.fields.CharField object>, 'course_ids': <django.forms.fields.CharField object>, 'course_key': <django.forms.fields.CharField object>, 'course_keys': <django.forms.fields.CharField object>, 'email': <django.forms.fields.CharField object>, 'username': <django.forms.fields.CharField object>}#
- legacy_param_aliases_used()#
Return the list of legacy parameter names that were actually present in the request, in declaration order. The view layer uses this to emit the ADR 0033
Deprecationheader.
- property media#
Return all media required to render the widgets on this form.
openedx.core.djangoapps.enrollments.v2.paginators module#
Pagination for the Enrollment API — v2.
ADR 0032 — uses DefaultPagination from
edx-rest-framework-extensions, which provides the standard 7-field
envelope: count, num_pages, current_page, start, next,
previous, results.
Distinct from v1’s openedx.core.djangoapps.enrollments.paginators.CourseEnrollmentsApiListPagination
(which is a CursorPagination subclass with a 3-field envelope).
v2 introduces the new shape — clients that need the legacy shape stay on
/api/enrollment/v1/ until they migrate.
- class openedx.core.djangoapps.enrollments.v2.paginators.EnrollmentsAdminListPagination#
Bases:
DefaultPaginationADR 0032 — standard pagination for the admin enrollments list API (GET /api/enrollment/v2/enrollments/).
Defaults sized for an admin-facing bulk-query endpoint: page_size 100, max 100.
- max_page_size = 100#
- page_size = 100#
- page_size_query_param = 'page_size'#
openedx.core.djangoapps.enrollments.v2.serializers module#
Serializers for the Enrollment API — v2.
Only contains the serializers introduced by ADR 0025 (replacing inline
dict construction in role-listing endpoints). The other v1 serializers
(CourseEnrollmentSerializer, CourseSerializer,
CourseEnrollmentAllowedSerializer, CourseEnrollmentsApiListSerializer)
are unchanged in shape between v1 and v2 — v2 view code imports them
directly from openedx.core.djangoapps.enrollments.serializers.
If a future v3 needs to break any of those response shapes, fork them into a new v3/serializers.py at that time.
- class openedx.core.djangoapps.enrollments.v2.serializers.UserRoleSerializer(*args, **kwargs)#
Bases:
SerializerSerializes a single course-level role entry for a user (ADR 0025).
- get_course_id(obj)#
Return course_id as a string.
- class openedx.core.djangoapps.enrollments.v2.serializers.UserRolesResponseSerializer(*args, **kwargs)#
Bases:
SerializerSerializes the full response payload for UserRolesViewSet (ADR 0025).
openedx.core.djangoapps.enrollments.v2.urls module#
URLs for the Enrollment API — v2.
Mounted at /api/enrollment/v2/ (see lms/urls.py).
ADR 0028 — EnrollmentViewSet is registered via DefaultRouter
(actions: list, create, unenroll, allowed). The other v2
endpoints (singleton retrieve by URL form, roles, course-detail-by-id,
admin enrollments list) cannot be expressed as router-generated URLs, so
they remain as standalone APIView classes routed via path() /
re_path().
URL surface#
- Router-generated (basename
enrollment): GET /enrollment/ POST /enrollment/ POST /enrollment/unenroll/ GET /enrollment/enrollment_allowed/ POST /enrollment/enrollment_allowed/ DELETE /enrollment/enrollment_allowed/
- Explicit paths:
GET /enrollment/{username},{course_key} (name: enrollment-v2-retrieve) GET /enrollment/{course_key} (name: enrollment-v2-retrieve) GET /enrollments/ (name: enrollment-v2-admin-list) GET /course/{course_key} (name: enrollment-v2-course-detail) GET /roles/ (name: enrollment-v2-roles)
openedx.core.djangoapps.enrollments.v2.view_services module#
Shared service layer for enrollment v2 HTTP operations.
ADR 0031 (Merge Similar Endpoints) — consolidates the business logic
behind the three v2 viewset actions that previously had partially duplicated
implementations in v1’s EnrollmentListView / UnenrollmentView /
EnrollmentAllowedView.
- class openedx.core.djangoapps.enrollments.v2.view_services.EnrollmentOperationsService#
Bases:
objectOperation handlers for the v2 EnrollmentViewSet.
All methods raise DRF exceptions on error paths so the viewset’s
StandardizedErrorMixincan produce the ADR 0029 envelope.- create_allowed_enrollment(serializer)#
Persist the allowed-enrollment described by
serializer.- Raises:
Conflict – if a row already exists for the (email, course_id) pair.
- create_or_update_enrollment(request, has_api_key, course_id)#
Handle the POST /enrollment/ create-or-update flow.
course_idis a parsedCourseKey. The viewset is responsible for the up-frontInvalidKeyError → ValidationErrortranslation before calling this method.Returns the enrollment dict on success. Raises DRF exceptions on any error path.
- delete_allowed_enrollment(email, course_id)#
Delete the allowed-enrollment row identified by (email, course_id).
- Raises:
NotFound – if no such row exists.
- list_allowed_for_email(email)#
Return the
CourseEnrollmentAllowedqueryset foremail.
- list_enrollments_for_user(request_user, target_username, has_api_key)#
Return enrollments visible to
request_userfortarget_username.Self / global staff / api-key requests → full list.
Otherwise filtered to courses
request_userstaffs.
- unenroll_user_for_retirement(username)#
Handle the retirement-pipeline /enrollment/unenroll/ flow.
- Returns:
Noneif the user has no active enrollments (caller should return 204 No Content).A dict (the unenroll-result payload) on success (caller returns 200).
- Raises:
ValidationError – if
usernameis missing.NotFound – if no retirement-status row exists for the user.
APIException – on any other unexpected error (mapped to 500).
openedx.core.djangoapps.enrollments.v2.views module#
API Views for the Enrollment API — v2.
This module is the v2 incarnation of the v1 enrollment views, restructured to apply the FC-0118 ADRs from the start:
ADR 0025 –
serializer_classon every viewset/viewADR 0026 – explicit
authentication_classes+permission_classesADR 0034 – auth standardization (OEP-0042). All four v2 viewsets/views use
(JwtAuthentication, EnrollmentCrossDomainSessionAuth);BearerAuthenticationAllowInactiveUserhas been removed per the deprecation policy.EnrollmentCrossDomainSessionAuthis retained (rather than relying on the platform-defaultSessionAuthentication) because these endpoints must accept cross-domain Studio/LMS CSRF-validated session cookies.ADR 0027 –
drf_spectacularfor OpenAPI schema generationADR 0028 – consolidated into
ViewSetclasses registered viaDefaultRouterwhere the URL shape allows itADR 0029 – standardized error envelope via
StandardizedErrorMixinADR 0031 – business logic centralized in
EnrollmentOperationsService(v2.view_services)ADR 0032 –
DefaultPagination7-field envelope on list endpointsADR 0033 – OEP-68 parameter naming (
course_keypreferred,course_idas deprecated alias) plus standardorderingwhitelistADR 0036 –
?view=minimalon the enrollmentlistand singletonretrieveactions. By default each enrollment record embeds the fullcourse_detailssub-object (which itself includes acourse_modeslist and other heavy fields). When?view=minimalis requested, the embedded sub-object is flattened to a singlecourse_idstring so callers that only need to know which courses a user is enrolled in (AI agents, sync pipelines) can skip the per-row sub-object payload.
Existing v1 endpoints at /api/enrollment/v1/ are unchanged — v2 is a
parallel new version mounted at /api/enrollment/v2/.
- class openedx.core.djangoapps.enrollments.v2.views.CourseEnrollmentDetailView(**kwargs)#
Bases:
StandardizedErrorMixin,APIViewGet enrollment information about a particular course.
- authentication_classes = ()#
- get(request, course_id=None)#
Return enrollment-related details for the specified course.
Public (no authentication required). The response includes the course schedule and supported enrollment modes; pass
?include_expired=1to include expired enrollment modes.
- permission_classes = ()#
- schema#
- serializer_class#
alias of
CourseSerializer
- throttle_classes = (<class 'openedx.core.djangoapps.enrollments.views.EnrollmentUserThrottle'>,)#
- class openedx.core.djangoapps.enrollments.v2.views.EnrollmentRetrieveView(**kwargs)#
Bases:
StandardizedErrorMixin,ApiKeyPermissionMixIn,APIViewGET enrollment for a course (and optionally a named user).
- authentication_classes = (<class 'edx_rest_framework_extensions.auth.jwt.authentication.JwtAuthentication'>, <class 'openedx.core.djangoapps.enrollments.views.EnrollmentCrossDomainSessionAuth'>)#
- get(request, course_id=None, username=None)#
Return the enrollment for
(username, course_id).When
usernameis omitted (theGET /enrollment/{course_id}URL form), the request user is used. Non-staff callers may only look up their own enrollment; any cross-user lookup withouthas_api_keyor staff privileges raisesNotFound(so the caller cannot probe for the existence of other users’ enrollments).
- permission_classes = (<class 'openedx.core.lib.api.permissions.ApiKeyHeaderPermissionIsAuthenticated'>,)#
- schema#
- serializer_class#
alias of
CourseEnrollmentSerializer
- throttle_classes = (<class 'openedx.core.djangoapps.enrollments.views.EnrollmentUserThrottle'>,)#
- class openedx.core.djangoapps.enrollments.v2.views.EnrollmentViewSet(**kwargs)#
Bases:
StandardizedErrorMixin,ViewSet,ApiKeyPermissionMixInCanonical ViewSet for the v2 Enrollment API.
Consolidates the v1
EnrollmentListView+UnenrollmentView+EnrollmentAllowedViewinto a single router-registered ViewSet (ADR 0028). Per-action permissions are declared via the@actiondecorator’spermission_classeskwarg.Router URLs (registered at
basename="enrollment"):GET /api/enrollment/v2/enrollment/ → list POST /api/enrollment/v2/enrollment/ → create POST /api/enrollment/v2/enrollment/unenroll/ → unenroll GET /api/enrollment/v2/enrollment/enrollment_allowed/ → allowed (GET) POST /api/enrollment/v2/enrollment/enrollment_allowed/ → allowed (POST) DELETE /api/enrollment/v2/enrollment/enrollment_allowed/ → allowed (DELETE)
- allowed(request)#
Retrieve, create, or delete CourseEnrollmentAllowed records. Admin-only.
- authentication_classes = (<class 'edx_rest_framework_extensions.auth.jwt.authentication.JwtAuthentication'>, <class 'openedx.core.djangoapps.enrollments.views.EnrollmentCrossDomainSessionAuth'>)#
- basename = None#
- check_throttles(request)#
Check if request should be throttled. Raises an appropriate exception if the request is throttled.
- create(request)#
Enroll a user in a course (or update an existing enrollment).
- description = None#
- detail = None#
- get_serializer(*args, **kwargs)#
- get_serializer_class()#
- list(request)#
List enrollments for the currently logged-in user (paginated).
ADR 0036 — when
?view=minimalis supplied, each enrollment’s embeddedcourse_detailssub-object is collapsed to a singlecourse_idstring;course_modesand the other heavy course-detail fields are dropped. Default response shape is unchanged for backwards compatibility.
- name = None#
- pagination_class#
alias of
DefaultPagination
- permission_classes = (<class 'openedx.core.lib.api.permissions.ApiKeyHeaderPermissionIsAuthenticated'>,)#
- schema#
- serializer_class#
alias of
CourseEnrollmentSerializer
- suffix = None#
- throttle_classes = (<class 'openedx.core.djangoapps.enrollments.views.EnrollmentUserThrottle'>,)#
- unenroll(request)#
Unenroll the specified user from all courses (retirement pipeline).
- class openedx.core.djangoapps.enrollments.v2.views.EnrollmentsAdminListView(**kwargs)#
Bases:
StandardizedErrorMixin,ListAPIViewAdmin-only paginated enrollment list with OEP-68 filter aliases.
- ALLOWED_ORDERING_FIELDS = frozenset({'-created', '-id', 'created', 'id'})#
- authentication_classes = (<class 'edx_rest_framework_extensions.auth.jwt.authentication.JwtAuthentication'>, <class 'openedx.core.djangoapps.enrollments.views.EnrollmentCrossDomainSessionAuth'>)#
- get_queryset()#
Get the list of items for this view. This must be an iterable, and may be a queryset. Defaults to using self.queryset.
This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.
You may want to override this if you need to provide different querysets depending on the incoming request.
(Eg. return a list of items that is specific to the user)
- list(request, *args, **kwargs)#
Override to emit the ADR 0033 Deprecation header when legacy params used.
- pagination_class#
alias of
EnrollmentsAdminListPagination
- permission_classes = (<class 'rest_framework.permissions.IsAdminUser'>,)#
- schema#
- serializer_class#
alias of
CourseEnrollmentsApiListSerializer
- throttle_classes = (<class 'openedx.core.djangoapps.enrollments.views.EnrollmentUserThrottle'>,)#
- class openedx.core.djangoapps.enrollments.v2.views.UserRolesView(**kwargs)#
Bases:
StandardizedErrorMixin,APIViewList the current user’s course-level roles.
- authentication_classes = (<class 'edx_rest_framework_extensions.auth.jwt.authentication.JwtAuthentication'>, <class 'openedx.core.djangoapps.enrollments.views.EnrollmentCrossDomainSessionAuth'>)#
- get(request)#
List the current user’s course-level roles.
Optionally filtered by
course_key(preferred, OEP-68) orcourse_id(deprecated alias). When both are present,course_keywins and the response carries the ADR 0033DeprecationHTTP header.
- permission_classes = (<class 'openedx.core.lib.api.permissions.ApiKeyHeaderPermissionIsAuthenticated'>,)#
- schema#
- serializer_class#
alias of
UserRolesResponseSerializer
- throttle_classes = (<class 'openedx.core.djangoapps.enrollments.views.EnrollmentUserThrottle'>,)#