django-cfg 1.4.109__py3-none-any.whl → 1.4.110__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 +1 -1
- django_cfg/modules/django_admin/__init__.py +6 -0
- django_cfg/modules/django_admin/base/pydantic_admin.py +91 -0
- django_cfg/modules/django_admin/config/__init__.py +5 -0
- django_cfg/modules/django_admin/config/admin_config.py +7 -0
- django_cfg/modules/django_admin/config/documentation_config.py +406 -0
- django_cfg/modules/django_admin/config/field_config.py +87 -0
- django_cfg/modules/django_admin/templates/django_admin/change_form_docs.html +23 -0
- django_cfg/modules/django_admin/templates/django_admin/change_list_docs.html +23 -0
- django_cfg/modules/django_admin/templates/django_admin/documentation_block.html +297 -0
- django_cfg/modules/django_admin/templates/django_admin/markdown_docs_block.html +37 -0
- django_cfg/modules/django_admin/utils/__init__.py +3 -0
- django_cfg/modules/django_admin/utils/html_builder.py +94 -1
- django_cfg/modules/django_admin/utils/markdown_renderer.py +344 -0
- django_cfg/pyproject.toml +2 -2
- {django_cfg-1.4.109.dist-info → django_cfg-1.4.110.dist-info}/METADATA +2 -1
- {django_cfg-1.4.109.dist-info → django_cfg-1.4.110.dist-info}/RECORD +20 -17
- django_cfg/modules/django_admin/CHANGELOG_CODE_METHODS.md +0 -153
- django_cfg/modules/django_admin/IMPORT_EXPORT_FIX.md +0 -72
- django_cfg/modules/django_admin/RESOURCE_CONFIG_ENHANCEMENT.md +0 -350
- {django_cfg-1.4.109.dist-info → django_cfg-1.4.110.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.109.dist-info → django_cfg-1.4.110.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.109.dist-info → django_cfg-1.4.110.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Markdown rendering service for Django Admin.
|
|
3
|
+
|
|
4
|
+
Provides utilities for rendering markdown content from strings or files
|
|
5
|
+
with beautiful Tailwind CSS styling and collapsible sections.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Optional, Union
|
|
12
|
+
|
|
13
|
+
from django.utils.html import escape, format_html
|
|
14
|
+
from django.utils.safestring import SafeString, mark_safe
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import mistune
|
|
20
|
+
MISTUNE_AVAILABLE = True
|
|
21
|
+
except ImportError:
|
|
22
|
+
MISTUNE_AVAILABLE = False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MarkdownRenderer:
|
|
26
|
+
"""
|
|
27
|
+
Markdown rendering service with file/string support and collapsible UI.
|
|
28
|
+
|
|
29
|
+
Features:
|
|
30
|
+
- Render markdown from strings or .md files
|
|
31
|
+
- Auto-detect content type (file path vs markdown string)
|
|
32
|
+
- Beautiful Tailwind CSS styling with dark mode
|
|
33
|
+
- Collapsible sections for documentation
|
|
34
|
+
- Support for all markdown features (tables, code blocks, etc.)
|
|
35
|
+
|
|
36
|
+
Usage:
|
|
37
|
+
# In admin.py
|
|
38
|
+
from django_cfg.modules.django_admin.utils import MarkdownRenderer
|
|
39
|
+
|
|
40
|
+
class MyAdmin(admin.ModelAdmin):
|
|
41
|
+
def documentation(self, obj):
|
|
42
|
+
# From file
|
|
43
|
+
return MarkdownRenderer.render(obj.docs_file_path, collapsible=True)
|
|
44
|
+
|
|
45
|
+
# From string
|
|
46
|
+
return MarkdownRenderer.render(obj.markdown_content, collapsible=True)
|
|
47
|
+
|
|
48
|
+
# From field or string with custom title
|
|
49
|
+
return MarkdownRenderer.render(
|
|
50
|
+
obj.description,
|
|
51
|
+
collapsible=True,
|
|
52
|
+
title="Description"
|
|
53
|
+
)
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
# Singleton markdown parser instances
|
|
57
|
+
_md_with_plugins = None
|
|
58
|
+
_md_without_plugins = None
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def _get_markdown_parser(cls, enable_plugins: bool = True):
|
|
62
|
+
"""Get or create markdown parser instance (singleton pattern)."""
|
|
63
|
+
if not MISTUNE_AVAILABLE:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
if enable_plugins:
|
|
67
|
+
if cls._md_with_plugins is None:
|
|
68
|
+
cls._md_with_plugins = mistune.create_markdown(
|
|
69
|
+
plugins=['strikethrough', 'table', 'url', 'task_lists', 'def_list']
|
|
70
|
+
)
|
|
71
|
+
return cls._md_with_plugins
|
|
72
|
+
else:
|
|
73
|
+
if cls._md_without_plugins is None:
|
|
74
|
+
cls._md_without_plugins = mistune.create_markdown()
|
|
75
|
+
return cls._md_without_plugins
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def _load_from_file(cls, file_path: Union[str, Path]) -> Optional[str]:
|
|
79
|
+
"""
|
|
80
|
+
Load markdown content from file.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
file_path: Path to .md file
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
File content or None if error
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
path = Path(file_path)
|
|
90
|
+
if not path.exists():
|
|
91
|
+
logger.warning(f"Markdown file not found: {file_path}")
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
if not path.suffix.lower() in ['.md', '.markdown']:
|
|
95
|
+
logger.warning(f"File is not a markdown file: {file_path}")
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
with open(path, 'r', encoding='utf-8') as f:
|
|
99
|
+
return f.read()
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logger.error(f"Error loading markdown file {file_path}: {e}")
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def _is_file_path(cls, content: str) -> bool:
|
|
106
|
+
"""
|
|
107
|
+
Check if content is a file path.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
content: String to check
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
True if looks like a file path
|
|
114
|
+
"""
|
|
115
|
+
# Check if it's a valid path and file exists
|
|
116
|
+
if '\n' in content:
|
|
117
|
+
return False # Multi-line content is not a file path
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
path = Path(content)
|
|
121
|
+
return path.exists() and path.is_file() and path.suffix.lower() in ['.md', '.markdown']
|
|
122
|
+
except (OSError, ValueError):
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def render_markdown(
|
|
127
|
+
cls,
|
|
128
|
+
text: str,
|
|
129
|
+
css_class: str = "",
|
|
130
|
+
max_height: Optional[str] = None,
|
|
131
|
+
enable_plugins: bool = True
|
|
132
|
+
) -> SafeString:
|
|
133
|
+
"""
|
|
134
|
+
Render markdown text to HTML with beautiful Tailwind styling.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
text: Markdown content
|
|
138
|
+
css_class: Additional CSS classes
|
|
139
|
+
max_height: Max height with scrolling (e.g., "400px", "20rem")
|
|
140
|
+
enable_plugins: Enable mistune plugins (tables, strikethrough, etc.)
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
SafeString with rendered HTML
|
|
144
|
+
"""
|
|
145
|
+
if not MISTUNE_AVAILABLE:
|
|
146
|
+
return format_html(
|
|
147
|
+
'<div class="text-warning-600 dark:text-warning-400 p-3 bg-warning-50 dark:bg-warning-900/20 border border-warning-200 dark:border-warning-700 rounded">'
|
|
148
|
+
'<strong>⚠️ Mistune not installed:</strong> Install with: pip install mistune>=3.1.4'
|
|
149
|
+
'</div>'
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
if not text:
|
|
153
|
+
return format_html(
|
|
154
|
+
'<span class="text-font-subtle-light dark:text-font-subtle-dark">No content</span>'
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Get markdown parser
|
|
158
|
+
md = cls._get_markdown_parser(enable_plugins)
|
|
159
|
+
if not md:
|
|
160
|
+
return format_html(
|
|
161
|
+
'<div class="text-danger-600 dark:text-danger-400">Error: Could not initialize markdown parser</div>'
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Render markdown to HTML
|
|
165
|
+
html_content = md(str(text))
|
|
166
|
+
|
|
167
|
+
# Beautiful Tailwind prose styles
|
|
168
|
+
base_classes = (
|
|
169
|
+
"prose prose-sm dark:prose-invert max-w-none "
|
|
170
|
+
"prose-headings:font-semibold prose-headings:text-font-default-light dark:prose-headings:text-font-default-dark "
|
|
171
|
+
"prose-h1:text-2xl prose-h1:mb-4 prose-h1:mt-6 prose-h1:border-b prose-h1:border-base-200 dark:prose-h1:border-base-700 prose-h1:pb-2 "
|
|
172
|
+
"prose-h2:text-xl prose-h2:mb-3 prose-h2:mt-5 prose-h2:border-b prose-h2:border-base-200 dark:prose-h2:border-base-700 prose-h2:pb-1 "
|
|
173
|
+
"prose-h3:text-lg prose-h3:mb-2 prose-h3:mt-4 "
|
|
174
|
+
"prose-h4:text-base prose-h4:mb-2 prose-h4:mt-3 "
|
|
175
|
+
"prose-p:mb-3 prose-p:leading-relaxed prose-p:text-font-default-light dark:prose-p:text-font-default-dark "
|
|
176
|
+
"prose-a:text-primary-600 dark:prose-a:text-primary-400 prose-a:no-underline hover:prose-a:underline prose-a:font-medium "
|
|
177
|
+
"prose-strong:text-font-default-light dark:prose-strong:text-font-default-dark prose-strong:font-semibold "
|
|
178
|
+
"prose-code:bg-base-100 dark:prose-code:bg-base-800 prose-code:text-danger-600 dark:prose-code:text-danger-400 "
|
|
179
|
+
"prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded prose-code:text-xs prose-code:font-mono "
|
|
180
|
+
"prose-code:before:content-none prose-code:after:content-none "
|
|
181
|
+
"prose-pre:bg-base-50 dark:prose-pre:bg-base-900 prose-pre:border prose-pre:border-base-200 dark:prose-pre:border-base-700 prose-pre:rounded-lg "
|
|
182
|
+
"prose-blockquote:border-l-4 prose-blockquote:border-primary-300 dark:prose-blockquote:border-primary-600 "
|
|
183
|
+
"prose-blockquote:bg-base-50 dark:prose-blockquote:bg-base-900 prose-blockquote:pl-4 prose-blockquote:pr-4 prose-blockquote:py-2 "
|
|
184
|
+
"prose-blockquote:italic prose-blockquote:text-font-subtle-light dark:prose-blockquote:text-font-subtle-dark "
|
|
185
|
+
"prose-ul:list-disc prose-ul:pl-6 prose-ul:mb-3 prose-ul:text-font-default-light dark:prose-ul:text-font-default-dark "
|
|
186
|
+
"prose-ol:list-decimal prose-ol:pl-6 prose-ol:mb-3 prose-ol:text-font-default-light dark:prose-ol:text-font-default-dark "
|
|
187
|
+
"prose-li:mb-1 "
|
|
188
|
+
"prose-table:border-collapse prose-table:w-full prose-table:text-sm "
|
|
189
|
+
"prose-th:bg-base-100 dark:prose-th:bg-base-800 prose-th:border prose-th:border-base-300 dark:prose-th:border-base-600 "
|
|
190
|
+
"prose-th:px-4 prose-th:py-2 prose-th:text-left prose-th:font-semibold prose-th:text-font-default-light dark:prose-th:text-font-default-dark "
|
|
191
|
+
"prose-td:border prose-td:border-base-200 dark:prose-td:border-base-700 prose-td:px-4 prose-td:py-2 "
|
|
192
|
+
"prose-td:text-font-default-light dark:prose-td:text-font-default-dark "
|
|
193
|
+
"prose-img:rounded-lg prose-img:shadow-md prose-img:border prose-img:border-base-200 dark:prose-img:border-base-700 "
|
|
194
|
+
"prose-hr:border-base-200 dark:prose-hr:border-base-700 prose-hr:my-6 "
|
|
195
|
+
"prose-em:text-font-default-light dark:prose-em:text-font-default-dark "
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Combine with custom classes
|
|
199
|
+
classes = f"{base_classes} {css_class}".strip()
|
|
200
|
+
|
|
201
|
+
# Add container div with max-height if specified
|
|
202
|
+
style = ""
|
|
203
|
+
if max_height:
|
|
204
|
+
style = f'max-height: {max_height}; overflow-y: auto;'
|
|
205
|
+
|
|
206
|
+
return format_html(
|
|
207
|
+
'<div class="{}" {}>{}</div>',
|
|
208
|
+
classes,
|
|
209
|
+
mark_safe(f'style="{style}"') if style else '',
|
|
210
|
+
mark_safe(html_content)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
@classmethod
|
|
214
|
+
def render(
|
|
215
|
+
cls,
|
|
216
|
+
content: Union[str, Path],
|
|
217
|
+
collapsible: bool = False,
|
|
218
|
+
title: str = "Documentation",
|
|
219
|
+
icon: str = "description",
|
|
220
|
+
max_height: Optional[str] = "500px",
|
|
221
|
+
enable_plugins: bool = True,
|
|
222
|
+
default_open: bool = False
|
|
223
|
+
) -> SafeString:
|
|
224
|
+
"""
|
|
225
|
+
Universal markdown renderer - auto-detects file vs string content.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
content: Markdown string, file path, or Path object
|
|
229
|
+
collapsible: Wrap in collapsible Tailwind details/summary
|
|
230
|
+
title: Title for collapsible section
|
|
231
|
+
icon: Material icon name for title
|
|
232
|
+
max_height: Max height for scrolling (None = no limit)
|
|
233
|
+
enable_plugins: Enable markdown plugins
|
|
234
|
+
default_open: If collapsible, open by default
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Rendered HTML as SafeString
|
|
238
|
+
|
|
239
|
+
Examples:
|
|
240
|
+
# Simple render from string
|
|
241
|
+
MarkdownRenderer.render("# Hello\\n\\nThis is **bold**")
|
|
242
|
+
|
|
243
|
+
# From file
|
|
244
|
+
MarkdownRenderer.render("/path/to/docs.md")
|
|
245
|
+
|
|
246
|
+
# Collapsible documentation
|
|
247
|
+
MarkdownRenderer.render(
|
|
248
|
+
obj.description,
|
|
249
|
+
collapsible=True,
|
|
250
|
+
title="API Documentation",
|
|
251
|
+
icon="api"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# From file with collapse
|
|
255
|
+
MarkdownRenderer.render(
|
|
256
|
+
"docs/README.md",
|
|
257
|
+
collapsible=True,
|
|
258
|
+
title="Project Documentation",
|
|
259
|
+
default_open=True
|
|
260
|
+
)
|
|
261
|
+
"""
|
|
262
|
+
if not content:
|
|
263
|
+
return format_html(
|
|
264
|
+
'<span class="text-font-subtle-light dark:text-font-subtle-dark text-sm">No documentation available</span>'
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Convert to string if Path object
|
|
268
|
+
content_str = str(content)
|
|
269
|
+
|
|
270
|
+
# Try to load from file if it looks like a file path
|
|
271
|
+
markdown_text = content_str
|
|
272
|
+
is_from_file = False
|
|
273
|
+
|
|
274
|
+
if cls._is_file_path(content_str):
|
|
275
|
+
file_content = cls._load_from_file(content_str)
|
|
276
|
+
if file_content:
|
|
277
|
+
markdown_text = file_content
|
|
278
|
+
is_from_file = True
|
|
279
|
+
else:
|
|
280
|
+
return format_html(
|
|
281
|
+
'<div class="text-warning-600 dark:text-warning-400 p-3 bg-warning-50 dark:bg-warning-900/20 border border-warning-200 dark:border-warning-700 rounded">'
|
|
282
|
+
'<strong>⚠️ File not found:</strong> {}'
|
|
283
|
+
'</div>',
|
|
284
|
+
escape(content_str)
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# Render markdown
|
|
288
|
+
rendered_html = cls.render_markdown(
|
|
289
|
+
markdown_text,
|
|
290
|
+
max_height=max_height if not collapsible else None,
|
|
291
|
+
enable_plugins=enable_plugins
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# Return without collapse if not requested
|
|
295
|
+
if not collapsible:
|
|
296
|
+
return rendered_html
|
|
297
|
+
|
|
298
|
+
# Wrap in beautiful collapsible section
|
|
299
|
+
open_attr = 'open' if default_open else ''
|
|
300
|
+
|
|
301
|
+
return format_html(
|
|
302
|
+
'<details class="group border border-base-200 dark:border-base-700 rounded-lg overflow-hidden bg-white dark:bg-base-800 shadow-sm hover:shadow-md transition-shadow" {}>'
|
|
303
|
+
'<summary class="cursor-pointer px-4 py-3 bg-base-50 dark:bg-base-900 hover:bg-base-100 dark:hover:bg-base-800 transition-colors flex items-center gap-2 select-none">'
|
|
304
|
+
'<span class="material-symbols-outlined text-base text-primary-600 dark:text-primary-400 group-open:rotate-90 transition-transform">{}</span>'
|
|
305
|
+
'<span class="font-semibold text-sm text-font-default-light dark:text-font-default-dark">{}</span>'
|
|
306
|
+
'<span class="text-xs text-font-subtle-light dark:text-font-subtle-dark ml-auto">{}</span>'
|
|
307
|
+
'</summary>'
|
|
308
|
+
'<div class="p-4 bg-white dark:bg-base-800" {}>'
|
|
309
|
+
'{}'
|
|
310
|
+
'</div>'
|
|
311
|
+
'</details>',
|
|
312
|
+
mark_safe(open_attr),
|
|
313
|
+
'chevron_right', # Arrow icon
|
|
314
|
+
escape(title),
|
|
315
|
+
'Click to expand' if not default_open else 'Click to collapse',
|
|
316
|
+
mark_safe(f'style="max-height: {max_height}; overflow-y: auto;"') if max_height else '',
|
|
317
|
+
rendered_html
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
@classmethod
|
|
321
|
+
def render_docs(
|
|
322
|
+
cls,
|
|
323
|
+
content: Union[str, Path],
|
|
324
|
+
**kwargs
|
|
325
|
+
) -> SafeString:
|
|
326
|
+
"""
|
|
327
|
+
Shorthand for rendering documentation (always collapsible by default).
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
content: Markdown string or file path
|
|
331
|
+
**kwargs: Additional arguments passed to render()
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
Rendered collapsible documentation
|
|
335
|
+
|
|
336
|
+
Example:
|
|
337
|
+
def documentation(self, obj):
|
|
338
|
+
return MarkdownRenderer.render_docs(obj.docs_path)
|
|
339
|
+
"""
|
|
340
|
+
# Set collapsible=True by default for docs
|
|
341
|
+
if 'collapsible' not in kwargs:
|
|
342
|
+
kwargs['collapsible'] = True
|
|
343
|
+
|
|
344
|
+
return cls.render(content, **kwargs)
|
django_cfg/pyproject.toml
CHANGED
|
@@ -4,13 +4,13 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "django-cfg"
|
|
7
|
-
version = "1.4.
|
|
7
|
+
version = "1.4.110"
|
|
8
8
|
description = "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."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "nextjs-admin", "react-admin", "websocket", "centrifugo", "real-time", "typescript-generation", "ai-agents", "enterprise-django", "django-settings", "type-safe-config", "modern-django",]
|
|
11
11
|
classifiers = [ "Development Status :: 4 - Beta", "Framework :: Django", "Framework :: Django :: 5.2", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: System :: Systems Administration", "Typing :: Typed",]
|
|
12
12
|
requires-python = ">=3.12,<3.14"
|
|
13
|
-
dependencies = [ "pydantic>=2.11.0,<3.0", "pydantic[email]>=2.11.0,<3.0", "PyYAML>=6.0,<7.0", "click>=8.2.0,<9.0", "questionary>=2.1.0,<3.0", "rich>=14.0.0,<15.0", "cloudflare>=4.3.0,<5.0", "loguru>=0.7.0,<1.0", "colorlog>=6.9.0,<7.0", "cachetools>=5.3.0,<7.0", "toml>=0.10.2,<0.11.0", "ngrok>=1.5.1; python_version>='3.12'", "psycopg[binary,pool]>=3.2.0,<4.0", "dj-database-url>=3.0.0,<4.0", "whitenoise>=6.8.0,<7.0", "django-cors-headers>=4.7.0,<5.0", "djangorestframework>=3.16.0,<4.0", "djangorestframework-simplejwt>=5.5.0,<6.0", "djangorestframework-simplejwt[token-blacklist]>=5.5.0,<6.0", "drf-nested-routers>=0.94.0,<1.0", "django-filter>=25.0,<26.0", "django-ratelimit>=4.1.0,<5.0.0", "drf-spectacular>=0.28.0,<1.0", "drf-spectacular-sidecar>=2025.8.0,<2026.0", "django-json-widget>=2.0.0,<3.0", "django-import-export>=4.3.0,<5.0", "django-extensions>=4.1.0,<5.0", "django-constance>=4.3.0,<5.0", "django-unfold>=0.64.0,<1.0", "django-redis>=6.0.0,<7.0", "redis>=6.4.0,<7.0", "hiredis>=2.0.0,<4.0", "rearq>=0.2.0,<1.0", "setuptools>=75.0.0; python_version>='3.13'", "pyTelegramBotAPI>=4.28.0,<5.0", "coolname>=2.2.0,<3.0", "django-admin-rangefilter>=0.13.0,<1.0", "python-json-logger>=3.3.0,<4.0", "requests>=2.32.0,<3.0", "tiktoken>=0.11.0,<1.0", "openai>=1.107.0,<2.0", "twilio>=9.8.0,<10.0", "sendgrid>=6.12.0,<7.0", "beautifulsoup4>=4.13.0,<5.0", "lxml>=6.0.0,<7.0", "pgvector>=0.4.0,<1.0", "tenacity>=9.1.2,<10.0.0", "mypy (>=1.18.2,<2.0.0)", "django-tailwind[reload] (>=4.2.0,<5.0.0)", "jinja2 (>=3.1.6,<4.0.0)", "django-axes[ipware] (>=8.0.0,<9.0.0)", "pydantic-settings (>=2.11.0,<3.0.0)", "pytz>=2025.1", "httpx>=0.28.1,<1.0",]
|
|
13
|
+
dependencies = [ "pydantic>=2.11.0,<3.0", "pydantic[email]>=2.11.0,<3.0", "PyYAML>=6.0,<7.0", "click>=8.2.0,<9.0", "questionary>=2.1.0,<3.0", "rich>=14.0.0,<15.0", "cloudflare>=4.3.0,<5.0", "loguru>=0.7.0,<1.0", "colorlog>=6.9.0,<7.0", "cachetools>=5.3.0,<7.0", "toml>=0.10.2,<0.11.0", "ngrok>=1.5.1; python_version>='3.12'", "psycopg[binary,pool]>=3.2.0,<4.0", "dj-database-url>=3.0.0,<4.0", "whitenoise>=6.8.0,<7.0", "django-cors-headers>=4.7.0,<5.0", "djangorestframework>=3.16.0,<4.0", "djangorestframework-simplejwt>=5.5.0,<6.0", "djangorestframework-simplejwt[token-blacklist]>=5.5.0,<6.0", "drf-nested-routers>=0.94.0,<1.0", "django-filter>=25.0,<26.0", "django-ratelimit>=4.1.0,<5.0.0", "drf-spectacular>=0.28.0,<1.0", "drf-spectacular-sidecar>=2025.8.0,<2026.0", "django-json-widget>=2.0.0,<3.0", "django-import-export>=4.3.0,<5.0", "django-extensions>=4.1.0,<5.0", "django-constance>=4.3.0,<5.0", "django-unfold>=0.64.0,<1.0", "django-redis>=6.0.0,<7.0", "redis>=6.4.0,<7.0", "hiredis>=2.0.0,<4.0", "rearq>=0.2.0,<1.0", "setuptools>=75.0.0; python_version>='3.13'", "pyTelegramBotAPI>=4.28.0,<5.0", "coolname>=2.2.0,<3.0", "django-admin-rangefilter>=0.13.0,<1.0", "python-json-logger>=3.3.0,<4.0", "requests>=2.32.0,<3.0", "tiktoken>=0.11.0,<1.0", "openai>=1.107.0,<2.0", "twilio>=9.8.0,<10.0", "sendgrid>=6.12.0,<7.0", "beautifulsoup4>=4.13.0,<5.0", "lxml>=6.0.0,<7.0", "pgvector>=0.4.0,<1.0", "tenacity>=9.1.2,<10.0.0", "mypy (>=1.18.2,<2.0.0)", "django-tailwind[reload] (>=4.2.0,<5.0.0)", "jinja2 (>=3.1.6,<4.0.0)", "django-axes[ipware] (>=8.0.0,<9.0.0)", "pydantic-settings (>=2.11.0,<3.0.0)", "pytz>=2025.1", "httpx>=0.28.1,<1.0", "mistune>=3.1.4,<4.0",]
|
|
14
14
|
[[project.authors]]
|
|
15
15
|
name = "Django-CFG Team"
|
|
16
16
|
email = "info@djangocfg.com"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-cfg
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.110
|
|
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
|
|
@@ -58,6 +58,7 @@ Requires-Dist: httpx<1.0,>=0.28.1
|
|
|
58
58
|
Requires-Dist: jinja2<4.0.0,>=3.1.6
|
|
59
59
|
Requires-Dist: loguru<1.0,>=0.7.0
|
|
60
60
|
Requires-Dist: lxml<7.0,>=6.0.0
|
|
61
|
+
Requires-Dist: mistune<4.0,>=3.1.4
|
|
61
62
|
Requires-Dist: mypy<2.0.0,>=1.18.2
|
|
62
63
|
Requires-Dist: ngrok>=1.5.1; python_version >= '3.12'
|
|
63
64
|
Requires-Dist: openai<2.0,>=1.107.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
django_cfg/__init__.py,sha256=
|
|
2
|
+
django_cfg/__init__.py,sha256=HbtRDtHlqf7LLLnJjYEq_tMe99Y4cjV_Kx8txhAOD_k,1621
|
|
3
3
|
django_cfg/apps.py,sha256=72m3uuvyqGiLx6gOfE-BD3P61jddCCERuBOYpxTX518,1605
|
|
4
4
|
django_cfg/config.py,sha256=y4Z3rnYsHBE0TehpwAIPaxr---mkvyKrZGGsNwYso74,1398
|
|
5
5
|
django_cfg/apps/__init__.py,sha256=JtDmEYt1OcleWM2ZaeX0LKDnRQzPOavfaXBWG4ECB5Q,26
|
|
@@ -668,18 +668,16 @@ django_cfg/models/tasks/config.py,sha256=0Tc7O2mVQLc8IYXMm__cdeP0ytYn_NucL2xyA95
|
|
|
668
668
|
django_cfg/models/tasks/utils.py,sha256=9TEbdxgd0N_O2T_7GGwkyPeDg4H7tSKLHoHG_n8edqg,4459
|
|
669
669
|
django_cfg/modules/__init__.py,sha256=Ip9WMpzImEwIAywpFwU056_v0O9oIGG7nCT1YSArxkw,316
|
|
670
670
|
django_cfg/modules/base.py,sha256=Grmgxc5dvnAEM1sudWEWO4kv8L0Ks-y32nxTk2vwdjQ,6272
|
|
671
|
-
django_cfg/modules/django_admin/
|
|
672
|
-
django_cfg/modules/django_admin/IMPORT_EXPORT_FIX.md,sha256=F8z4oHZk7jkRChxW8tKsVf0Q_OujjlBUs8UJNirn55Y,2055
|
|
673
|
-
django_cfg/modules/django_admin/RESOURCE_CONFIG_ENHANCEMENT.md,sha256=MKUpRHMqM3KDEKZthWhul-4EoSnE75kkWd7IcEzwYDQ,8729
|
|
674
|
-
django_cfg/modules/django_admin/__init__.py,sha256=LYVFCVgN6Irr80dK8SkXgKQ-Xgg2MyRL5CgfOgmSt8A,3190
|
|
671
|
+
django_cfg/modules/django_admin/__init__.py,sha256=ncTMbxR7ccVd5xS-sZE_yJODjVMFq8HDFaDnP6lyvIg,3328
|
|
675
672
|
django_cfg/modules/django_admin/base/__init__.py,sha256=tzre09bnD_SlS-pA30WzYZRxyvch7eLq3q0wLEcZOmc,118
|
|
676
|
-
django_cfg/modules/django_admin/base/pydantic_admin.py,sha256=
|
|
673
|
+
django_cfg/modules/django_admin/base/pydantic_admin.py,sha256=EZnkPFXDHvqR-JtDfYvJfg5_Nocn7svgXvQdVsTbv9A,24389
|
|
677
674
|
django_cfg/modules/django_admin/base/unfold_admin.py,sha256=iqpRWSkzW5HktXDuuG7G3J6RoIfW48dWPMJTa7Yk08g,729
|
|
678
|
-
django_cfg/modules/django_admin/config/__init__.py,sha256=
|
|
675
|
+
django_cfg/modules/django_admin/config/__init__.py,sha256=HDuJxhAS27EiL1pXLNvMePREB2gfoS2i_0Ur1ITCjAM,926
|
|
679
676
|
django_cfg/modules/django_admin/config/action_config.py,sha256=JjS01JxLT-FzUVq7RlKaB7L38wmVL8uibXO_iXZcljo,1668
|
|
680
|
-
django_cfg/modules/django_admin/config/admin_config.py,sha256=
|
|
677
|
+
django_cfg/modules/django_admin/config/admin_config.py,sha256=TnPI-kLZj6uoIH7opCD70HQ6I_yJ7jPKnphS0h9P42s,5242
|
|
681
678
|
django_cfg/modules/django_admin/config/background_task_config.py,sha256=7-8B1rhpeafanVxtjFQUx0mVjcA5xmxZIxqKzaBwMX0,1760
|
|
682
|
-
django_cfg/modules/django_admin/config/
|
|
679
|
+
django_cfg/modules/django_admin/config/documentation_config.py,sha256=lI_gMSWCtyKmdyttLNdgbg_zbGgrwXA-QoLxVOXJj9A,14189
|
|
680
|
+
django_cfg/modules/django_admin/config/field_config.py,sha256=LeHoumm-lmiYIu0dBmoyCdTkddQdcv20_KrD3WRhkQ0,11743
|
|
683
681
|
django_cfg/modules/django_admin/config/fieldset_config.py,sha256=5BPUWO_HvS6YhPU_vqQPzRT2y3OIPrBCioFuer5-mrA,1249
|
|
684
682
|
django_cfg/modules/django_admin/config/resource_config.py,sha256=6ELWSuHlrpNqV2wO2sL_o2mE2DXLd_solcsdN5O6eQg,3962
|
|
685
683
|
django_cfg/modules/django_admin/icons/README.md,sha256=j-NUixSC1QJh7PqYKxLZpPrTxKrAnx0urQraXgv4JHI,5612
|
|
@@ -691,12 +689,17 @@ django_cfg/modules/django_admin/models/action_models.py,sha256=clzu_DJOexrqyKfkv
|
|
|
691
689
|
django_cfg/modules/django_admin/models/badge_models.py,sha256=ob5NOtx8YZ9KRVmn9fxLWg_OXbvt3fLhhk8wwU5eP8E,538
|
|
692
690
|
django_cfg/modules/django_admin/models/base.py,sha256=fmgS-X3GAGEBXiBy4OQGeOMzB7qIkvzDXla5-_EHAQE,611
|
|
693
691
|
django_cfg/modules/django_admin/models/display_models.py,sha256=rONmja60Qz8n4qNvzjXIQryO_-UGZK136SrElO8iFfM,1087
|
|
692
|
+
django_cfg/modules/django_admin/templates/django_admin/change_form_docs.html,sha256=MtP-UKebcOdz6YsZxLV5-UFq6WfmlV3mEF7qsCdYOKg,815
|
|
693
|
+
django_cfg/modules/django_admin/templates/django_admin/change_list_docs.html,sha256=MfH6mf9v6E3y97TjvjvZ2A7RMhOkKd6CtxEw3tPV5WE,802
|
|
694
|
+
django_cfg/modules/django_admin/templates/django_admin/documentation_block.html,sha256=IHB1dDGavvHNnez6modI1wJxvoXuZSQmsBu0nheYcZA,13974
|
|
695
|
+
django_cfg/modules/django_admin/templates/django_admin/markdown_docs_block.html,sha256=nO9oEyDYozYF-euaLzE0Cn_Yo_LGUlta-BsRgShDG10,1992
|
|
694
696
|
django_cfg/modules/django_admin/utils/CODE_BLOCK_DOCS.md,sha256=rp5qMG-Ci30fIs6EyZctjEbhQwxfNq9e36B4CTZOnR0,9456
|
|
695
|
-
django_cfg/modules/django_admin/utils/__init__.py,sha256=
|
|
697
|
+
django_cfg/modules/django_admin/utils/__init__.py,sha256=5V1Og6fpIpRqDIoqF7jYnBmPl9TjV5kByghzT6iOpPs,745
|
|
696
698
|
django_cfg/modules/django_admin/utils/badges.py,sha256=eZ1UThdwvv2cHAIDc4vTrD5xAet7fmeb9h9yj4ZXJ-c,6328
|
|
697
699
|
django_cfg/modules/django_admin/utils/decorators.py,sha256=s4jTcgPklY4T4xjXsMHpShd71K_LzgKogem9JKBgNzk,8371
|
|
698
700
|
django_cfg/modules/django_admin/utils/displays.py,sha256=f-FT1mD-X56X6xLDJ9FuCi4oz_UYirdLosYrXnJP3hI,7862
|
|
699
|
-
django_cfg/modules/django_admin/utils/html_builder.py,sha256=
|
|
701
|
+
django_cfg/modules/django_admin/utils/html_builder.py,sha256=EMzz1rGJCM7jTgfWb8LKqSdok1oq_oaWbBk6v9qvCrU,12831
|
|
702
|
+
django_cfg/modules/django_admin/utils/markdown_renderer.py,sha256=nrMopdVA114W_UOvYFUtnbfj_Di0LHmtmrxG53hsn_Y,13834
|
|
700
703
|
django_cfg/modules/django_admin/widgets/__init__.py,sha256=mmPw5FMYR21GDGFMr-MOCcdM4G2_ZR60ClInHjdnTBE,115
|
|
701
704
|
django_cfg/modules/django_admin/widgets/registry.py,sha256=q0Yyaze5ZTYLJslPyX9e4Few_FGLnGBQwtNln9Okyt4,5610
|
|
702
705
|
django_cfg/modules/django_client/__init__.py,sha256=iHaGKbsyR2wMmVCWNsETC7cwB60fZudvnFMiK1bchW8,529
|
|
@@ -1050,9 +1053,9 @@ django_cfg/utils/version_check.py,sha256=WO51J2m2e-wVqWCRwbultEwu3q1lQasV67Mw2aa
|
|
|
1050
1053
|
django_cfg/CHANGELOG.md,sha256=jtT3EprqEJkqSUh7IraP73vQ8PmKUMdRtznQsEnqDZk,2052
|
|
1051
1054
|
django_cfg/CONTRIBUTING.md,sha256=DU2kyQ6PU0Z24ob7O_OqKWEYHcZmJDgzw-lQCmu6uBg,3041
|
|
1052
1055
|
django_cfg/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
|
|
1053
|
-
django_cfg/pyproject.toml,sha256=
|
|
1054
|
-
django_cfg-1.4.
|
|
1055
|
-
django_cfg-1.4.
|
|
1056
|
-
django_cfg-1.4.
|
|
1057
|
-
django_cfg-1.4.
|
|
1058
|
-
django_cfg-1.4.
|
|
1056
|
+
django_cfg/pyproject.toml,sha256=lz106jlJ2YsUxr9D0NqVgp5x2TZSNT1vbTrZ_YaeydQ,8637
|
|
1057
|
+
django_cfg-1.4.110.dist-info/METADATA,sha256=xG_TAByn-K2qm3HlL_4cMNNHz4UpMrpV5hVIzTNGdKU,23836
|
|
1058
|
+
django_cfg-1.4.110.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
1059
|
+
django_cfg-1.4.110.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
|
|
1060
|
+
django_cfg-1.4.110.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
|
|
1061
|
+
django_cfg-1.4.110.dist-info/RECORD,,
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# CHANGELOG: Code Display Methods
|
|
2
|
-
|
|
3
|
-
## [2024-10-29] Added `code()` and `code_block()` methods to HtmlBuilder
|
|
4
|
-
|
|
5
|
-
### Summary
|
|
6
|
-
Added two new methods to `HtmlBuilder` for displaying code in Django Admin interfaces with proper styling, scrolling support, and color variants.
|
|
7
|
-
|
|
8
|
-
### New Methods
|
|
9
|
-
|
|
10
|
-
#### 1. `html.code(text, css_class="")`
|
|
11
|
-
Display inline code snippets.
|
|
12
|
-
|
|
13
|
-
**Use cases:**
|
|
14
|
-
- File paths
|
|
15
|
-
- Short commands
|
|
16
|
-
- Configuration keys
|
|
17
|
-
- Single-line code
|
|
18
|
-
|
|
19
|
-
**Example:**
|
|
20
|
-
```python
|
|
21
|
-
self.html.code("/path/to/file")
|
|
22
|
-
self.html.code("DEBUG=True")
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
#### 2. `html.code_block(text, language=None, max_height=None, variant="default")`
|
|
26
|
-
Display multi-line code blocks with:
|
|
27
|
-
- Optional scrolling (via `max_height`)
|
|
28
|
-
- Color variants (default, warning, danger, success, info)
|
|
29
|
-
- Language hints for future syntax highlighting
|
|
30
|
-
- Proper text wrapping and escaping
|
|
31
|
-
|
|
32
|
-
**Use cases:**
|
|
33
|
-
- JSON configuration
|
|
34
|
-
- Command output (stdout/stderr)
|
|
35
|
-
- Error messages and stack traces
|
|
36
|
-
- Log files
|
|
37
|
-
- API requests/responses
|
|
38
|
-
- SQL queries
|
|
39
|
-
|
|
40
|
-
**Examples:**
|
|
41
|
-
```python
|
|
42
|
-
# JSON
|
|
43
|
-
self.html.code_block(json.dumps(data, indent=2), language="json")
|
|
44
|
-
|
|
45
|
-
# Logs with scrolling
|
|
46
|
-
self.html.code_block(obj.stdout, max_height="400px")
|
|
47
|
-
|
|
48
|
-
# Errors
|
|
49
|
-
self.html.code_block(obj.error_message, variant="danger", max_height="300px")
|
|
50
|
-
|
|
51
|
-
# Warnings
|
|
52
|
-
self.html.code_block(obj.stderr, variant="warning", max_height="400px")
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Files Modified
|
|
56
|
-
|
|
57
|
-
**`utils/html_builder.py`:**
|
|
58
|
-
- Added `code()` method (lines 207-227)
|
|
59
|
-
- Added `code_block()` method (lines 229-278)
|
|
60
|
-
|
|
61
|
-
### Features
|
|
62
|
-
|
|
63
|
-
✅ **Automatic HTML Escaping** - Prevents XSS
|
|
64
|
-
✅ **Dark Mode Support** - Full Tailwind dark: classes
|
|
65
|
-
✅ **Responsive Design** - Works on mobile
|
|
66
|
-
✅ **Color Variants** - 5 variants for different contexts
|
|
67
|
-
✅ **Scrollable Content** - Max height with overflow
|
|
68
|
-
✅ **Consistent Styling** - Matches django-cfg design system
|
|
69
|
-
✅ **Future-proof** - Language parameter for syntax highlighting
|
|
70
|
-
✅ **Type Hints** - Full typing support
|
|
71
|
-
|
|
72
|
-
### Technical Details
|
|
73
|
-
|
|
74
|
-
**Dependencies:**
|
|
75
|
-
- Django's `format_html` and `escape`
|
|
76
|
-
- Tailwind CSS classes (already in use)
|
|
77
|
-
- No additional packages required
|
|
78
|
-
|
|
79
|
-
**Styling:**
|
|
80
|
-
- Uses existing Tailwind utility classes
|
|
81
|
-
- Follows django-cfg color system
|
|
82
|
-
- Light/dark mode variants
|
|
83
|
-
|
|
84
|
-
**Performance:**
|
|
85
|
-
- No JavaScript required
|
|
86
|
-
- Pure CSS for scrolling
|
|
87
|
-
- Minimal HTML overhead
|
|
88
|
-
|
|
89
|
-
### Breaking Changes
|
|
90
|
-
None - purely additive changes.
|
|
91
|
-
|
|
92
|
-
### Backward Compatibility
|
|
93
|
-
100% backward compatible. All existing code continues to work.
|
|
94
|
-
|
|
95
|
-
### Migration Path
|
|
96
|
-
Optional migration from manual HTML:
|
|
97
|
-
|
|
98
|
-
**Before:**
|
|
99
|
-
```python
|
|
100
|
-
def output_display(self, obj):
|
|
101
|
-
return format_html('<pre>{}</pre>', obj.stdout)
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**After:**
|
|
105
|
-
```python
|
|
106
|
-
def output_display(self, obj):
|
|
107
|
-
return self.html.code_block(obj.stdout, max_height="400px")
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### Real-World Usage
|
|
111
|
-
|
|
112
|
-
Already implemented in:
|
|
113
|
-
- `apps.adminpanel.admin.config.CommandExecutionAdmin`
|
|
114
|
-
- JSON args/options display
|
|
115
|
-
- stdout/stderr output with scrolling
|
|
116
|
-
- Error messages with danger variant
|
|
117
|
-
|
|
118
|
-
### Documentation
|
|
119
|
-
|
|
120
|
-
Full documentation available in:
|
|
121
|
-
- `CODE_BLOCK_DOCS.md` - Complete guide with examples
|
|
122
|
-
- API Reference (to be updated)
|
|
123
|
-
- Quick Start guide (to be updated)
|
|
124
|
-
|
|
125
|
-
### Testing
|
|
126
|
-
|
|
127
|
-
Manual testing performed:
|
|
128
|
-
- ✅ Inline code rendering
|
|
129
|
-
- ✅ Code block with/without scrolling
|
|
130
|
-
- ✅ All 5 color variants
|
|
131
|
-
- ✅ JSON formatting
|
|
132
|
-
- ✅ Long text with wrapping
|
|
133
|
-
- ✅ Dark mode
|
|
134
|
-
- ✅ HTML escaping
|
|
135
|
-
|
|
136
|
-
### Next Steps
|
|
137
|
-
|
|
138
|
-
Recommended for future:
|
|
139
|
-
1. Add to API Reference documentation
|
|
140
|
-
2. Add to Examples documentation
|
|
141
|
-
3. Consider syntax highlighting library integration (Prism.js, highlight.js)
|
|
142
|
-
4. Add to django-cfg changelog
|
|
143
|
-
5. Update tutorial/quick-start docs
|
|
144
|
-
|
|
145
|
-
### Contributors
|
|
146
|
-
- Implementation: Claude Code
|
|
147
|
-
- Use Case: CommandExecution admin in StockAPIS project
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
**Version:** Added in django-cfg dev build (2024-10-29)
|
|
152
|
-
**Status:** ✅ Complete and tested
|
|
153
|
-
**Impact:** Low - purely additive feature
|