Backend Architecture Overview

Author: Zhenyu Yang yangzhenyu@sust.edu.cn

Last updated: Apr 24, 2026

This document describes the shared structure and cross-module constraints of the Django backend. For specific business rules, see the corresponding app pages.

Directory Structure

backend/
├── apps/
│   ├── accounts/
│   ├── classrooms/
│   ├── courses/
│   ├── signage/
│   ├── checkins/
│   ├── borrowings/
│   ├── repairs/
│   ├── abuse/
│   └── logs/
├── classroom_manager/
│   ├── settings/
│   └── urls.py
├── common/
├── services/
└── manage.py

Configuration & Runtime

  • The default settings module is classroom_manager.settings.base; manage.py, wsgi.py, and asgi.py all point directly to it.

  • The database defaults to MySQL; connection parameters come from MYSQL_* environment variables.

  • The default language is zh-hans; the default timezone is Asia/Shanghai.

  • REST Framework requires authentication by default; the default pagination class is common.pagination.DefaultPagination with a max page size of 200.

  • Login throttling uses LOGIN_THROTTLE_RATE, defaulting to 5/minute.

  • The upload directory defaults to LOCAL_UPLOAD_DIR=/data/uploads; the access prefix defaults to /media/uploads/.

Global Routing

  • Django admin entry: /django-admin/

  • Business API common prefix: /api/v1/

  • The root URL configuration is defined in backend/classroom_manager/urls.py.

  • Routes are organized in two ways:

    • DRF router.register(...) manages standard resources such as users, classrooms, borrow, and courses.

    • path(...) manages standalone actions such as authentication, uploads, system config, and signage activation.

Authentication Model

  • Default authentication classes:

    • apps.accounts.authentication.CookieJWTAuthentication

    • rest_framework.authentication.SessionAuthentication

  • After successful email or WebAuthn login on the web, tokens are returned and token, refresh_token, logged_in, and csrftoken cookies are set.

  • The WeChat mini program login does not use cookies; it returns access/refresh tokens in the response body.

  • CookieJWTAuthentication reads the Authorization header first, then falls back to the token cookie.

  • The user’s active role current_role is not persisted; it comes from the JWT claim or the X-Current-Role request header. Middleware validates that it exists in user.roles.

  • Signage devices do not use user JWTs; they use apps.signage.authentication.SignageDeviceAuthentication with the header format Authorization: Bearer <device_token>.

Cross-Module Dependencies

  • courses provides the source of truth for classroom schedules; classrooms, signage, borrowings, and checkins all depend on CourseOccurrence.

  • borrowings conflict detection checks both manual reservations and course occupancy.

  • checkins anchors CheckinSession creation to CourseOccurrence.

  • common.SystemConfig provides seasonal schedules and semester start dates to courses.

  • logs is reused across business modules via OperationLogContextMiddleware and log_operation().

Responses & Pagination

  • Two response styles coexist in the project:

    • The {code, message, data} wrapper from common.responses.success/error

    • DRF’s default pagination or serializer responses

  • Most custom actions use the unified wrapper.

  • Endpoints that rely on the default ModelViewSet.list/retrieve/update implementation may return DRF’s native structure.

  • During frontend-backend integration, do not assume all endpoints uniformly return {code, message, data}.

Middleware & Shared Capabilities

  • apps.logs.utils.OperationLogContextMiddleware

    • Stores the current request in a ContextVar, making it available to the service layer for logging.

  • apps.accounts.middleware.CurrentRoleMiddleware

    • Resolves the active role from the JWT or X-Current-Role header.

  • common.middleware.request_timezone.RequestTimezoneMiddleware

    • Switches the current request timezone based on the X-Timezone header.

Current Implementation Caveats

  • The log permission class apps.logs.permissions.IsLogAdmin still reads the legacy user.role field, so non-Django is_superuser/is_staff accounts typically cannot reliably access log endpoints.

  • services/file_storage.py already has a local/S3 dual-backend abstraction, but common.views.upload_file still writes directly to the local filesystem.

  • Signage already has independent device authentication, but it is currently used only for information display and is not integrated into the check-in write flow.