mirror of
https://github.com/ansible/awx.git
synced 2026-01-07 14:02:07 -03:30
Added Django and API requirements to AWX Contributor Docs for POC (#16093)
* Requirements POC docs from Claude Code eval * Removed unnecessary reference. * Excluded custom DRF configurations per @AlanCoding * Implement review changes from @chrismeyersfsu --------- Co-authored-by: Peter Braun <pbraun@redhat.com>
This commit is contained in:
parent
f3fd9945d6
commit
0d9483b54c
1305
docs/docsite/rst/contributor/API_REQUIREMENTS.rst
Normal file
1305
docs/docsite/rst/contributor/API_REQUIREMENTS.rst
Normal file
File diff suppressed because it is too large
Load Diff
725
docs/docsite/rst/contributor/DJANGO_REQUIREMENTS.rst
Normal file
725
docs/docsite/rst/contributor/DJANGO_REQUIREMENTS.rst
Normal file
@ -0,0 +1,725 @@
|
||||
=====================================================
|
||||
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
|
||||
|
||||
----
|
||||
|
||||
.. contents:: Table of Contents
|
||||
:depth: 3
|
||||
:local:
|
||||
|
||||
----
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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 organization
|
||||
- Implement 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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 <https://docs.djangoproject.com/en/5.2/ref/settings/#secret-key>`_.
|
||||
|
||||
6.3 Proxy and Network Security
|
||||
------------------------------
|
||||
|
||||
**REQUIRED**: Configure reverse proxy security:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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()`` method
|
||||
- Environment-based feature toggling
|
||||
- Plugin system integration
|
||||
- Signal registration
|
||||
|
||||
----
|
||||
|
||||
10. Middleware Implementation
|
||||
=============================
|
||||
|
||||
10.1 Custom Middleware for Enterprise Features
|
||||
----------------------------------------------
|
||||
|
||||
**REQUIRED**: Implement domain-specific middleware:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# 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
|
||||
---------------------
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 50 10
|
||||
|
||||
* - 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
|
||||
--------------------
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 50 10
|
||||
|
||||
* - 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
|
||||
------------
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 50 10
|
||||
|
||||
* - 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
|
||||
|
||||
----
|
||||
|
||||
| **Document Maintainer**: Development Team
|
||||
| **Last Updated**: September 2025
|
||||
| **Review Schedule**: Quarterly
|
||||
@ -15,6 +15,8 @@ Ansible AWX helps teams manage complex multi-tier deployments by adding control,
|
||||
:caption: Community
|
||||
|
||||
contributor/index
|
||||
contributor/DJANGO_REQUIREMENTS
|
||||
contributor/API_REQUIREMENTS
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user