django-cfg 1.1.82__py3-none-any.whl → 1.2.0__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.
- django_cfg/__init__.py +20 -448
- django_cfg/apps/accounts/README.md +3 -3
- django_cfg/apps/accounts/admin/__init__.py +0 -2
- django_cfg/apps/accounts/admin/activity.py +2 -9
- django_cfg/apps/accounts/admin/filters.py +0 -42
- django_cfg/apps/accounts/admin/inlines.py +8 -8
- django_cfg/apps/accounts/admin/otp.py +5 -5
- django_cfg/apps/accounts/admin/registration_source.py +1 -8
- django_cfg/apps/accounts/admin/user.py +12 -20
- django_cfg/apps/accounts/managers/user_manager.py +2 -129
- django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
- django_cfg/apps/accounts/models.py +3 -123
- django_cfg/apps/accounts/serializers/otp.py +40 -44
- django_cfg/apps/accounts/serializers/profile.py +0 -2
- django_cfg/apps/accounts/services/otp_service.py +98 -186
- django_cfg/apps/accounts/signals.py +25 -15
- django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
- django_cfg/apps/accounts/views/otp.py +35 -36
- django_cfg/apps/agents/README.md +129 -0
- django_cfg/apps/agents/__init__.py +68 -0
- django_cfg/apps/agents/admin/__init__.py +17 -0
- django_cfg/apps/agents/admin/execution_admin.py +460 -0
- django_cfg/apps/agents/admin/registry_admin.py +360 -0
- django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
- django_cfg/apps/agents/apps.py +29 -0
- django_cfg/apps/agents/core/__init__.py +20 -0
- django_cfg/apps/agents/core/agent.py +281 -0
- django_cfg/apps/agents/core/dependencies.py +154 -0
- django_cfg/apps/agents/core/exceptions.py +66 -0
- django_cfg/apps/agents/core/models.py +106 -0
- django_cfg/apps/agents/core/orchestrator.py +391 -0
- django_cfg/apps/agents/examples/__init__.py +3 -0
- django_cfg/apps/agents/examples/simple_example.py +161 -0
- django_cfg/apps/agents/integration/__init__.py +14 -0
- django_cfg/apps/agents/integration/middleware.py +80 -0
- django_cfg/apps/agents/integration/registry.py +345 -0
- django_cfg/apps/agents/integration/signals.py +50 -0
- django_cfg/apps/agents/management/__init__.py +3 -0
- django_cfg/apps/agents/management/commands/__init__.py +3 -0
- django_cfg/apps/agents/management/commands/create_agent.py +365 -0
- django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
- django_cfg/apps/agents/managers/__init__.py +23 -0
- django_cfg/apps/agents/managers/execution.py +236 -0
- django_cfg/apps/agents/managers/registry.py +254 -0
- django_cfg/apps/agents/managers/toolsets.py +496 -0
- django_cfg/apps/agents/migrations/0001_initial.py +286 -0
- django_cfg/apps/agents/migrations/__init__.py +5 -0
- django_cfg/apps/agents/models/__init__.py +15 -0
- django_cfg/apps/agents/models/execution.py +215 -0
- django_cfg/apps/agents/models/registry.py +220 -0
- django_cfg/apps/agents/models/toolsets.py +305 -0
- django_cfg/apps/agents/patterns/__init__.py +24 -0
- django_cfg/apps/agents/patterns/content_agents.py +234 -0
- django_cfg/apps/agents/toolsets/__init__.py +15 -0
- django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
- django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
- django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
- django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
- django_cfg/apps/agents/urls.py +46 -0
- django_cfg/apps/knowbase/README.md +150 -0
- django_cfg/apps/knowbase/__init__.py +27 -0
- django_cfg/apps/knowbase/admin/__init__.py +23 -0
- django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
- django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
- django_cfg/apps/knowbase/admin/document_admin.py +650 -0
- django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
- django_cfg/apps/knowbase/apps.py +81 -0
- django_cfg/apps/knowbase/config/README.md +176 -0
- django_cfg/apps/knowbase/config/__init__.py +51 -0
- django_cfg/apps/knowbase/config/constance_fields.py +186 -0
- django_cfg/apps/knowbase/config/constance_settings.py +200 -0
- django_cfg/apps/knowbase/config/settings.py +444 -0
- django_cfg/apps/knowbase/examples/__init__.py +3 -0
- django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
- django_cfg/apps/knowbase/management/__init__.py +0 -0
- django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
- django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
- django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
- django_cfg/apps/knowbase/managers/__init__.py +22 -0
- django_cfg/apps/knowbase/managers/archive.py +426 -0
- django_cfg/apps/knowbase/managers/base.py +32 -0
- django_cfg/apps/knowbase/managers/chat.py +141 -0
- django_cfg/apps/knowbase/managers/document.py +203 -0
- django_cfg/apps/knowbase/managers/external_data.py +471 -0
- django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
- django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
- django_cfg/apps/knowbase/migrations/__init__.py +5 -0
- django_cfg/apps/knowbase/mixins/__init__.py +15 -0
- django_cfg/apps/knowbase/mixins/config.py +108 -0
- django_cfg/apps/knowbase/mixins/creator.py +81 -0
- django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
- django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
- django_cfg/apps/knowbase/mixins/service.py +362 -0
- django_cfg/apps/knowbase/models/__init__.py +41 -0
- django_cfg/apps/knowbase/models/archive.py +599 -0
- django_cfg/apps/knowbase/models/base.py +58 -0
- django_cfg/apps/knowbase/models/chat.py +157 -0
- django_cfg/apps/knowbase/models/document.py +267 -0
- django_cfg/apps/knowbase/models/external_data.py +376 -0
- django_cfg/apps/knowbase/serializers/__init__.py +68 -0
- django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
- django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
- django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
- django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
- django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
- django_cfg/apps/knowbase/services/__init__.py +40 -0
- django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
- django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
- django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
- django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
- django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
- django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
- django_cfg/apps/knowbase/services/base.py +53 -0
- django_cfg/apps/knowbase/services/chat_service.py +239 -0
- django_cfg/apps/knowbase/services/document_service.py +144 -0
- django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
- django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
- django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
- django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
- django_cfg/apps/knowbase/services/embedding/models.py +229 -0
- django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
- django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
- django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
- django_cfg/apps/knowbase/services/search_service.py +293 -0
- django_cfg/apps/knowbase/signals/__init__.py +21 -0
- django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
- django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
- django_cfg/apps/knowbase/signals/document_signals.py +143 -0
- django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
- django_cfg/apps/knowbase/tasks/__init__.py +39 -0
- django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
- django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
- django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
- django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
- django_cfg/apps/knowbase/urls.py +43 -0
- django_cfg/apps/knowbase/utils/__init__.py +12 -0
- django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
- django_cfg/apps/knowbase/utils/text_processing.py +375 -0
- django_cfg/apps/knowbase/utils/validation.py +99 -0
- django_cfg/apps/knowbase/views/__init__.py +28 -0
- django_cfg/apps/knowbase/views/archive_views.py +469 -0
- django_cfg/apps/knowbase/views/base.py +49 -0
- django_cfg/apps/knowbase/views/chat_views.py +181 -0
- django_cfg/apps/knowbase/views/document_views.py +183 -0
- django_cfg/apps/knowbase/views/public_views.py +129 -0
- django_cfg/apps/leads/admin.py +70 -0
- django_cfg/apps/newsletter/admin.py +234 -0
- django_cfg/apps/newsletter/admin_filters.py +124 -0
- django_cfg/apps/support/admin.py +196 -0
- django_cfg/apps/support/admin_filters.py +71 -0
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
- django_cfg/apps/urls.py +5 -4
- django_cfg/cli/README.md +1 -1
- django_cfg/cli/commands/create_project.py +2 -2
- django_cfg/cli/commands/info.py +1 -1
- django_cfg/config.py +44 -0
- django_cfg/core/config.py +29 -82
- django_cfg/core/environment.py +1 -1
- django_cfg/core/generation.py +19 -107
- django_cfg/{integration.py → core/integration.py} +18 -16
- django_cfg/core/validation.py +1 -1
- django_cfg/management/__init__.py +1 -1
- django_cfg/management/commands/__init__.py +1 -1
- django_cfg/management/commands/auto_generate.py +482 -0
- django_cfg/management/commands/migrator.py +19 -101
- django_cfg/management/commands/test_email.py +1 -1
- django_cfg/middleware/README.md +0 -158
- django_cfg/middleware/__init__.py +0 -2
- django_cfg/middleware/user_activity.py +3 -3
- django_cfg/models/api.py +145 -0
- django_cfg/models/base.py +287 -0
- django_cfg/models/cache.py +4 -4
- django_cfg/models/constance.py +25 -88
- django_cfg/models/database.py +9 -9
- django_cfg/models/drf.py +3 -36
- django_cfg/models/email.py +163 -0
- django_cfg/models/environment.py +276 -0
- django_cfg/models/limits.py +1 -1
- django_cfg/models/logging.py +366 -0
- django_cfg/models/revolution.py +41 -2
- django_cfg/models/security.py +125 -0
- django_cfg/models/services.py +1 -1
- django_cfg/modules/__init__.py +2 -56
- django_cfg/modules/base.py +78 -52
- django_cfg/modules/django_currency/service.py +2 -2
- django_cfg/modules/django_email.py +2 -2
- django_cfg/modules/django_health.py +267 -0
- django_cfg/modules/django_llm/llm/client.py +79 -17
- django_cfg/modules/django_llm/translator/translator.py +2 -2
- django_cfg/modules/django_logger.py +2 -2
- django_cfg/modules/django_ngrok.py +2 -2
- django_cfg/modules/django_tasks.py +68 -3
- django_cfg/modules/django_telegram.py +3 -3
- django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
- django_cfg/modules/django_twilio/service.py +2 -2
- django_cfg/modules/django_twilio/simple_service.py +2 -2
- django_cfg/modules/django_twilio/twilio_service.py +2 -2
- django_cfg/modules/django_unfold/__init__.py +69 -0
- django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
- django_cfg/modules/django_unfold/dashboard.py +278 -0
- django_cfg/modules/django_unfold/icons/README.md +145 -0
- django_cfg/modules/django_unfold/icons/__init__.py +12 -0
- django_cfg/modules/django_unfold/icons/constants.py +2851 -0
- django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
- django_cfg/modules/django_unfold/models/__init__.py +42 -0
- django_cfg/modules/django_unfold/models/config.py +601 -0
- django_cfg/modules/django_unfold/models/dashboard.py +206 -0
- django_cfg/modules/django_unfold/models/dropdown.py +40 -0
- django_cfg/modules/django_unfold/models/navigation.py +73 -0
- django_cfg/modules/django_unfold/models/tabs.py +25 -0
- django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
- django_cfg/modules/django_unfold/utils.py +140 -0
- django_cfg/registry/__init__.py +23 -0
- django_cfg/registry/core.py +61 -0
- django_cfg/registry/exceptions.py +11 -0
- django_cfg/registry/modules.py +12 -0
- django_cfg/registry/services.py +26 -0
- django_cfg/registry/third_party.py +52 -0
- django_cfg/routing/__init__.py +19 -0
- django_cfg/routing/callbacks.py +198 -0
- django_cfg/routing/routers.py +48 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
- django_cfg/templatetags/__init__.py +0 -0
- django_cfg/templatetags/django_cfg.py +33 -0
- django_cfg/urls.py +33 -0
- django_cfg/utils/path_resolution.py +1 -1
- django_cfg/utils/smart_defaults.py +7 -61
- django_cfg/utils/toolkit.py +663 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
- django_cfg-1.2.0.dist-info/RECORD +441 -0
- django_cfg/archive/django_sample.zip +0 -0
- django_cfg/models/unfold.py +0 -271
- django_cfg/modules/unfold/__init__.py +0 -29
- django_cfg/modules/unfold/dashboard.py +0 -318
- django_cfg/pyproject.toml +0 -370
- django_cfg/routers.py +0 -83
- django_cfg-1.1.82.dist-info/RECORD +0 -278
- /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
- /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
- /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
- /django_cfg/{version_check.py → utils/version_check.py} +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
django_cfg/models/constance.py
CHANGED
@@ -9,8 +9,6 @@ from typing import Dict, List, Optional, Any, Union, Literal
|
|
9
9
|
from pydantic import BaseModel, Field, field_validator
|
10
10
|
from pathlib import Path
|
11
11
|
|
12
|
-
from unfold.contrib.constance.settings import UNFOLD_CONSTANCE_ADDITIONAL_FIELDS
|
13
|
-
|
14
12
|
|
15
13
|
class ConstanceField(BaseModel):
|
16
14
|
"""
|
@@ -39,7 +37,7 @@ class ConstanceField(BaseModel):
|
|
39
37
|
max_length=500,
|
40
38
|
)
|
41
39
|
|
42
|
-
field_type: Literal["str", "int", "float", "bool", "choice"
|
40
|
+
field_type: Literal["str", "int", "float", "bool", "choice"] = Field(
|
43
41
|
default="str",
|
44
42
|
description="Field type for form rendering and validation",
|
45
43
|
)
|
@@ -68,21 +66,9 @@ class ConstanceField(BaseModel):
|
|
68
66
|
raise ValueError("Choices must be provided for choice field type")
|
69
67
|
return v
|
70
68
|
|
71
|
-
def to_constance_config(self) -> tuple[Any, str
|
69
|
+
def to_constance_config(self) -> tuple[Any, str]:
|
72
70
|
"""Convert to Constance configuration format."""
|
73
|
-
|
74
|
-
type_mapping = {
|
75
|
-
"str": str,
|
76
|
-
"int": int,
|
77
|
-
"float": float,
|
78
|
-
"bool": int, # Use int for boolean (0/1)
|
79
|
-
"choice": str, # Choices are typically strings
|
80
|
-
"longtext": str, # Long text fields are strings
|
81
|
-
"description": str, # Description fields are strings
|
82
|
-
}
|
83
|
-
|
84
|
-
field_python_type = type_mapping.get(self.field_type, str)
|
85
|
-
return (self.default, self.help_text, field_python_type)
|
71
|
+
return (self.default, self.help_text)
|
86
72
|
|
87
73
|
def to_constance_field_config(self) -> Dict[str, Any]:
|
88
74
|
"""Convert to Constance additional fields configuration."""
|
@@ -109,21 +95,10 @@ class ConstanceField(BaseModel):
|
|
109
95
|
"django.forms.FloatField",
|
110
96
|
{"widget": "unfold.widgets.UnfoldAdminTextInputWidget"},
|
111
97
|
],
|
112
|
-
"
|
113
|
-
"django.forms.
|
114
|
-
{
|
115
|
-
"widget": "unfold.widgets.UnfoldAdminTextareaWidget",
|
116
|
-
"attrs": {"rows": 4, "cols": 80, "class": "max-w-4xl"},
|
117
|
-
},
|
118
|
-
],
|
119
|
-
"description": [
|
120
|
-
"django.forms.CharField",
|
121
|
-
{
|
122
|
-
"widget": "unfold.widgets.UnfoldAdminTextareaWidget",
|
123
|
-
"attrs": {"rows": 3, "cols": 60, "class": "max-w-3xl"},
|
124
|
-
},
|
98
|
+
"bool": [
|
99
|
+
"django.forms.BooleanField",
|
100
|
+
{"widget": "unfold.widgets.UnfoldBooleanSwitchWidget"},
|
125
101
|
],
|
126
|
-
# Boolean fields will use default Unfold configuration from UNFOLD_CONSTANCE_ADDITIONAL_FIELDS
|
127
102
|
}
|
128
103
|
|
129
104
|
return field_mapping.get(self.field_type, field_mapping["str"])
|
@@ -171,12 +146,12 @@ class ConstanceConfig(BaseModel):
|
|
171
146
|
description="List of Constance fields",
|
172
147
|
)
|
173
148
|
|
174
|
-
def get_config_dict(self) -> Dict[str, tuple[Any, str
|
149
|
+
def get_config_dict(self) -> Dict[str, tuple[Any, str]]:
|
175
150
|
"""Generate CONSTANCE_CONFIG dictionary."""
|
176
151
|
return {field.name: field.to_constance_config() for field in self.fields}
|
177
152
|
|
178
|
-
def get_fieldsets_dict(self) -> Dict[str,
|
179
|
-
"""Generate
|
153
|
+
def get_fieldsets_dict(self) -> Dict[str, List[str]]:
|
154
|
+
"""Generate CONSTANCE_FIELDSETS dictionary grouped by field groups."""
|
180
155
|
fieldsets = {}
|
181
156
|
|
182
157
|
for field in self.fields:
|
@@ -185,60 +160,19 @@ class ConstanceConfig(BaseModel):
|
|
185
160
|
fieldsets[group] = []
|
186
161
|
fieldsets[group].append(field.name)
|
187
162
|
|
188
|
-
|
189
|
-
return {group: tuple(fields) for group, fields in fieldsets.items()}
|
163
|
+
return fieldsets
|
190
164
|
|
191
165
|
def get_additional_fields_dict(self) -> Dict[str, List[Any]]:
|
192
|
-
"""Generate CONSTANCE_ADDITIONAL_FIELDS dictionary
|
166
|
+
"""Generate CONSTANCE_ADDITIONAL_FIELDS dictionary."""
|
167
|
+
# Start with Unfold base fields
|
193
168
|
from unfold.contrib.constance.settings import UNFOLD_CONSTANCE_ADDITIONAL_FIELDS
|
194
169
|
|
195
170
|
additional_fields = dict(UNFOLD_CONSTANCE_ADDITIONAL_FIELDS)
|
196
171
|
|
197
|
-
# Add custom field configurations
|
198
|
-
additional_fields.update({
|
199
|
-
"text": [
|
200
|
-
"django.forms.CharField",
|
201
|
-
{
|
202
|
-
"widget": "unfold.widgets.UnfoldAdminTextInputWidget",
|
203
|
-
"attrs": {"class": "max-w-2xl"},
|
204
|
-
},
|
205
|
-
],
|
206
|
-
"longtext": [
|
207
|
-
"django.forms.CharField",
|
208
|
-
{
|
209
|
-
"widget": "unfold.widgets.UnfoldAdminTextareaWidget",
|
210
|
-
"attrs": {"rows": 4, "cols": 80, "class": "max-w-4xl"},
|
211
|
-
},
|
212
|
-
],
|
213
|
-
"description": [
|
214
|
-
"django.forms.CharField",
|
215
|
-
{
|
216
|
-
"widget": "unfold.widgets.UnfoldAdminTextareaWidget",
|
217
|
-
"attrs": {"rows": 3, "cols": 60, "class": "max-w-3xl"},
|
218
|
-
},
|
219
|
-
],
|
220
|
-
})
|
221
|
-
|
222
|
-
# Override specific fields with custom widgets
|
172
|
+
# Add custom field configurations
|
223
173
|
for field in self.fields:
|
224
|
-
if field.field_type == "choice"
|
225
|
-
additional_fields[field.name] = field.to_constance_field_config()
|
226
|
-
elif field.field_type in ["longtext", "description"]:
|
227
|
-
# Use field-specific configuration to override Unfold defaults
|
228
|
-
additional_fields[field.name] = field.to_constance_field_config()
|
229
|
-
|
230
|
-
# CRITICAL FIX: Override fields that should use textarea based on field_type
|
231
|
-
# This ensures longtext and description fields use textarea regardless of their Python type
|
232
|
-
for field in self.fields:
|
233
|
-
if field.field_type in ["longtext", "description"]:
|
234
|
-
# Force textarea widget for these field types, overriding any type-based defaults
|
235
|
-
additional_fields[field.name] = [
|
236
|
-
"django.forms.CharField",
|
237
|
-
{
|
238
|
-
"widget": "unfold.widgets.UnfoldAdminTextareaWidget",
|
239
|
-
"attrs": field.to_constance_field_config()[1]["attrs"],
|
240
|
-
},
|
241
|
-
]
|
174
|
+
if field.field_type == "choice" or field.choices:
|
175
|
+
additional_fields[f"{field.name.lower()}_field"] = field.to_constance_field_config()
|
242
176
|
|
243
177
|
return additional_fields
|
244
178
|
|
@@ -247,23 +181,26 @@ class ConstanceConfig(BaseModel):
|
|
247
181
|
if not self.fields:
|
248
182
|
return {}
|
249
183
|
|
250
|
-
config_dict = self.get_config_dict()
|
251
|
-
fieldsets_dict = self.get_fieldsets_dict()
|
252
|
-
additional_fields_dict = self.get_additional_fields_dict()
|
253
|
-
|
254
184
|
settings = {
|
255
|
-
|
256
|
-
"
|
185
|
+
# Main configuration
|
186
|
+
"CONSTANCE_CONFIG": self.get_config_dict(),
|
187
|
+
"CONSTANCE_FIELDSETS": self.get_fieldsets_dict(),
|
188
|
+
# Backend settings (using default database backend)
|
257
189
|
"CONSTANCE_BACKEND": "constance.backends.database.DatabaseBackend",
|
190
|
+
# Cache settings
|
258
191
|
"CONSTANCE_DATABASE_CACHE_BACKEND": self.database_cache_backend,
|
259
192
|
"CONSTANCE_DATABASE_PREFIX": self.cache_prefix,
|
260
193
|
"CONSTANCE_DATABASE_CACHE_AUTOFILL_TIMEOUT": self.cache_autofill_timeout,
|
194
|
+
# Redis settings (if using Redis backend)
|
261
195
|
"CONSTANCE_REDIS_CONNECTION_CLASS": self.redis_connection_class,
|
262
196
|
"CONSTANCE_REDIS_PREFIX": self.redis_prefix,
|
263
|
-
|
197
|
+
# Additional fields with Unfold widgets
|
198
|
+
"CONSTANCE_ADDITIONAL_FIELDS": self.get_additional_fields_dict(),
|
199
|
+
# Ignore admin version check
|
264
200
|
"CONSTANCE_IGNORE_ADMIN_VERSION_CHECK": True,
|
265
201
|
}
|
266
202
|
|
203
|
+
# Remove None values
|
267
204
|
return {k: v for k, v in settings.items() if v is not None}
|
268
205
|
|
269
206
|
|
django_cfg/models/database.py
CHANGED
@@ -13,10 +13,10 @@ from pathlib import Path
|
|
13
13
|
from pydantic import BaseModel, Field, field_validator, model_validator, PrivateAttr
|
14
14
|
from urllib.parse import urlparse
|
15
15
|
|
16
|
-
from django_cfg.exceptions import DatabaseError, ValidationError
|
16
|
+
from django_cfg.core.exceptions import DatabaseError, ValidationError
|
17
17
|
|
18
18
|
|
19
|
-
class
|
19
|
+
class DatabaseConfig(BaseModel):
|
20
20
|
"""
|
21
21
|
Type-safe database connection configuration.
|
22
22
|
|
@@ -193,7 +193,7 @@ class DatabaseConnection(BaseModel):
|
|
193
193
|
return values
|
194
194
|
|
195
195
|
@model_validator(mode="after")
|
196
|
-
def validate_connection_after(self) -> "
|
196
|
+
def validate_connection_after(self) -> "DatabaseConfig":
|
197
197
|
"""Validate connection after model creation."""
|
198
198
|
# Parse connection string if present
|
199
199
|
if "://" in self.name:
|
@@ -440,9 +440,9 @@ class DatabaseConnection(BaseModel):
|
|
440
440
|
operations: Optional[List[Literal["read", "write", "migrate"]]] = None,
|
441
441
|
routing_description: str = "",
|
442
442
|
**kwargs
|
443
|
-
) -> "
|
443
|
+
) -> "DatabaseConfig":
|
444
444
|
"""
|
445
|
-
Create
|
445
|
+
Create DatabaseConfig from URL with automatic engine detection.
|
446
446
|
|
447
447
|
Args:
|
448
448
|
url: Database URL (e.g., 'postgresql://user:pass@host:port/db')
|
@@ -452,14 +452,14 @@ class DatabaseConnection(BaseModel):
|
|
452
452
|
**kwargs: Additional parameters to override defaults
|
453
453
|
|
454
454
|
Returns:
|
455
|
-
|
455
|
+
DatabaseConfig instance with auto-detected engine
|
456
456
|
|
457
457
|
Example:
|
458
458
|
# Simple SQLite database
|
459
|
-
db =
|
459
|
+
db = DatabaseConfig.from_url("sqlite:///db.sqlite3")
|
460
460
|
|
461
461
|
# PostgreSQL with routing
|
462
|
-
blog_db =
|
462
|
+
blog_db = DatabaseConfig.from_url(
|
463
463
|
"postgresql://user:pass@localhost:5432/blog",
|
464
464
|
apps=["apps.blog"],
|
465
465
|
routing_description="Blog posts and comments"
|
@@ -476,5 +476,5 @@ class DatabaseConnection(BaseModel):
|
|
476
476
|
|
477
477
|
# Export all models
|
478
478
|
__all__ = [
|
479
|
-
"
|
479
|
+
"DatabaseConfig",
|
480
480
|
]
|
django_cfg/models/drf.py
CHANGED
@@ -103,18 +103,7 @@ class SpectacularConfig(BaseModel):
|
|
103
103
|
# Schema Settings
|
104
104
|
schema_path_prefix: str = Field(default="/api", description="Schema path prefix")
|
105
105
|
serve_include_schema: bool = Field(default=False, description="Include schema in UI")
|
106
|
-
|
107
|
-
schema_coerce_method_names: Dict[str, str] = Field(
|
108
|
-
default_factory=lambda: {
|
109
|
-
"retrieve": "get",
|
110
|
-
"destroy": "delete",
|
111
|
-
"partial_update": "patch",
|
112
|
-
"update": "put",
|
113
|
-
},
|
114
|
-
description="Coerce method names for stable operation IDs"
|
115
|
-
)
|
116
|
-
# component_split_request: bool = Field(default=False, description="Split request components")
|
117
|
-
# component_no_read_only_required: bool = Field(default=True, description="No read-only required")
|
106
|
+
component_split_request: bool = Field(default=True, description="Split request components")
|
118
107
|
sort_operations: bool = Field(default=False, description="Sort operations")
|
119
108
|
|
120
109
|
# UI Settings
|
@@ -125,24 +114,13 @@ class SpectacularConfig(BaseModel):
|
|
125
114
|
default_factory=RedocUISettings, description="Redoc UI settings"
|
126
115
|
)
|
127
116
|
|
128
|
-
#
|
117
|
+
# Post-processing
|
129
118
|
postprocessing_hooks: List[str] = Field(
|
130
119
|
default_factory=lambda: [
|
131
120
|
'drf_spectacular.contrib.djangorestframework_camel_case.camelize_serializer_fields'
|
132
121
|
],
|
133
122
|
description="Post-processing hooks"
|
134
123
|
)
|
135
|
-
preprocessing_hooks: List[str] = Field(
|
136
|
-
default_factory=list,
|
137
|
-
description="Pre-processing hooks"
|
138
|
-
)
|
139
|
-
|
140
|
-
# Additional stability settings
|
141
|
-
disable_errors_and_warnings: bool = Field(default=False, description="Disable errors and warnings")
|
142
|
-
operation_id_generator_class: Optional[str] = Field(
|
143
|
-
default=None,
|
144
|
-
description="Custom operation ID generator class"
|
145
|
-
)
|
146
124
|
|
147
125
|
# Enum overrides
|
148
126
|
enum_name_overrides: Dict[str, str] = Field(
|
@@ -161,27 +139,16 @@ class SpectacularConfig(BaseModel):
|
|
161
139
|
"VERSION": self.version,
|
162
140
|
"SERVE_INCLUDE_SCHEMA": self.serve_include_schema,
|
163
141
|
"SCHEMA_PATH_PREFIX": self.schema_path_prefix,
|
164
|
-
|
165
|
-
"SCHEMA_COERCE_PATH_PK_SUFFIX": self.schema_coerce_path_pk_suffix,
|
166
|
-
"SCHEMA_COERCE_METHOD_NAMES": self.schema_coerce_method_names,
|
167
|
-
'COMPONENT_SPLIT_REQUEST': False,
|
168
|
-
'COMPONENT_NO_READ_ONLY_REQUIRED': False,
|
142
|
+
"COMPONENT_SPLIT_REQUEST": self.component_split_request,
|
169
143
|
"SORT_OPERATIONS": self.sort_operations,
|
170
144
|
# UI Settings
|
171
145
|
"SWAGGER_UI_SETTINGS": self.swagger_ui_settings.to_dict(),
|
172
146
|
"REDOC_UI_SETTINGS": self.redoc_ui_settings.to_dict(),
|
173
147
|
# Processing
|
174
148
|
"POSTPROCESSING_HOOKS": self.postprocessing_hooks,
|
175
|
-
"PREPROCESSING_HOOKS": self.preprocessing_hooks,
|
176
149
|
"ENUM_NAME_OVERRIDES": self.enum_name_overrides,
|
177
|
-
# Stability settings
|
178
|
-
"DISABLE_ERRORS_AND_WARNINGS": self.disable_errors_and_warnings,
|
179
150
|
}
|
180
151
|
|
181
|
-
# Add optional operation ID generator
|
182
|
-
if self.operation_id_generator_class:
|
183
|
-
settings["OPERATION_ID_GENERATOR_CLASS"] = self.operation_id_generator_class
|
184
|
-
|
185
152
|
# Add optional fields if present
|
186
153
|
if self.terms_of_service:
|
187
154
|
settings["TERMS_OF_SERVICE"] = self.terms_of_service
|
@@ -0,0 +1,163 @@
|
|
1
|
+
"""
|
2
|
+
Email Configuration Model
|
3
|
+
|
4
|
+
Django email settings with Pydantic 2.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Dict, Any, Optional
|
8
|
+
from pydantic import Field, field_validator
|
9
|
+
from .base import BaseConfig
|
10
|
+
|
11
|
+
|
12
|
+
class EmailConfig(BaseConfig):
|
13
|
+
"""
|
14
|
+
📧 Email Configuration - Django email settings
|
15
|
+
|
16
|
+
Supports SMTP, console, file, and other email backends
|
17
|
+
with environment-aware defaults.
|
18
|
+
"""
|
19
|
+
|
20
|
+
# Email backend
|
21
|
+
backend: str = Field(
|
22
|
+
default="console",
|
23
|
+
description="Email backend (smtp/console/file/memory)"
|
24
|
+
)
|
25
|
+
|
26
|
+
# SMTP settings
|
27
|
+
host: str = Field(
|
28
|
+
default="localhost",
|
29
|
+
description="SMTP server host"
|
30
|
+
)
|
31
|
+
|
32
|
+
port: int = Field(
|
33
|
+
default=587,
|
34
|
+
ge=1,
|
35
|
+
le=65535,
|
36
|
+
description="SMTP server port"
|
37
|
+
)
|
38
|
+
|
39
|
+
username: Optional[str] = Field(
|
40
|
+
default=None,
|
41
|
+
description="SMTP username"
|
42
|
+
)
|
43
|
+
|
44
|
+
password: Optional[str] = Field(
|
45
|
+
default=None,
|
46
|
+
description="SMTP password"
|
47
|
+
)
|
48
|
+
|
49
|
+
use_tls: bool = Field(
|
50
|
+
default=True,
|
51
|
+
description="Use TLS for SMTP"
|
52
|
+
)
|
53
|
+
|
54
|
+
use_ssl: bool = Field(
|
55
|
+
default=False,
|
56
|
+
description="Use SSL for SMTP"
|
57
|
+
)
|
58
|
+
|
59
|
+
timeout: int = Field(
|
60
|
+
default=30,
|
61
|
+
ge=1,
|
62
|
+
le=300,
|
63
|
+
description="SMTP timeout in seconds"
|
64
|
+
)
|
65
|
+
|
66
|
+
# Email addresses
|
67
|
+
default_from: str = Field(
|
68
|
+
default="noreply@example.com",
|
69
|
+
description="Default 'from' email address"
|
70
|
+
)
|
71
|
+
|
72
|
+
admin_email: Optional[str] = Field(
|
73
|
+
default=None,
|
74
|
+
description="Admin email for error notifications"
|
75
|
+
)
|
76
|
+
|
77
|
+
# File backend settings
|
78
|
+
file_path: str = Field(
|
79
|
+
default="emails/",
|
80
|
+
description="Path for file-based email backend"
|
81
|
+
)
|
82
|
+
|
83
|
+
@field_validator('backend')
|
84
|
+
@classmethod
|
85
|
+
def validate_backend(cls, v: str) -> str:
|
86
|
+
"""Validate email backend."""
|
87
|
+
valid_backends = ['smtp', 'console', 'file', 'memory', 'dummy']
|
88
|
+
if v not in valid_backends:
|
89
|
+
raise ValueError(f"Email backend must be one of: {valid_backends}")
|
90
|
+
return v
|
91
|
+
|
92
|
+
@field_validator('default_from', 'admin_email')
|
93
|
+
@classmethod
|
94
|
+
def validate_email(cls, v: Optional[str]) -> Optional[str]:
|
95
|
+
"""Validate email address format."""
|
96
|
+
if v and '@' not in v:
|
97
|
+
raise ValueError("Invalid email address format")
|
98
|
+
return v
|
99
|
+
|
100
|
+
@field_validator('use_tls', 'use_ssl', mode='after')
|
101
|
+
@classmethod
|
102
|
+
def validate_tls_ssl(cls, v, info):
|
103
|
+
"""Ensure TLS and SSL are not both enabled."""
|
104
|
+
data = info.data
|
105
|
+
if data.get('use_tls') and data.get('use_ssl'):
|
106
|
+
raise ValueError("Cannot use both TLS and SSL simultaneously")
|
107
|
+
return v
|
108
|
+
|
109
|
+
def to_django_settings(self) -> Dict[str, Any]:
|
110
|
+
"""Convert to Django email settings."""
|
111
|
+
if self.backend == 'smtp':
|
112
|
+
backend_class = 'django.core.mail.backends.smtp.EmailBackend'
|
113
|
+
|
114
|
+
settings = {
|
115
|
+
'EMAIL_BACKEND': backend_class,
|
116
|
+
'EMAIL_HOST': self.host,
|
117
|
+
'EMAIL_PORT': self.port,
|
118
|
+
'EMAIL_USE_TLS': self.use_tls,
|
119
|
+
'EMAIL_USE_SSL': self.use_ssl,
|
120
|
+
'EMAIL_TIMEOUT': self.timeout,
|
121
|
+
'DEFAULT_FROM_EMAIL': self.default_from,
|
122
|
+
}
|
123
|
+
|
124
|
+
if self.username:
|
125
|
+
settings['EMAIL_HOST_USER'] = self.username
|
126
|
+
|
127
|
+
if self.password:
|
128
|
+
settings['EMAIL_HOST_PASSWORD'] = self.password
|
129
|
+
|
130
|
+
elif self.backend == 'console':
|
131
|
+
settings = {
|
132
|
+
'EMAIL_BACKEND': 'django.core.mail.backends.console.EmailBackend',
|
133
|
+
'DEFAULT_FROM_EMAIL': self.default_from,
|
134
|
+
}
|
135
|
+
|
136
|
+
elif self.backend == 'file':
|
137
|
+
settings = {
|
138
|
+
'EMAIL_BACKEND': 'django.core.mail.backends.filebased.EmailBackend',
|
139
|
+
'EMAIL_FILE_PATH': self.file_path,
|
140
|
+
'DEFAULT_FROM_EMAIL': self.default_from,
|
141
|
+
}
|
142
|
+
|
143
|
+
elif self.backend == 'memory':
|
144
|
+
settings = {
|
145
|
+
'EMAIL_BACKEND': 'django.core.mail.backends.locmem.EmailBackend',
|
146
|
+
'DEFAULT_FROM_EMAIL': self.default_from,
|
147
|
+
}
|
148
|
+
|
149
|
+
elif self.backend == 'dummy':
|
150
|
+
settings = {
|
151
|
+
'EMAIL_BACKEND': 'django.core.mail.backends.dummy.EmailBackend',
|
152
|
+
'DEFAULT_FROM_EMAIL': self.default_from,
|
153
|
+
}
|
154
|
+
|
155
|
+
else:
|
156
|
+
raise ValueError(f"Unsupported email backend: {self.backend}")
|
157
|
+
|
158
|
+
# Add admin email if provided
|
159
|
+
if self.admin_email:
|
160
|
+
settings['ADMINS'] = [('Admin', self.admin_email)]
|
161
|
+
settings['MANAGERS'] = [('Manager', self.admin_email)]
|
162
|
+
|
163
|
+
return settings
|