App Structure
Every django-cfg app follows a layered architecture with clear separation of concerns.
Standard Layout
my_app/
├── models/
│ ├── __init__.py # Re-exports all models
│ ├── my_model.py
│ └── choices.py # TextChoices enums
├── managers/
│ ├── __init__.py
│ └── my_model_managers.py # Custom QuerySet + Manager
├── services/
│ ├── __init__.py
│ └── my_service.py # Business logic
├── views/
│ ├── api/
│ │ ├── __init__.py
│ │ └── my_viewsets.py # DRF ViewSets
│ └── serializers/
│ ├── __init__.py
│ └── my_serializers.py # DRF Serializers
├── admin/
│ ├── __init__.py
│ ├── my_admin.py # PydanticAdmin config
│ └── actions.py # Admin actions
├── tasks/
│ ├── __init__.py
│ └── process_task.py # Background tasks (RQ/Dramatiq)
├── tests/
│ ├── __init__.py
│ ├── conftest.py # Fixtures
│ ├── factories.py # Model factories
│ ├── test_models.py
│ └── test_services.py
├── management/
│ └── commands/
│ └── my_command.py # Management commands
├── migrations/
├── apps.py
├── urls.py # DRF router
└── signals.py # Django signals (if needed)When to Use Files vs Directories
Single file — simple apps
If an app has 1-2 models and straightforward logic, use flat files:
activity/
├── models.py # Single model
├── admin.py # Single admin class
├── urls.py
└── views/
├── api/
│ └── activity_viewsets.py
└── serializers/
└── activity_serializers.pyReal example: the activity app (cmdop) is a simple audit logger — one model, one read-only ViewSet, one serializer.
Directory — complex apps
When a file exceeds ~300 lines or has multiple related classes, split into a directory with __init__.py re-exports:
# models/__init__.py
from .download_request import DownloadRequest
from .download_segment import DownloadSegment
from .download_error import DownloadError
from .export_job import ExportJobReal example: cryptodb_driver (stockapis) has 4 models, 8 services, 5 task files — everything is in directories.
File Size Limits
| File type | Max lines | Notes |
|---|---|---|
| Production code | 500 | Excluding comments and docstrings |
| Complex logic (models, services) | 300 | Split into multiple files if exceeded |
| Configuration files | 200 | Admin configs, app configs |
| Test files | 400 | Per test file |
| Test classes | 150 | Per class within a file |
When a file approaches the limit, decompose it — don’t compress code to fit. Create a directory with focused files instead.
Layer Flow
Request → URLs → ViewSet → Serializer → Service → Manager → Model → DB
↑ ↑
(validation) (query logic)Each layer has a single responsibility:
| Layer | Responsibility | Imports from |
|---|---|---|
| Models | Schema, constraints, properties | Nothing (bottom layer) |
| Managers | Query composition, chainable filters | Models |
| Services | Business logic, transactions, external APIs | Models, Managers |
| Serializers | Validation, field mapping, formatting | Models |
| ViewSets | HTTP handling, permissions, routing | Serializers, Services |
| Admin | Admin UI configuration | Models |
Key rule: ViewSets should never contain business logic. They orchestrate: validate input (serializer) → call service → serialize output.
App Registration
All django-cfg apps are registered through the core builder — never in local project configs:
# django_cfg/core/builders/apps_builder.py
apps.append("django_cfg.apps.system.my_app") # ✅ Correct
# django_cfg/apps/urls.py
path('cfg/my-app/', include('django_cfg.apps.system.my_app.urls')) # ✅ CorrectNaming Conventions
| Entity | Pattern | Example |
|---|---|---|
| App directory | snake_case | api_keys, cryptodb_driver |
| Model class | PascalCase | DownloadRequest, WorkspaceMember |
| Manager | {Model}Manager | MachineManager, AgentManager |
| QuerySet | {Model}QuerySet | MachineQuerySet |
| ViewSet | {Model}ViewSet | SymbolViewSet, ActivityViewSet |
| Serializer | {Model}{Action}Serializer | AgentListSerializer, APIKeyCreateSerializer |
| Service | {Feature}Service | SyncService, ConfidenceService |
| Task function | {verb}_{noun}_task | sync_all_symbols_task |
| URL basename | snake_case | basename='api-key' |
Checklist for New Apps
- Choose flat files or directories based on complexity
- Create models with proper constraints and indexes
- Add custom managers for common query patterns
- Implement services for business logic
- Create ViewSet with
@extend_schemadecorators - Write separate serializers per action (List, Detail, Create)
- Configure admin with
PydanticAdmin+AdminConfig - Register app in
apps_builder.py - Register URLs in
django_cfg/apps/urls.py - Write tests (models + services)
- Create and apply migrations