django-cfg 1.1.82__py3-none-any.whl → 1.2.1__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.
Files changed (244) hide show
  1. django_cfg/__init__.py +20 -448
  2. django_cfg/apps/accounts/README.md +3 -3
  3. django_cfg/apps/accounts/admin/__init__.py +0 -2
  4. django_cfg/apps/accounts/admin/activity.py +2 -9
  5. django_cfg/apps/accounts/admin/filters.py +0 -42
  6. django_cfg/apps/accounts/admin/inlines.py +8 -8
  7. django_cfg/apps/accounts/admin/otp.py +5 -5
  8. django_cfg/apps/accounts/admin/registration_source.py +1 -8
  9. django_cfg/apps/accounts/admin/user.py +12 -20
  10. django_cfg/apps/accounts/managers/user_manager.py +2 -129
  11. django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
  12. django_cfg/apps/accounts/models.py +3 -123
  13. django_cfg/apps/accounts/serializers/otp.py +40 -44
  14. django_cfg/apps/accounts/serializers/profile.py +0 -2
  15. django_cfg/apps/accounts/services/otp_service.py +98 -186
  16. django_cfg/apps/accounts/signals.py +25 -15
  17. django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
  18. django_cfg/apps/accounts/views/otp.py +35 -36
  19. django_cfg/apps/agents/README.md +129 -0
  20. django_cfg/apps/agents/__init__.py +68 -0
  21. django_cfg/apps/agents/admin/__init__.py +17 -0
  22. django_cfg/apps/agents/admin/execution_admin.py +460 -0
  23. django_cfg/apps/agents/admin/registry_admin.py +360 -0
  24. django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
  25. django_cfg/apps/agents/apps.py +29 -0
  26. django_cfg/apps/agents/core/__init__.py +20 -0
  27. django_cfg/apps/agents/core/agent.py +281 -0
  28. django_cfg/apps/agents/core/dependencies.py +154 -0
  29. django_cfg/apps/agents/core/exceptions.py +66 -0
  30. django_cfg/apps/agents/core/models.py +106 -0
  31. django_cfg/apps/agents/core/orchestrator.py +391 -0
  32. django_cfg/apps/agents/examples/__init__.py +3 -0
  33. django_cfg/apps/agents/examples/simple_example.py +161 -0
  34. django_cfg/apps/agents/integration/__init__.py +14 -0
  35. django_cfg/apps/agents/integration/middleware.py +80 -0
  36. django_cfg/apps/agents/integration/registry.py +345 -0
  37. django_cfg/apps/agents/integration/signals.py +50 -0
  38. django_cfg/apps/agents/management/__init__.py +3 -0
  39. django_cfg/apps/agents/management/commands/__init__.py +3 -0
  40. django_cfg/apps/agents/management/commands/create_agent.py +365 -0
  41. django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
  42. django_cfg/apps/agents/managers/__init__.py +23 -0
  43. django_cfg/apps/agents/managers/execution.py +236 -0
  44. django_cfg/apps/agents/managers/registry.py +254 -0
  45. django_cfg/apps/agents/managers/toolsets.py +496 -0
  46. django_cfg/apps/agents/migrations/0001_initial.py +286 -0
  47. django_cfg/apps/agents/migrations/__init__.py +5 -0
  48. django_cfg/apps/agents/models/__init__.py +15 -0
  49. django_cfg/apps/agents/models/execution.py +215 -0
  50. django_cfg/apps/agents/models/registry.py +220 -0
  51. django_cfg/apps/agents/models/toolsets.py +305 -0
  52. django_cfg/apps/agents/patterns/__init__.py +24 -0
  53. django_cfg/apps/agents/patterns/content_agents.py +234 -0
  54. django_cfg/apps/agents/toolsets/__init__.py +15 -0
  55. django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
  56. django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
  57. django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
  58. django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
  59. django_cfg/apps/agents/urls.py +46 -0
  60. django_cfg/apps/knowbase/README.md +150 -0
  61. django_cfg/apps/knowbase/__init__.py +27 -0
  62. django_cfg/apps/knowbase/admin/__init__.py +23 -0
  63. django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
  64. django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
  65. django_cfg/apps/knowbase/admin/document_admin.py +650 -0
  66. django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
  67. django_cfg/apps/knowbase/apps.py +81 -0
  68. django_cfg/apps/knowbase/config/README.md +176 -0
  69. django_cfg/apps/knowbase/config/__init__.py +51 -0
  70. django_cfg/apps/knowbase/config/constance_fields.py +186 -0
  71. django_cfg/apps/knowbase/config/constance_settings.py +200 -0
  72. django_cfg/apps/knowbase/config/settings.py +450 -0
  73. django_cfg/apps/knowbase/examples/__init__.py +3 -0
  74. django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
  75. django_cfg/apps/knowbase/management/__init__.py +0 -0
  76. django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
  77. django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
  78. django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
  79. django_cfg/apps/knowbase/managers/__init__.py +22 -0
  80. django_cfg/apps/knowbase/managers/archive.py +426 -0
  81. django_cfg/apps/knowbase/managers/base.py +32 -0
  82. django_cfg/apps/knowbase/managers/chat.py +141 -0
  83. django_cfg/apps/knowbase/managers/document.py +203 -0
  84. django_cfg/apps/knowbase/managers/external_data.py +471 -0
  85. django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
  86. django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
  87. django_cfg/apps/knowbase/migrations/__init__.py +5 -0
  88. django_cfg/apps/knowbase/mixins/__init__.py +15 -0
  89. django_cfg/apps/knowbase/mixins/config.py +108 -0
  90. django_cfg/apps/knowbase/mixins/creator.py +81 -0
  91. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
  92. django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
  93. django_cfg/apps/knowbase/mixins/service.py +362 -0
  94. django_cfg/apps/knowbase/models/__init__.py +41 -0
  95. django_cfg/apps/knowbase/models/archive.py +599 -0
  96. django_cfg/apps/knowbase/models/base.py +58 -0
  97. django_cfg/apps/knowbase/models/chat.py +157 -0
  98. django_cfg/apps/knowbase/models/document.py +267 -0
  99. django_cfg/apps/knowbase/models/external_data.py +376 -0
  100. django_cfg/apps/knowbase/serializers/__init__.py +68 -0
  101. django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
  102. django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
  103. django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
  104. django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
  105. django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
  106. django_cfg/apps/knowbase/services/__init__.py +40 -0
  107. django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
  108. django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
  109. django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
  110. django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
  111. django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
  112. django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
  113. django_cfg/apps/knowbase/services/base.py +53 -0
  114. django_cfg/apps/knowbase/services/chat_service.py +239 -0
  115. django_cfg/apps/knowbase/services/document_service.py +144 -0
  116. django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
  117. django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
  118. django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
  119. django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
  120. django_cfg/apps/knowbase/services/embedding/models.py +229 -0
  121. django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
  122. django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
  123. django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
  124. django_cfg/apps/knowbase/services/search_service.py +293 -0
  125. django_cfg/apps/knowbase/signals/__init__.py +21 -0
  126. django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
  127. django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
  128. django_cfg/apps/knowbase/signals/document_signals.py +143 -0
  129. django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
  130. django_cfg/apps/knowbase/tasks/__init__.py +39 -0
  131. django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
  132. django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
  133. django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
  134. django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
  135. django_cfg/apps/knowbase/urls.py +43 -0
  136. django_cfg/apps/knowbase/utils/__init__.py +12 -0
  137. django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
  138. django_cfg/apps/knowbase/utils/text_processing.py +375 -0
  139. django_cfg/apps/knowbase/utils/validation.py +99 -0
  140. django_cfg/apps/knowbase/views/__init__.py +28 -0
  141. django_cfg/apps/knowbase/views/archive_views.py +469 -0
  142. django_cfg/apps/knowbase/views/base.py +49 -0
  143. django_cfg/apps/knowbase/views/chat_views.py +181 -0
  144. django_cfg/apps/knowbase/views/document_views.py +183 -0
  145. django_cfg/apps/knowbase/views/public_views.py +129 -0
  146. django_cfg/apps/leads/admin.py +70 -0
  147. django_cfg/apps/newsletter/admin.py +234 -0
  148. django_cfg/apps/newsletter/admin_filters.py +124 -0
  149. django_cfg/apps/support/admin.py +196 -0
  150. django_cfg/apps/support/admin_filters.py +71 -0
  151. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  152. django_cfg/apps/urls.py +5 -4
  153. django_cfg/cli/README.md +1 -1
  154. django_cfg/cli/commands/create_project.py +2 -2
  155. django_cfg/cli/commands/info.py +1 -1
  156. django_cfg/config.py +44 -0
  157. django_cfg/core/config.py +29 -82
  158. django_cfg/core/environment.py +1 -1
  159. django_cfg/core/generation.py +19 -107
  160. django_cfg/{integration.py → core/integration.py} +18 -16
  161. django_cfg/core/validation.py +1 -1
  162. django_cfg/management/__init__.py +1 -1
  163. django_cfg/management/commands/__init__.py +1 -1
  164. django_cfg/management/commands/auto_generate.py +482 -0
  165. django_cfg/management/commands/migrator.py +19 -101
  166. django_cfg/management/commands/test_email.py +1 -1
  167. django_cfg/middleware/README.md +0 -158
  168. django_cfg/middleware/__init__.py +0 -2
  169. django_cfg/middleware/user_activity.py +3 -3
  170. django_cfg/models/api.py +145 -0
  171. django_cfg/models/base.py +287 -0
  172. django_cfg/models/cache.py +4 -4
  173. django_cfg/models/constance.py +25 -88
  174. django_cfg/models/database.py +9 -9
  175. django_cfg/models/drf.py +3 -36
  176. django_cfg/models/email.py +163 -0
  177. django_cfg/models/environment.py +276 -0
  178. django_cfg/models/limits.py +1 -1
  179. django_cfg/models/logging.py +366 -0
  180. django_cfg/models/revolution.py +41 -2
  181. django_cfg/models/security.py +125 -0
  182. django_cfg/models/services.py +1 -1
  183. django_cfg/modules/__init__.py +2 -56
  184. django_cfg/modules/base.py +78 -52
  185. django_cfg/modules/django_currency/service.py +2 -2
  186. django_cfg/modules/django_email.py +2 -2
  187. django_cfg/modules/django_health.py +267 -0
  188. django_cfg/modules/django_llm/llm/client.py +91 -19
  189. django_cfg/modules/django_llm/translator/translator.py +2 -2
  190. django_cfg/modules/django_logger.py +2 -2
  191. django_cfg/modules/django_ngrok.py +2 -2
  192. django_cfg/modules/django_tasks.py +68 -3
  193. django_cfg/modules/django_telegram.py +3 -3
  194. django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
  195. django_cfg/modules/django_twilio/service.py +2 -2
  196. django_cfg/modules/django_twilio/simple_service.py +2 -2
  197. django_cfg/modules/django_twilio/twilio_service.py +2 -2
  198. django_cfg/modules/django_unfold/__init__.py +69 -0
  199. django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
  200. django_cfg/modules/django_unfold/dashboard.py +278 -0
  201. django_cfg/modules/django_unfold/icons/README.md +145 -0
  202. django_cfg/modules/django_unfold/icons/__init__.py +12 -0
  203. django_cfg/modules/django_unfold/icons/constants.py +2851 -0
  204. django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
  205. django_cfg/modules/django_unfold/models/__init__.py +42 -0
  206. django_cfg/modules/django_unfold/models/config.py +601 -0
  207. django_cfg/modules/django_unfold/models/dashboard.py +206 -0
  208. django_cfg/modules/django_unfold/models/dropdown.py +40 -0
  209. django_cfg/modules/django_unfold/models/navigation.py +73 -0
  210. django_cfg/modules/django_unfold/models/tabs.py +25 -0
  211. django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
  212. django_cfg/modules/django_unfold/utils.py +140 -0
  213. django_cfg/registry/__init__.py +23 -0
  214. django_cfg/registry/core.py +61 -0
  215. django_cfg/registry/exceptions.py +11 -0
  216. django_cfg/registry/modules.py +12 -0
  217. django_cfg/registry/services.py +26 -0
  218. django_cfg/registry/third_party.py +52 -0
  219. django_cfg/routing/__init__.py +19 -0
  220. django_cfg/routing/callbacks.py +198 -0
  221. django_cfg/routing/routers.py +48 -0
  222. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
  223. django_cfg/templatetags/__init__.py +0 -0
  224. django_cfg/templatetags/django_cfg.py +33 -0
  225. django_cfg/urls.py +33 -0
  226. django_cfg/utils/path_resolution.py +1 -1
  227. django_cfg/utils/smart_defaults.py +7 -61
  228. django_cfg/utils/toolkit.py +663 -0
  229. {django_cfg-1.1.82.dist-info → django_cfg-1.2.1.dist-info}/METADATA +83 -86
  230. django_cfg-1.2.1.dist-info/RECORD +441 -0
  231. django_cfg/archive/django_sample.zip +0 -0
  232. django_cfg/models/unfold.py +0 -271
  233. django_cfg/modules/unfold/__init__.py +0 -29
  234. django_cfg/modules/unfold/dashboard.py +0 -318
  235. django_cfg/pyproject.toml +0 -370
  236. django_cfg/routers.py +0 -83
  237. django_cfg-1.1.82.dist-info/RECORD +0 -278
  238. /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
  239. /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
  240. /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
  241. /django_cfg/{version_check.py → utils/version_check.py} +0 -0
  242. {django_cfg-1.1.82.dist-info → django_cfg-1.2.1.dist-info}/WHEEL +0 -0
  243. {django_cfg-1.1.82.dist-info → django_cfg-1.2.1.dist-info}/entry_points.txt +0 -0
  244. {django_cfg-1.1.82.dist-info → django_cfg-1.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -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", "longtext", "description"] = Field(
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, type]:
69
+ def to_constance_config(self) -> tuple[Any, str]:
72
70
  """Convert to Constance configuration format."""
