django-cfg 1.5.14__py3-none-any.whl → 1.5.29__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-cfg might be problematic. Click here for more details.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/business/accounts/serializers/profile.py +42 -0
- django_cfg/apps/business/support/serializers.py +3 -2
- django_cfg/apps/integrations/centrifugo/__init__.py +2 -0
- django_cfg/apps/integrations/centrifugo/apps.py +2 -1
- django_cfg/apps/integrations/centrifugo/codegen/generators/typescript_thin/templates/rpc-client.ts.j2 +151 -12
- django_cfg/apps/integrations/centrifugo/management/commands/generate_centrifugo_clients.py +2 -2
- django_cfg/apps/integrations/centrifugo/services/__init__.py +6 -0
- django_cfg/apps/integrations/centrifugo/services/client/__init__.py +6 -1
- django_cfg/apps/integrations/centrifugo/services/client/client.py +1 -1
- django_cfg/apps/integrations/centrifugo/services/client/direct_client.py +282 -0
- django_cfg/apps/integrations/centrifugo/services/logging.py +47 -0
- django_cfg/apps/integrations/centrifugo/services/publisher.py +371 -0
- django_cfg/apps/integrations/centrifugo/services/token_generator.py +122 -0
- django_cfg/apps/integrations/centrifugo/urls.py +8 -0
- django_cfg/apps/integrations/centrifugo/views/__init__.py +2 -0
- django_cfg/apps/integrations/centrifugo/views/admin_api.py +29 -32
- django_cfg/apps/integrations/centrifugo/views/testing_api.py +31 -116
- django_cfg/apps/integrations/centrifugo/views/token_api.py +101 -0
- django_cfg/apps/integrations/centrifugo/views/wrapper.py +259 -0
- django_cfg/apps/integrations/grpc/auth/api_key_auth.py +11 -10
- django_cfg/apps/integrations/grpc/management/commands/compile_proto.py +105 -0
- django_cfg/apps/integrations/grpc/management/commands/generate_protos.py +56 -1
- django_cfg/apps/integrations/grpc/management/commands/rungrpc.py +315 -26
- django_cfg/apps/integrations/grpc/management/proto/__init__.py +3 -0
- django_cfg/apps/integrations/grpc/management/proto/compiler.py +194 -0
- django_cfg/apps/integrations/grpc/managers/grpc_request_log.py +84 -0
- django_cfg/apps/integrations/grpc/managers/grpc_server_status.py +126 -3
- django_cfg/apps/integrations/grpc/models/grpc_api_key.py +7 -1
- django_cfg/apps/integrations/grpc/models/grpc_server_status.py +22 -3
- django_cfg/apps/integrations/grpc/services/__init__.py +102 -17
- django_cfg/apps/integrations/grpc/services/centrifugo/__init__.py +29 -0
- django_cfg/apps/integrations/grpc/services/centrifugo/bridge.py +469 -0
- django_cfg/apps/integrations/grpc/services/centrifugo/config.py +167 -0
- django_cfg/apps/integrations/grpc/services/centrifugo/demo.py +626 -0
- django_cfg/apps/integrations/grpc/services/centrifugo/test_publish.py +229 -0
- django_cfg/apps/integrations/grpc/services/centrifugo/transformers.py +89 -0
- django_cfg/apps/integrations/grpc/services/client/__init__.py +26 -0
- django_cfg/apps/integrations/grpc/services/commands/IMPLEMENTATION.md +456 -0
- django_cfg/apps/integrations/grpc/services/commands/README.md +252 -0
- django_cfg/apps/integrations/grpc/services/commands/__init__.py +93 -0
- django_cfg/apps/integrations/grpc/services/commands/base.py +243 -0
- django_cfg/apps/integrations/grpc/services/commands/examples/__init__.py +22 -0
- django_cfg/apps/integrations/grpc/services/commands/examples/base_client.py +228 -0
- django_cfg/apps/integrations/grpc/services/commands/examples/client.py +272 -0
- django_cfg/apps/integrations/grpc/services/commands/examples/config.py +177 -0
- django_cfg/apps/integrations/grpc/services/commands/examples/start.py +125 -0
- django_cfg/apps/integrations/grpc/services/commands/examples/stop.py +101 -0
- django_cfg/apps/integrations/grpc/services/commands/registry.py +170 -0
- django_cfg/apps/integrations/grpc/services/discovery/__init__.py +39 -0
- django_cfg/apps/integrations/grpc/services/{discovery.py → discovery/discovery.py} +67 -54
- django_cfg/apps/integrations/grpc/services/{service_registry.py → discovery/registry.py} +215 -5
- django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/__init__.py +3 -1
- django_cfg/apps/integrations/grpc/services/interceptors/centrifugo.py +541 -0
- django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/metrics.py +3 -3
- django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/request_logger.py +10 -13
- django_cfg/apps/integrations/grpc/services/management/__init__.py +37 -0
- django_cfg/apps/integrations/grpc/services/monitoring/__init__.py +38 -0
- django_cfg/apps/integrations/grpc/services/{monitoring_service.py → monitoring/monitoring.py} +2 -2
- django_cfg/apps/integrations/grpc/services/{testing_service.py → monitoring/testing.py} +5 -5
- django_cfg/apps/integrations/grpc/services/rendering/__init__.py +27 -0
- django_cfg/apps/integrations/grpc/services/{chart_generator.py → rendering/charts.py} +1 -1
- django_cfg/apps/integrations/grpc/services/routing/__init__.py +59 -0
- django_cfg/apps/integrations/grpc/services/routing/config.py +76 -0
- django_cfg/apps/integrations/grpc/services/routing/router.py +430 -0
- django_cfg/apps/integrations/grpc/services/streaming/__init__.py +117 -0
- django_cfg/apps/integrations/grpc/services/streaming/config.py +451 -0
- django_cfg/apps/integrations/grpc/services/streaming/service.py +651 -0
- django_cfg/apps/integrations/grpc/services/streaming/types.py +367 -0
- django_cfg/apps/integrations/grpc/utils/SERVER_LOGGING.md +164 -0
- django_cfg/apps/integrations/grpc/utils/__init__.py +58 -1
- django_cfg/apps/integrations/grpc/utils/converters.py +565 -0
- django_cfg/apps/integrations/grpc/utils/handlers.py +242 -0
- django_cfg/apps/integrations/grpc/utils/proto_gen.py +1 -1
- django_cfg/apps/integrations/grpc/utils/streaming_logger.py +261 -13
- django_cfg/apps/integrations/grpc/views/charts.py +1 -1
- django_cfg/apps/integrations/grpc/views/config.py +1 -1
- django_cfg/apps/system/dashboard/serializers/config.py +95 -9
- django_cfg/apps/system/dashboard/serializers/statistics.py +9 -4
- django_cfg/apps/system/frontend/views.py +87 -6
- django_cfg/core/base/config_model.py +11 -0
- django_cfg/core/builders/middleware_builder.py +5 -0
- django_cfg/core/builders/security_builder.py +1 -0
- django_cfg/core/generation/integration_generators/api.py +2 -0
- django_cfg/management/commands/pool_status.py +153 -0
- django_cfg/middleware/pool_cleanup.py +261 -0
- django_cfg/models/api/grpc/config.py +2 -2
- django_cfg/models/infrastructure/database/config.py +16 -0
- django_cfg/models/infrastructure/database/converters.py +2 -0
- django_cfg/modules/django_admin/utils/html/composition.py +57 -13
- django_cfg/modules/django_admin/utils/html_builder.py +1 -0
- django_cfg/modules/django_client/core/generator/typescript/generator.py +26 -0
- django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +7 -1
- django_cfg/modules/django_client/core/generator/typescript/models_generator.py +5 -0
- django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +11 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +1 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/function.ts.jinja +29 -1
- django_cfg/modules/django_client/core/generator/typescript/templates/hooks/hooks.ts.jinja +4 -0
- django_cfg/modules/django_client/core/groups/manager.py +25 -18
- django_cfg/modules/django_client/core/ir/schema.py +15 -1
- django_cfg/modules/django_client/core/parser/base.py +12 -0
- django_cfg/modules/django_client/management/commands/generate_client.py +9 -5
- django_cfg/modules/django_logging/django_logger.py +58 -19
- django_cfg/pyproject.toml +3 -3
- django_cfg/static/frontend/admin.zip +0 -0
- django_cfg/templates/admin/index.html +0 -39
- django_cfg/utils/pool_monitor.py +320 -0
- django_cfg/utils/smart_defaults.py +233 -7
- {django_cfg-1.5.14.dist-info → django_cfg-1.5.29.dist-info}/METADATA +75 -5
- {django_cfg-1.5.14.dist-info → django_cfg-1.5.29.dist-info}/RECORD +118 -74
- /django_cfg/apps/integrations/grpc/services/{grpc_client.py → client/client.py} +0 -0
- /django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/errors.py +0 -0
- /django_cfg/apps/integrations/grpc/{interceptors → services/interceptors}/logging.py +0 -0
- /django_cfg/apps/integrations/grpc/services/{config_helper.py → management/config_helper.py} +0 -0
- /django_cfg/apps/integrations/grpc/services/{proto_files_manager.py → management/proto_manager.py} +0 -0
- {django_cfg-1.5.14.dist-info → django_cfg-1.5.29.dist-info}/WHEEL +0 -0
- {django_cfg-1.5.14.dist-info → django_cfg-1.5.29.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.5.14.dist-info → django_cfg-1.5.29.dist-info}/licenses/LICENSE +0 -0
|
@@ -7,6 +7,8 @@ Following KISS principle:
|
|
|
7
7
|
- Logging handled by django_logger module
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
10
12
|
from pathlib import Path
|
|
11
13
|
from typing import Any, Dict, List, Optional
|
|
12
14
|
|
|
@@ -14,9 +16,9 @@ from typing import Any, Dict, List, Optional
|
|
|
14
16
|
def get_log_filename() -> str:
|
|
15
17
|
"""
|
|
16
18
|
Determine the correct log filename based on project type.
|
|
17
|
-
|
|
19
|
+
|
|
18
20
|
Returns:
|
|
19
|
-
- 'django-cfg.log' for django-cfg projects
|
|
21
|
+
- 'django-cfg.log' for django-cfg projects
|
|
20
22
|
- 'django.log' for regular Django projects
|
|
21
23
|
"""
|
|
22
24
|
try:
|
|
@@ -35,6 +37,188 @@ def get_log_filename() -> str:
|
|
|
35
37
|
return 'django-cfg.log'
|
|
36
38
|
|
|
37
39
|
|
|
40
|
+
def _detect_asgi_mode() -> bool:
|
|
41
|
+
"""
|
|
42
|
+
Detect if Django is running in ASGI or WSGI mode.
|
|
43
|
+
|
|
44
|
+
Detection priority:
|
|
45
|
+
1. DJANGO_ASGI environment variable (explicit override)
|
|
46
|
+
2. ASGI_APPLICATION setting (if Django is configured)
|
|
47
|
+
3. Command-line arguments (uvicorn, daphne, hypercorn)
|
|
48
|
+
4. Default: False (WSGI mode)
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
True if ASGI mode, False if WSGI mode
|
|
52
|
+
|
|
53
|
+
Examples:
|
|
54
|
+
>>> os.environ['DJANGO_ASGI'] = 'true'
|
|
55
|
+
>>> _detect_asgi_mode()
|
|
56
|
+
True
|
|
57
|
+
|
|
58
|
+
>>> 'uvicorn' in sys.argv[0]
|
|
59
|
+
>>> _detect_asgi_mode()
|
|
60
|
+
True
|
|
61
|
+
"""
|
|
62
|
+
# 1. Check explicit env var override
|
|
63
|
+
asgi_env = os.environ.get('DJANGO_ASGI', '').lower()
|
|
64
|
+
if asgi_env in ('true', '1', 'yes'):
|
|
65
|
+
return True
|
|
66
|
+
elif asgi_env in ('false', '0', 'no'):
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
# 2. Check Django settings for ASGI_APPLICATION
|
|
70
|
+
try:
|
|
71
|
+
from django.conf import settings
|
|
72
|
+
if hasattr(settings, 'ASGI_APPLICATION') and settings.ASGI_APPLICATION:
|
|
73
|
+
return True
|
|
74
|
+
except (ImportError, Exception):
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
# 3. Check command-line arguments for ASGI servers
|
|
78
|
+
command_line = ' '.join(sys.argv).lower()
|
|
79
|
+
asgi_servers = ['uvicorn', 'daphne', 'hypercorn']
|
|
80
|
+
for server in asgi_servers:
|
|
81
|
+
if server in command_line:
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
# Default: WSGI mode
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_pool_config(environment: str = "development", is_asgi: Optional[bool] = None) -> Dict[str, Any]:
|
|
89
|
+
"""
|
|
90
|
+
Get connection pool configuration.
|
|
91
|
+
|
|
92
|
+
By default, uses simple environment-based configuration. Set AUTO_POOL_SIZE=true
|
|
93
|
+
to enable automatic ASGI/WSGI detection and optimization.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
environment: Environment name ('development', 'testing', 'staging', 'production')
|
|
97
|
+
is_asgi: Deployment mode. If None and AUTO_POOL_SIZE=true, auto-detects mode
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Dict with pool configuration:
|
|
101
|
+
{
|
|
102
|
+
'min_size': int, # Minimum pool size
|
|
103
|
+
'max_size': int, # Maximum pool size
|
|
104
|
+
'timeout': int, # Connection timeout (seconds)
|
|
105
|
+
'max_lifetime': int, # Max connection lifetime (seconds)
|
|
106
|
+
'max_idle': int, # Max idle time before closing (seconds)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
Environment Variables:
|
|
110
|
+
DB_POOL_MIN_SIZE: Minimum pool size (default: 10)
|
|
111
|
+
DB_POOL_MAX_SIZE: Maximum pool size (default: 50)
|
|
112
|
+
DB_POOL_TIMEOUT: Connection timeout in seconds (default: 30)
|
|
113
|
+
AUTO_POOL_SIZE: Enable automatic ASGI/WSGI detection (default: false)
|
|
114
|
+
|
|
115
|
+
Examples:
|
|
116
|
+
# Simple static config (default):
|
|
117
|
+
>>> get_pool_config('production')
|
|
118
|
+
{'min_size': 10, 'max_size': 50, ...}
|
|
119
|
+
|
|
120
|
+
# With auto-detection:
|
|
121
|
+
>>> os.environ['AUTO_POOL_SIZE'] = 'true'
|
|
122
|
+
>>> get_pool_config('production', is_asgi=True)
|
|
123
|
+
{'min_size': 5, 'max_size': 20, ...} # Optimized for ASGI
|
|
124
|
+
"""
|
|
125
|
+
# Check if auto-detection is enabled
|
|
126
|
+
auto_detect = os.environ.get('AUTO_POOL_SIZE', 'false').lower() in ('true', '1', 'yes')
|
|
127
|
+
|
|
128
|
+
# Simple static configuration (default)
|
|
129
|
+
if not auto_detect and is_asgi is None:
|
|
130
|
+
# Use simple env var based config
|
|
131
|
+
try:
|
|
132
|
+
min_size = int(os.environ.get('DB_POOL_MIN_SIZE', 10))
|
|
133
|
+
except ValueError:
|
|
134
|
+
min_size = 10
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
max_size = int(os.environ.get('DB_POOL_MAX_SIZE', 50))
|
|
138
|
+
except ValueError:
|
|
139
|
+
max_size = 50
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
timeout = int(os.environ.get('DB_POOL_TIMEOUT', 30))
|
|
143
|
+
except ValueError:
|
|
144
|
+
timeout = 30
|
|
145
|
+
|
|
146
|
+
# Validate
|
|
147
|
+
if min_size >= max_size:
|
|
148
|
+
min_size = max(1, max_size - 1)
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
'min_size': min_size,
|
|
152
|
+
'max_size': max_size,
|
|
153
|
+
'timeout': timeout,
|
|
154
|
+
'max_lifetime': 3600, # 1 hour
|
|
155
|
+
'max_idle': 600, # 10 minutes
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# Auto-detect ASGI mode if enabled and not specified
|
|
159
|
+
if is_asgi is None:
|
|
160
|
+
is_asgi = _detect_asgi_mode()
|
|
161
|
+
|
|
162
|
+
# Pool configuration matrix
|
|
163
|
+
# Format: (min_size, max_size, timeout)
|
|
164
|
+
pool_configs = {
|
|
165
|
+
'development': {
|
|
166
|
+
'asgi': (2, 10, 10),
|
|
167
|
+
'wsgi': (3, 15, 20),
|
|
168
|
+
},
|
|
169
|
+
'testing': {
|
|
170
|
+
'asgi': (1, 5, 5),
|
|
171
|
+
'wsgi': (2, 10, 10),
|
|
172
|
+
},
|
|
173
|
+
'staging': {
|
|
174
|
+
'asgi': (3, 15, 10),
|
|
175
|
+
'wsgi': (5, 30, 20),
|
|
176
|
+
},
|
|
177
|
+
'production': {
|
|
178
|
+
'asgi': (5, 20, 10),
|
|
179
|
+
'wsgi': (10, 50, 30),
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# Get base configuration
|
|
184
|
+
env_key = environment.lower()
|
|
185
|
+
if env_key not in pool_configs:
|
|
186
|
+
# Fallback to development for unknown environments
|
|
187
|
+
env_key = 'development'
|
|
188
|
+
|
|
189
|
+
mode_key = 'asgi' if is_asgi else 'wsgi'
|
|
190
|
+
min_size, max_size, timeout = pool_configs[env_key][mode_key]
|
|
191
|
+
|
|
192
|
+
# Allow environment variable overrides
|
|
193
|
+
try:
|
|
194
|
+
min_size = int(os.environ.get('DB_POOL_MIN_SIZE', min_size))
|
|
195
|
+
except ValueError:
|
|
196
|
+
pass # Keep default if invalid
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
max_size = int(os.environ.get('DB_POOL_MAX_SIZE', max_size))
|
|
200
|
+
except ValueError:
|
|
201
|
+
pass
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
timeout = int(os.environ.get('DB_POOL_TIMEOUT', timeout))
|
|
205
|
+
except ValueError:
|
|
206
|
+
pass
|
|
207
|
+
|
|
208
|
+
# Validate: min_size must be < max_size
|
|
209
|
+
if min_size >= max_size:
|
|
210
|
+
min_size = max(1, max_size - 1)
|
|
211
|
+
|
|
212
|
+
# Build and return pool configuration
|
|
213
|
+
return {
|
|
214
|
+
'min_size': min_size,
|
|
215
|
+
'max_size': max_size,
|
|
216
|
+
'timeout': timeout,
|
|
217
|
+
'max_lifetime': 3600, # 1 hour
|
|
218
|
+
'max_idle': 600, # 10 minutes
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
38
222
|
class SmartDefaults:
|
|
39
223
|
"""
|
|
40
224
|
Environment-aware smart defaults for Django configuration.
|
|
@@ -45,18 +229,50 @@ class SmartDefaults:
|
|
|
45
229
|
|
|
46
230
|
@staticmethod
|
|
47
231
|
def get_database_defaults(environment: str = "development", debug: bool = False, engine: str = "sqlite3") -> Dict[str, Any]:
|
|
48
|
-
"""
|
|
232
|
+
"""
|
|
233
|
+
Get database configuration defaults.
|
|
234
|
+
|
|
235
|
+
For PostgreSQL with Django 5.1+:
|
|
236
|
+
- Uses native connection pooling (recommended for ASGI/async apps)
|
|
237
|
+
- CONN_MAX_AGE = 0 (required with native pooling)
|
|
238
|
+
- ATOMIC_REQUESTS = True (default - safe and works with pooling)
|
|
239
|
+
- Pool sizes: Auto-configured based on environment and ASGI/WSGI mode
|
|
240
|
+
- Health checks: Handled automatically by psycopg3 pool
|
|
241
|
+
|
|
242
|
+
Note on Transaction Safety:
|
|
243
|
+
ATOMIC_REQUESTS=True is enabled by default, which wraps each request
|
|
244
|
+
in a database transaction. This adds ~5-10ms overhead but ensures data
|
|
245
|
+
integrity without manual transaction management.
|
|
246
|
+
|
|
247
|
+
This works perfectly fine with connection pooling. If you need to optimize
|
|
248
|
+
for read-heavy workloads, you can disable ATOMIC_REQUESTS and use selective
|
|
249
|
+
transactions via Django's @transaction.atomic decorator on write views.
|
|
250
|
+
|
|
251
|
+
References:
|
|
252
|
+
- Django 5.1+ native pooling: https://docs.djangoproject.com/en/5.2/ref/databases/#connection-pooling
|
|
253
|
+
- ASGI best practices: persistent connections should be disabled with ASGI
|
|
254
|
+
"""
|
|
49
255
|
defaults = {
|
|
50
256
|
'ENGINE': 'django.db.backends.sqlite3',
|
|
51
257
|
'NAME': Path('db') / 'db.sqlite3',
|
|
52
|
-
'ATOMIC_REQUESTS': True,
|
|
53
|
-
'CONN_MAX_AGE':
|
|
258
|
+
'ATOMIC_REQUESTS': True, # Safe default - ~5-10ms overhead acceptable for data integrity
|
|
259
|
+
'CONN_MAX_AGE': 0, # Set to 0 for native pooling (Django 5.1+)
|
|
260
|
+
'CONN_HEALTH_CHECKS': True, # Enable health checks to prevent stale connections
|
|
54
261
|
'OPTIONS': {}
|
|
55
262
|
}
|
|
56
263
|
|
|
57
264
|
# Add engine-specific options
|
|
58
265
|
if engine == "django.db.backends.postgresql":
|
|
59
|
-
|
|
266
|
+
# Native connection pooling for Django 5.1+ with psycopg >= 3.1
|
|
267
|
+
# See: https://docs.djangoproject.com/en/5.2/ref/databases/#postgresql-connection-pooling
|
|
268
|
+
|
|
269
|
+
# Get dynamic pool configuration based on environment and deployment mode
|
|
270
|
+
pool_config = get_pool_config(environment=environment, is_asgi=None)
|
|
271
|
+
|
|
272
|
+
defaults['OPTIONS'] = {
|
|
273
|
+
'connect_timeout': 20,
|
|
274
|
+
'pool': pool_config, # Dynamic pool config (ASGI/WSGI aware)
|
|
275
|
+
}
|
|
60
276
|
elif engine == "django.db.backends.sqlite3":
|
|
61
277
|
defaults['OPTIONS']['timeout'] = 20 # SQLite uses 'timeout'
|
|
62
278
|
|
|
@@ -139,7 +355,16 @@ class SmartDefaults:
|
|
|
139
355
|
|
|
140
356
|
@staticmethod
|
|
141
357
|
def get_middleware_defaults() -> List[str]:
|
|
142
|
-
"""
|
|
358
|
+
"""
|
|
359
|
+
Get middleware configuration defaults.
|
|
360
|
+
|
|
361
|
+
Note:
|
|
362
|
+
ConnectionPoolCleanupMiddleware is automatically added LAST if
|
|
363
|
+
enable_pool_cleanup=True in DjangoConfig (default).
|
|
364
|
+
|
|
365
|
+
This middleware prevents connection leaks when using native
|
|
366
|
+
connection pooling with ATOMIC_REQUESTS=False.
|
|
367
|
+
"""
|
|
143
368
|
return [
|
|
144
369
|
'corsheaders.middleware.CorsMiddleware',
|
|
145
370
|
'django.middleware.security.SecurityMiddleware',
|
|
@@ -149,6 +374,7 @@ class SmartDefaults:
|
|
|
149
374
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
150
375
|
'django.contrib.messages.middleware.MessageMiddleware',
|
|
151
376
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
377
|
+
# ConnectionPoolCleanupMiddleware added in DjangoConfig.get_middleware()
|
|
152
378
|
]
|
|
153
379
|
|
|
154
380
|
@staticmethod
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-cfg
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.29
|
|
4
4
|
Summary: Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features.
|
|
5
5
|
Project-URL: Homepage, https://djangocfg.com
|
|
6
6
|
Project-URL: Documentation, https://djangocfg.com
|
|
@@ -116,6 +116,7 @@ Requires-Dist: mkdocs>=1.5; extra == 'docs'
|
|
|
116
116
|
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
|
|
117
117
|
Requires-Dist: pymdown-extensions>=10.0; extra == 'docs'
|
|
118
118
|
Provides-Extra: full
|
|
119
|
+
Requires-Dist: aiobreaker<2.0,>=1.2.0; extra == 'full'
|
|
119
120
|
Requires-Dist: black>=23.0; extra == 'full'
|
|
120
121
|
Requires-Dist: build>=1.0; extra == 'full'
|
|
121
122
|
Requires-Dist: cent<6.0,>=5.0.0; extra == 'full'
|
|
@@ -150,6 +151,7 @@ Requires-Dist: tomlkit>=0.11; extra == 'full'
|
|
|
150
151
|
Requires-Dist: twine>=4.0; extra == 'full'
|
|
151
152
|
Requires-Dist: websockets<15.0,>=13.0; extra == 'full'
|
|
152
153
|
Provides-Extra: grpc
|
|
154
|
+
Requires-Dist: aiobreaker<2.0,>=1.2.0; extra == 'grpc'
|
|
153
155
|
Requires-Dist: grpcio-health-checking<2.0,>=1.50.0; extra == 'grpc'
|
|
154
156
|
Requires-Dist: grpcio-reflection<2.0,>=1.50.0; extra == 'grpc'
|
|
155
157
|
Requires-Dist: grpcio-tools<2.0,>=1.50.0; extra == 'grpc'
|
|
@@ -194,7 +196,7 @@ Description-Content-Type: text/markdown
|
|
|
194
196
|
|
|
195
197
|
### 🚀 The Modern Django Framework for Enterprise Applications
|
|
196
198
|
|
|
197
|
-
**Type-safe configuration** • **Next.js Admin** • **
|
|
199
|
+
**Type-safe configuration** • **Next.js Admin** • **gRPC Streaming** • **Real-time WebSockets** • **AI Agents** • **8 Enterprise Apps**
|
|
198
200
|
|
|
199
201
|
**[🎯 Live Demo](http://demo.djangocfg.com)** • **[📚 Documentation](https://djangocfg.com/docs/getting-started/intro)** • **[🐙 GitHub](https://github.com/markolofsen/django-cfg)**
|
|
200
202
|
|
|
@@ -394,6 +396,72 @@ class MyConfig(DjangoConfig):
|
|
|
394
396
|
|
|
395
397
|
---
|
|
396
398
|
|
|
399
|
+
### 🌐 gRPC Microservices & Streaming
|
|
400
|
+
|
|
401
|
+
**Production-ready gRPC integration** - bidirectional streaming, WebSocket bridge, and type-safe Protobuf.
|
|
402
|
+
|
|
403
|
+
```python
|
|
404
|
+
from django_cfg.apps.integrations.grpc.services.centrifugo import (
|
|
405
|
+
CentrifugoBridgeMixin,
|
|
406
|
+
CentrifugoChannels,
|
|
407
|
+
ChannelConfig,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# Define type-safe channel mappings
|
|
411
|
+
class BotChannels(CentrifugoChannels):
|
|
412
|
+
heartbeat: ChannelConfig = ChannelConfig(
|
|
413
|
+
template='bot#{bot_id}#heartbeat',
|
|
414
|
+
rate_limit=5.0, # Max once per 5 seconds
|
|
415
|
+
)
|
|
416
|
+
status: ChannelConfig = ChannelConfig(
|
|
417
|
+
template='bot#{bot_id}#status',
|
|
418
|
+
critical=True, # Bypass rate limiting
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# gRPC service with automatic WebSocket publishing
|
|
422
|
+
class BotStreamingService(
|
|
423
|
+
pb2_grpc.BotStreamingServiceServicer,
|
|
424
|
+
CentrifugoBridgeMixin # ← One-line WebSocket integration
|
|
425
|
+
):
|
|
426
|
+
centrifugo_channels = BotChannels()
|
|
427
|
+
|
|
428
|
+
async def ConnectBot(self, request_iterator, context):
|
|
429
|
+
async for message in request_iterator:
|
|
430
|
+
# Your business logic
|
|
431
|
+
await process_message(message)
|
|
432
|
+
|
|
433
|
+
# Auto-publish to WebSocket (1 line!)
|
|
434
|
+
await self._notify_centrifugo(message, bot_id=bot_id)
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**Built-in features:**
|
|
438
|
+
- 🔄 **Bidirectional Streaming** - Full-duplex gRPC communication
|
|
439
|
+
- 🌉 **Centrifugo Bridge** - Auto-publish gRPC events to WebSocket
|
|
440
|
+
- 🛡️ **Circuit Breaker** - Graceful degradation if Centrifugo unavailable
|
|
441
|
+
- 🔁 **Auto Retry** - Exponential backoff for critical events
|
|
442
|
+
- 📦 **Dead Letter Queue** - Never lose important messages
|
|
443
|
+
- ⚡ **Rate Limiting** - Per-channel throttling with critical bypass
|
|
444
|
+
- 🎯 **Type-Safe Config** - Pydantic v2 validation for channels
|
|
445
|
+
|
|
446
|
+
**Architecture:**
|
|
447
|
+
```
|
|
448
|
+
Trading Bot ──gRPC──> Django gRPC Service ──WebSocket──> Browser
|
|
449
|
+
↓
|
|
450
|
+
[Business Logic]
|
|
451
|
+
[Database Save]
|
|
452
|
+
[Centrifugo Publish]
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
**Why this approach?**
|
|
456
|
+
- ✅ Django controls all business logic and validation
|
|
457
|
+
- ✅ Single source of truth for data transformations
|
|
458
|
+
- ✅ Graceful degradation - gRPC works even if WebSocket fails
|
|
459
|
+
- ✅ Production-ready resilience patterns built-in
|
|
460
|
+
|
|
461
|
+
**[📚 gRPC Integration Guide →](https://djangocfg.com/docs/features/integrations/grpc)**
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
397
465
|
### 🤖 AI-Ready Infrastructure
|
|
398
466
|
|
|
399
467
|
**Built-in AI agent framework** - LLM workflow automation with Django ORM integration.
|
|
@@ -585,9 +653,11 @@ class ProductionConfig(DjangoConfig):
|
|
|
585
653
|
- **[Quick Start](https://djangocfg.com/docs/features/integrations/nextjs-admin/quick-start)** - 5-minute setup
|
|
586
654
|
- **[Configuration](https://djangocfg.com/docs/features/integrations/nextjs-admin/configuration)** - All options
|
|
587
655
|
|
|
588
|
-
### 📡 Real-Time
|
|
656
|
+
### 📡 Real-Time & Microservices
|
|
589
657
|
- **[Centrifugo Integration](https://djangocfg.com/docs/features/integrations/centrifugo)** - WebSocket setup
|
|
590
658
|
- **[Live Updates](https://djangocfg.com/docs/features/integrations/centrifugo/live-updates)** - Real-time data
|
|
659
|
+
- **[gRPC Streaming](https://djangocfg.com/docs/features/integrations/grpc)** - Bidirectional streaming
|
|
660
|
+
- **[gRPC → WebSocket Bridge](https://djangocfg.com/docs/features/integrations/grpc/centrifugo-bridge)** - Auto-publish to clients
|
|
591
661
|
|
|
592
662
|
### 🏗️ Core Features
|
|
593
663
|
- **[Built-in Apps](https://djangocfg.com/docs/features/built-in-apps/overview)** - 8 enterprise apps
|
|
@@ -632,9 +702,9 @@ class ProductionConfig(DjangoConfig):
|
|
|
632
702
|
|
|
633
703
|
<div align="center">
|
|
634
704
|
|
|
635
|
-
**Modern Django Framework** • **Type-Safe Configuration** • **Next.js Admin** • **Real-Time WebSockets** • **AI-Ready**
|
|
705
|
+
**Modern Django Framework** • **Type-Safe Configuration** • **Next.js Admin** • **gRPC Streaming** • **Real-Time WebSockets** • **AI-Ready**
|
|
636
706
|
|
|
637
|
-
Django-CFG is the modern Django framework for enterprise applications. Built with Pydantic v2 for type-safe configuration, includes Next.js admin integration,
|
|
707
|
+
Django-CFG is the modern Django framework for enterprise applications. Built with Pydantic v2 for type-safe configuration, includes Next.js admin integration, gRPC bidirectional streaming with WebSocket bridge, Centrifugo real-time support, AI agent framework, and 8 production-ready apps. Perfect for building scalable microservices and real-time Django applications with reduced boilerplate and enterprise features out of the box.
|
|
638
708
|
|
|
639
709
|
---
|
|
640
710
|
|