Django Development Requirements
AWX Codebase Best Practices
- Version:
1.0
- Date:
September 2025
- Based on:
AWX Enterprise Django Application Analysis
- Generated by:
Claude Code AI
1. Project Structure
1.1 Modular Application Architecture
REQUIRED: Organize Django project with clear separation of concerns:
awx/
├── __init__.py # Version management and environment detection
├── main/ # Core business logic and models
├── api/ # REST API layer (Django REST Framework)
├── ui/ # Frontend integration
├── conf/ # Configuration management
├── settings/ # Environment-specific settings
├── templates/ # Django templates
└── static/ # Static assets
Requirements:
Each functional area must have its own Django app
Use descriptive app names that reflect business domains
Separate API logic from core business logic
1.2 Pre-Management Command Code
This section describes the code that runs before every management command.
AWX persistent services (i.e. wsrelay, heartbeat, dispatcher) all have management commands as entry points. So if you want to write a new persistent service, make a management command.
System jobs are implemented as management commands too.
REQUIRED: Implement custom Django management integration:
# awx/__init__.py
def manage():
"""Custom management function with environment preparation"""
prepare_env()
from django.core.management import execute_from_command_line
# Version validation for production
if not MODE == 'development':
validate_production_requirements()
execute_from_command_line(sys.argv)
Requirements:
Environment detection (development/production modes)
Production deployment validation
Custom version checking mechanisms
Database version compatibility checks
2. Settings Management
2.1 Environment-Based Settings Architecture
REQUIRED: Use django-split-settings for modular configuration:
# settings/defaults.py - Base configuration
# settings/development.py - Development overrides
# settings/production.py - Production security settings
# settings/testing.py - Test-specific configuration
Settings Pattern:
# development.py
from .defaults import *
from split_settings.tools import optional, include
DEBUG = True
ALLOWED_HOSTS = ['*']
# Include optional local settings
include(optional('local_settings.py'))
2.2 Sourcing config from files
REQUIRED: Sourcing config from multiple files (in a directory) on disk:
# External settings loading
EXTERNAL_SETTINGS = os.environ.get('AWX_SETTINGS_FILE')
if EXTERNAL_SETTINGS:
include(EXTERNAL_SETTINGS, scope=locals())
3. URL Patterns and Routing
3.1 Modular URL Architecture
REQUIRED: Implement hierarchical URL organization with namespacing:
# urls.py
def get_urlpatterns(prefix=None):
"""Dynamic URL pattern generation with prefix support"""
if not prefix:
prefix = '/'
else:
prefix = f'/{prefix}/'
return [
path(f'api{prefix}', include('awx.api.urls', namespace='api')),
path(f'ui{prefix}', include('awx.ui.urls', namespace='ui')),
]
urlpatterns = get_urlpatterns()
3.2 Environment-Specific URL Inclusion
REQUIRED: Conditional URL patterns based on environment:
This example allows the Django debug toolbar to work.
# Development-only URLs
if settings.DEBUG:
try:
import debug_toolbar
urlpatterns += [path('__debug__/', include(debug_toolbar.urls))]
except ImportError:
pass
OPTIONAL: If you want to include your own debug logic and endpoints:
if MODE == 'development':
# Only include these if we are in the development environment
from awx.api.swagger import schema_view
from awx.api.urls.debug import urls as debug_urls
urlpatterns += [re_path(r'^debug/', include(debug_urls))]
urlpatterns += [
re_path(r'^swagger(?P<format>\.json|\.yaml)/$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
re_path(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
Requirements:
Use Django’s
include()for modular organizationImplement URL namespacing for API versioning
Support dynamic URL prefix configuration
Separate URL patterns by functional area
4. Model Design
4.1 Abstract Base Models
REQUIRED: Use abstract base models for common functionality:
# models/base.py
class BaseModel(models.Model):
"""Common fields and methods for all models"""
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class AuditableModel(BaseModel):
"""Models requiring audit trail"""
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
abstract = True
4.2 Mixin-Based Architecture
REQUIRED: Implement reusable model behaviors through mixins:
# models/mixins.py
class ResourceMixin(models.Model):
"""Common resource management functionality"""
class Meta:
abstract = True
class ExecutionEnvironmentMixin(models.Model):
"""Execution environment configuration"""
class Meta:
abstract = True
4.3 Model Organization
REQUIRED: Organize models by domain functionality:
models/
├── __init__.py
├── base.py # Abstract base models
├── mixins.py # Reusable model behaviors
├── inventory.py # Inventory-related models
├── jobs.py # Job execution models
├── credential.py # Credential management
└── organization.py # Organization models
Requirements:
One file per logical domain until the domain gets too big, create a folder for it instead. In the past, credentials were broken out into logical domains until they were moved out of AWX, then they were collapsed back down to a single file.
Use consistent naming conventions
Implement comprehensive model validation
Custom managers for complex queries
5. REST API Development
5.1 Custom Authentication Classes
The recommended best practice is to log all of the terminal (return) paths of authentication, not just the successful ones.
REQUIRED: Implement domain-specific authentication with logging:
# api/authentication.py
class LoggedBasicAuthentication(authentication.BasicAuthentication):
"""Basic authentication with request logging"""
def authenticate(self, request):
if not settings.AUTH_BASIC_ENABLED:
return
ret = super().authenticate(request)
if ret:
username = ret[0].username if ret[0] else '<none>'
logger.info(
f"User {username} performed {request.method} "
f"to {request.path} through the API"
)
return ret
5.2 Custom Permission Classes
REQUIRED: Implement comprehensive permission checking:
# api/permissions.py
class ModelAccessPermission(permissions.BasePermission):
"""Model-based access control with hierarchy support"""
def has_permission(self, request, view):
if hasattr(view, 'parent_model'):
parent_obj = view.get_parent_object()
return check_user_access(
request.user,
view.parent_model,
'read',
parent_obj
)
return True
Requirements:
Multiple authentication methods (JWT, Session, Basic)
Custom pagination, renderers, and metadata classes
Comprehensive API exception handling
Resource-based URL organization
Logging for authentication events
6. Security Requirements
6.1 Production Security Settings
REQUIRED: Enforce secure defaults for production:
# settings/production.py
DEBUG = False
SECRET_KEY = None # Force explicit configuration
ALLOWED_HOSTS = [] # Must be explicitly set
# Session security
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_COOKIE_AGE = 1800
# CSRF protection
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_TRUSTED_ORIGINS = []
6.2 Django SECRET_KEY loading
REQUIRED: Implement Django SECRET_KEY loading:
# Secret key from external file
SECRET_KEY_FILE = os.environ.get('SECRET_KEY_FILE', '/etc/awx/SECRET_KEY')
if os.path.exists(SECRET_KEY_FILE):
with open(SECRET_KEY_FILE, 'rb') as f:
SECRET_KEY = f.read().strip().decode()
else:
if not DEBUG:
raise ImproperlyConfigured("SECRET_KEY must be configured in production")
For more detail, refer to the Django documentation.
6.3 Proxy and Network Security
REQUIRED: Configure reverse proxy security:
# Proxy configuration
REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST']
PROXY_IP_ALLOWED_LIST = []
USE_X_FORWARDED_HOST = True
USE_X_FORWARDED_PORT = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Requirements:
External secret file management
Secure cookie configuration
CSRF protection with trusted origins
Proxy header validation
Force HTTPS in production
7. Database Management
7.1 Advanced Database Configuration
REQUIRED: Robust database connections for production:
# Database configuration with connection tuning
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DATABASE_NAME', 'awx'),
'ATOMIC_REQUESTS': True,
'CONN_MAX_AGE': 0,
'OPTIONS': {
'keepalives': 1,
'keepalives_idle': 5,
'keepalives_interval': 5,
'keepalives_count': 5,
},
}
}
7.2 Database Version Validation
REQUIRED: Implement database compatibility checking:
# PostgreSQL version enforcement
def validate_database_version():
from django.db import connection
if (connection.pg_version // 10000) < 12:
raise ImproperlyConfigured(
"PostgreSQL version 12 or higher is required"
)
7.3 Migration Management
REQUIRED: Structured migration organization
migrations/
├── 0001_initial.py
├── 0002_squashed_v300_release.py
├── 0003_squashed_v300_v303_updates.py
└── _migration_utils.py
Requirements: It is best practice to not to re-write migrations. If possible, include a reverse migration, especially for data migrations to make testing easier.
8. Testing Standards
8.1 Pytest Configuration
REQUIRED: Comprehensive test setup with optimization:
# pytest.ini
[pytest]
DJANGO_SETTINGS_MODULE = awx.main.tests.settings_for_test
python_files = *.py
addopts = --reuse-db --nomigrations --tb=native
markers =
ac: access control test
survey: tests related to survey feature
inventory_import: tests of code used by inventory import command
integration: integration tests requiring external services
8.2 Test Settings Module
REQUIRED: Dedicated test configuration:
# settings/testing.py
from .defaults import *
# Fast test database
DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3'
DATABASES['default']['NAME'] = ':memory:'
# Disable migrations for speed
class DisableMigrations:
def __contains__(self, item):
return True
def __getitem__(self, item):
return None
MIGRATION_MODULES = DisableMigrations()
8.3 Coverage Requirements
REQUIRED: Enforce comprehensive test coverage:
# Coverage targets
COVERAGE_TARGETS = {
'project_overall': 75,
'library_code': 75,
'test_code': 95,
'new_patches': 100,
'type_checking': 100,
}
Requirements:
Database reuse for faster execution
Skip migrations in tests
Custom test markers for categorization
Dedicated test settings module
Comprehensive warning filters
9. Application Configuration
9.1 Advanced AppConfig Implementation
REQUIRED: Custom application configuration with initialization:
# apps.py
class MainConfig(AppConfig):
name = 'awx.main'
verbose_name = _('Main')
default_auto_field = 'django.db.models.AutoField'
def ready(self):
super().ready()
# Feature loading with environment checks
if not os.environ.get('AWX_SKIP_FEATURES', None):
self.load_credential_types()
self.load_inventory_plugins()
self.load_named_urls()
# Signal registration
self.register_signals()
def load_credential_types(self):
"""Load credential type definitions"""
pass
def register_signals(self):
"""Register Django signals"""
pass
Requirements:
Custom AppConfig for complex initialization
Feature loading in
ready()methodEnvironment-based feature toggling
Plugin system integration
Signal registration
10. Middleware Implementation
10.1 Custom Middleware for Enterprise Features
REQUIRED: Implement domain-specific middleware:
# middleware.py
class SettingsCacheMiddleware(MiddlewareMixin):
"""Clear settings cache on each request"""
def process_request(self, request):
from django.conf import settings
if hasattr(settings, '_awx_conf_memoizedcache'):
settings._awx_conf_memoizedcache.clear()
class TimingMiddleware(threading.local, MiddlewareMixin):
"""Request timing and performance monitoring"""
def process_request(self, request):
self.start_time = time.time()
def process_response(self, request, response):
if hasattr(self, 'start_time'):
duration = time.time() - self.start_time
response['X-Response-Time'] = f"{duration:.3f}s"
return response
Requirements:
Settings cache management middleware
Performance monitoring middleware
Thread-local storage for request data
Conditional middleware activation
11. Deployment Patterns
11.1 Production-Ready ASGI/WSGI Configuration
REQUIRED: Proper application server setup:
# asgi.py
import os
import django
from channels.routing import get_default_application
from awx import prepare_env
prepare_env()
django.setup()
application = get_default_application()
# wsgi.py
import os
from django.core.wsgi import get_wsgi_application
from awx import prepare_env
prepare_env()
application = get_wsgi_application()
Compliance Checklist
Development Standards
Requirement |
Status |
|---|---|
Modular app architecture implemented |
☐ |
Environment-based settings configured |
☐ |
Custom authentication and permissions |
☐ |
Comprehensive test coverage (>75%) |
☐ |
Security settings enforced |
☐ |
Database optimization configured |
☐ |
Static files properly organized |
☐ |
Custom middleware implemented |
☐ |
Production Readiness
Requirement |
Status |
|---|---|
External secret management |
☐ |
Database version validation |
☐ |
Version deployment verification |
☐ |
Performance monitoring |
☐ |
Security headers configured |
☐ |
HTTPS enforcement |
☐ |
Proper logging setup |
☐ |
Error handling and monitoring |
☐ |
Code Quality
Requirement |
Status |
|---|---|
Abstract base models used |
☐ |
Mixin-based architecture |
☐ |
Custom management commands |
☐ |
Plugin system support |
☐ |
Signal registration |
☐ |
Migration organization |
☐ |
API documentation |
☐ |
Type hints and validation |
☐ |
References
Django Documentation: https://docs.djangoproject.com/
Django REST Framework: https://www.django-rest-framework.org/
Django Split Settings: https://github.com/sobolevn/django-split-settings
AWX Source Code: https://github.com/ansible/awx