73
- # Map field types to Python types
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
- "longtext": [
113
- "django.forms.CharField",
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, type]]:
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, tuple]:
179
- """Generate CONSTANCE_CONFIG_FIELDSETS dictionary grouped by field groups."""
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
- # Convert lists to tuples as required by Constance
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 with enhanced widgets."""
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 for better text handling
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" and field.choices:
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
- "CONSTANCE_CONFIG": config_dict,
256
- "CONSTANCE_CONFIG_FIELDSETS": fieldsets_dict,
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
- "CONSTANCE_ADDITIONAL_FIELDS": additional_fields_dict,
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
 
@@ -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 DatabaseConnection(BaseModel):
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) -> "DatabaseConnection":
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
- ) -> "DatabaseConnection":
443
+ ) -> "DatabaseConfig":
444
444
  """
445
- Create DatabaseConnection from URL with automatic engine detection.
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
- DatabaseConnection instance with auto-detected engine
455
+ DatabaseConfig instance with auto-detected engine
456
456
 
457
457
  Example:
458
458
  # Simple SQLite database
459
- db = DatabaseConnection.from_url("sqlite:///db.sqlite3")
459
+ db = DatabaseConfig.from_url("sqlite:///db.sqlite3")
460
460
 
461
461
  # PostgreSQL with routing
462
- blog_db = DatabaseConnection.from_url(
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
- "DatabaseConnection",
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
- schema_coerce_path_pk_suffix: bool = Field(default=True, description="Coerce path PK suffix")
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
- # Processing hooks
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
- # Schema stability settings
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