django-cfg 1.4.109__py3-none-any.whl → 1.4.111__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-cfg might be problematic. Click here for more details.
- django_cfg/__init__.py +1 -1
- django_cfg/modules/django_admin/__init__.py +6 -0
- django_cfg/modules/django_admin/base/pydantic_admin.py +101 -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 +303 -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 +360 -0
- django_cfg/modules/django_admin/utils/mermaid_plugin.py +288 -0
- django_cfg/pyproject.toml +2 -2
- {django_cfg-1.4.109.dist-info → django_cfg-1.4.111.dist-info}/METADATA +2 -1
- {django_cfg-1.4.109.dist-info → django_cfg-1.4.111.dist-info}/RECORD +21 -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.111.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.109.dist-info → django_cfg-1.4.111.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.109.dist-info → django_cfg-1.4.111.dist-info}/licenses/LICENSE +0 -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=nlFfAVPhDfA96placDSiyW_fDaW9Op6e1pCYQC1crb4,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=6tB_8hZLT3ZzwUu6rlvzhkQ_BkDq8niaTE3TIa0IcgM,24977
|
|
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,18 @@ 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=fDUZYDWuB68NOm4fjTr0OLfqG2m-UpE-MZL1upkUNHc,14174
|
|
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=eMYde8cDo9y5CO5qNjy7juYe_OPpSpFB1-d84ylpbk4,14324
|
|
703
|
+
django_cfg/modules/django_admin/utils/mermaid_plugin.py,sha256=37x_0FQE5IyR6TiaDZDrQQHeQmAsPtlZBnq5N8eAKqA,8889
|
|
700
704
|
django_cfg/modules/django_admin/widgets/__init__.py,sha256=mmPw5FMYR21GDGFMr-MOCcdM4G2_ZR60ClInHjdnTBE,115
|
|
701
705
|
django_cfg/modules/django_admin/widgets/registry.py,sha256=q0Yyaze5ZTYLJslPyX9e4Few_FGLnGBQwtNln9Okyt4,5610
|
|
702
706
|
django_cfg/modules/django_client/__init__.py,sha256=iHaGKbsyR2wMmVCWNsETC7cwB60fZudvnFMiK1bchW8,529
|
|
@@ -1050,9 +1054,9 @@ django_cfg/utils/version_check.py,sha256=WO51J2m2e-wVqWCRwbultEwu3q1lQasV67Mw2aa
|
|
|
1050
1054
|
django_cfg/CHANGELOG.md,sha256=jtT3EprqEJkqSUh7IraP73vQ8PmKUMdRtznQsEnqDZk,2052
|
|
1051
1055
|
django_cfg/CONTRIBUTING.md,sha256=DU2kyQ6PU0Z24ob7O_OqKWEYHcZmJDgzw-lQCmu6uBg,3041
|
|
1052
1056
|
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.
|
|
1057
|
+
django_cfg/pyproject.toml,sha256=gPSeLcWnzT8nOM1SW0-4PTlRjLnYYLTqGXeRnoDXN5w,8637
|
|
1058
|
+
django_cfg-1.4.111.dist-info/METADATA,sha256=zMEB1rQo52w6G3TcTEChugaK1CMRMdkJ9ClhP0VzcJ0,23836
|
|
1059
|
+
django_cfg-1.4.111.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
1060
|
+
django_cfg-1.4.111.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
|
|
1061
|
+
django_cfg-1.4.111.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
|
|
1062
|
+
django_cfg-1.4.111.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
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# Import/Export Button Fix
|
|
2
|
-
|
|
3
|
-
## Problem
|
|
4
|
-
|
|
5
|
-
Import/Export buttons in `PydanticAdmin` were displaying incorrectly because:
|
|
6
|
-
|
|
7
|
-
1. `PydanticAdmin` was using the original `ImportExportModelAdmin` from django-import-export
|
|
8
|
-
2. Original django-import-export templates don't integrate properly with Unfold UI
|
|
9
|
-
3. Buttons were styled as tabs instead of toolbar buttons
|
|
10
|
-
|
|
11
|
-
## Solution
|
|
12
|
-
|
|
13
|
-
Updated `PydanticAdmin` base class to use django_cfg's custom `ImportExportMixin`:
|
|
14
|
-
|
|
15
|
-
### Before
|
|
16
|
-
```python
|
|
17
|
-
class UnfoldImportExportModelAdmin(ImportExportModelAdmin, UnfoldModelAdmin):
|
|
18
|
-
pass
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
### After
|
|
22
|
-
```python
|
|
23
|
-
class UnfoldImportExportModelAdmin(UnfoldModelAdmin, ImportExportMixin):
|
|
24
|
-
pass
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Changes Made
|
|
28
|
-
|
|
29
|
-
1. **MRO (Method Resolution Order)**:
|
|
30
|
-
- `UnfoldModelAdmin` comes first (for template priority)
|
|
31
|
-
- `ImportExportMixin` comes second (adds import/export functionality)
|
|
32
|
-
|
|
33
|
-
2. **Custom Templates** (`django_cfg.modules.django_import_export`):
|
|
34
|
-
- Custom `change_list_*.html` templates that extend Unfold's base
|
|
35
|
-
- Properly styled import/export buttons as round icons
|
|
36
|
-
- Import button: green with upload icon
|
|
37
|
-
- Export button: blue with download icon
|
|
38
|
-
|
|
39
|
-
3. **Integration**:
|
|
40
|
-
- Uses Unfold's forms (`ImportForm`, `ExportForm`)
|
|
41
|
-
- Buttons appear in `object-tools-items` block (next to "Add" button)
|
|
42
|
-
- Full dark mode support
|
|
43
|
-
|
|
44
|
-
## Result
|
|
45
|
-
|
|
46
|
-
Now `PydanticAdmin` admins with `import_export_enabled=True` will have:
|
|
47
|
-
- ✅ Properly styled import/export buttons
|
|
48
|
-
- ✅ Consistent Unfold UI
|
|
49
|
-
- ✅ Round icon buttons matching the design system
|
|
50
|
-
- ✅ Correct positioning in the toolbar
|
|
51
|
-
|
|
52
|
-
## Usage
|
|
53
|
-
|
|
54
|
-
No changes required in user code - this fix is automatic:
|
|
55
|
-
|
|
56
|
-
```python
|
|
57
|
-
from django_cfg.modules.django_admin import AdminConfig
|
|
58
|
-
from django_cfg.modules.django_admin.base import PydanticAdmin
|
|
59
|
-
|
|
60
|
-
config = AdminConfig(
|
|
61
|
-
model=MyModel,
|
|
62
|
-
import_export_enabled=True,
|
|
63
|
-
resource_class=MyModelResource,
|
|
64
|
-
list_display=["name", "status"],
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
@admin.register(MyModel)
|
|
68
|
-
class MyModelAdmin(PydanticAdmin):
|
|
69
|
-
config = config
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
The import/export buttons will now display correctly!
|
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
# ResourceConfig & BackgroundTaskConfig Enhancement
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
|
|
5
|
-
Enhanced django-admin module with declarative import/export configuration and background task support.
|
|
6
|
-
|
|
7
|
-
## New Features
|
|
8
|
-
|
|
9
|
-
### 1. ResourceConfig
|
|
10
|
-
|
|
11
|
-
Declarative configuration for django-import-export Resource classes.
|
|
12
|
-
|
|
13
|
-
**Location:** `django_cfg/modules/django_admin/config/resource_config.py`
|
|
14
|
-
|
|
15
|
-
**Features:**
|
|
16
|
-
- Field selection and exclusion
|
|
17
|
-
- Import ID fields configuration
|
|
18
|
-
- Import/export behavior control
|
|
19
|
-
- Hook support (before/after import, per-row hooks)
|
|
20
|
-
- Export field ordering
|
|
21
|
-
- Batch processing options
|
|
22
|
-
|
|
23
|
-
**Example:**
|
|
24
|
-
```python
|
|
25
|
-
from django_cfg.modules.django_admin import ResourceConfig
|
|
26
|
-
|
|
27
|
-
resource_config = ResourceConfig(
|
|
28
|
-
fields=['host', 'port', 'username', 'password'],
|
|
29
|
-
exclude=['metadata', 'config'],
|
|
30
|
-
import_id_fields=['host', 'port'],
|
|
31
|
-
skip_unchanged=True,
|
|
32
|
-
after_import_row='apps.myapp.tasks.test_after_import',
|
|
33
|
-
batch_size=100,
|
|
34
|
-
)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### 2. BackgroundTaskConfig
|
|
38
|
-
|
|
39
|
-
Configuration for background task processing.
|
|
40
|
-
|
|
41
|
-
**Location:** `django_cfg/modules/django_admin/config/background_task_config.py`
|
|
42
|
-
|
|
43
|
-
**Features:**
|
|
44
|
-
- Task runner selection (rearq, celery, django_q, sync)
|
|
45
|
-
- Batch size configuration
|
|
46
|
-
- Timeout settings
|
|
47
|
-
- Retry policy
|
|
48
|
-
- Priority levels
|
|
49
|
-
|
|
50
|
-
**Example:**
|
|
51
|
-
```python
|
|
52
|
-
from django_cfg.modules.django_admin import BackgroundTaskConfig
|
|
53
|
-
|
|
54
|
-
background_config = BackgroundTaskConfig(
|
|
55
|
-
enabled=True,
|
|
56
|
-
task_runner='rearq',
|
|
57
|
-
batch_size=50,
|
|
58
|
-
timeout=300,
|
|
59
|
-
retry_on_failure=True,
|
|
60
|
-
max_retries=3,
|
|
61
|
-
)
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### 3. Enhanced AdminConfig
|
|
65
|
-
|
|
66
|
-
**Updated:** `django_cfg/modules/django_admin/config/admin_config.py`
|
|
67
|
-
|
|
68
|
-
**New Fields:**
|
|
69
|
-
```python
|
|
70
|
-
class AdminConfig(BaseModel):
|
|
71
|
-
# ... existing fields ...
|
|
72
|
-
|
|
73
|
-
# Import/Export enhancement
|
|
74
|
-
resource_config: Optional[ResourceConfig] = None
|
|
75
|
-
|
|
76
|
-
# Background task processing
|
|
77
|
-
background_task_config: Optional[BackgroundTaskConfig] = None
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### 4. Enhanced PydanticAdmin
|
|
81
|
-
|
|
82
|
-
**Updated:** `django_cfg/modules/django_admin/base/pydantic_admin.py`
|
|
83
|
-
|
|
84
|
-
**Key Changes:**
|
|
85
|
-
- `_generate_resource_class()` now uses ResourceConfig if provided
|
|
86
|
-
- Auto-generates Resource with hooks support
|
|
87
|
-
- Dynamically attaches hook methods to Resource class
|
|
88
|
-
|
|
89
|
-
**Hook Support:**
|
|
90
|
-
- `before_import` - Called before import starts
|
|
91
|
-
- `after_import` - Called after import completes
|
|
92
|
-
- `before_import_row` - Called before each row import
|
|
93
|
-
- `after_import_row` - Called after each row import (★ most useful)
|
|
94
|
-
|
|
95
|
-
## Usage Example
|
|
96
|
-
|
|
97
|
-
### Complete Proxy Admin with Import/Export
|
|
98
|
-
|
|
99
|
-
```python
|
|
100
|
-
from django_cfg.modules.django_admin import (
|
|
101
|
-
ActionConfig,
|
|
102
|
-
AdminConfig,
|
|
103
|
-
BackgroundTaskConfig,
|
|
104
|
-
ResourceConfig,
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
proxy_config = AdminConfig(
|
|
108
|
-
model=Proxy,
|
|
109
|
-
|
|
110
|
-
# Enable import/export with ResourceConfig
|
|
111
|
-
import_export_enabled=True,
|
|
112
|
-
resource_config=ResourceConfig(
|
|
113
|
-
fields=[
|
|
114
|
-
'host', 'port', 'proxy_type', 'proxy_mode',
|
|
115
|
-
'username', 'password',
|
|
116
|
-
'provider', 'country',
|
|
117
|
-
],
|
|
118
|
-
exclude=['metadata', 'config', 'last_error'],
|
|
119
|
-
import_id_fields=['host', 'port', 'provider'],
|
|
120
|
-
skip_unchanged=True,
|
|
121
|
-
# Auto-test after import
|
|
122
|
-
after_import_row='apps.proxies.tasks.after_import_row_test_proxy',
|
|
123
|
-
),
|
|
124
|
-
|
|
125
|
-
# Background task configuration
|
|
126
|
-
background_task_config=BackgroundTaskConfig(
|
|
127
|
-
enabled=True,
|
|
128
|
-
task_runner='rearq',
|
|
129
|
-
batch_size=50,
|
|
130
|
-
timeout=300,
|
|
131
|
-
),
|
|
132
|
-
|
|
133
|
-
# Admin actions
|
|
134
|
-
actions=[
|
|
135
|
-
ActionConfig(
|
|
136
|
-
name='test_selected_proxies',
|
|
137
|
-
description='Test selected proxies',
|
|
138
|
-
variant='warning',
|
|
139
|
-
icon='speed',
|
|
140
|
-
handler='apps.proxies.admin.actions.test_selected_proxies',
|
|
141
|
-
),
|
|
142
|
-
],
|
|
143
|
-
|
|
144
|
-
list_display=['host', 'port', 'status', 'success_rate'],
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
@admin.register(Proxy)
|
|
148
|
-
class ProxyAdmin(PydanticAdmin):
|
|
149
|
-
config = proxy_config
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Hook Implementation
|
|
153
|
-
|
|
154
|
-
```python
|
|
155
|
-
# apps/proxies/tasks.py
|
|
156
|
-
|
|
157
|
-
def after_import_row_test_proxy(row, row_result, **kwargs):
|
|
158
|
-
"""Hook called after each proxy import."""
|
|
159
|
-
|
|
160
|
-
# Skip if dry run
|
|
161
|
-
if kwargs.get('dry_run'):
|
|
162
|
-
return
|
|
163
|
-
|
|
164
|
-
# Only test new proxies
|
|
165
|
-
if row_result.import_type == 'new':
|
|
166
|
-
proxy = row_result.instance
|
|
167
|
-
|
|
168
|
-
# Queue async test
|
|
169
|
-
from api.workers import get_worker
|
|
170
|
-
worker = get_worker()
|
|
171
|
-
worker.enqueue_task(
|
|
172
|
-
'apps.proxies.tasks.test_proxy_async',
|
|
173
|
-
proxy_id=str(proxy.id)
|
|
174
|
-
)
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## Benefits
|
|
178
|
-
|
|
179
|
-
### Before (Manual Resource Class)
|
|
180
|
-
```python
|
|
181
|
-
from import_export import resources
|
|
182
|
-
|
|
183
|
-
class ProxyResource(resources.ModelResource):
|
|
184
|
-
class Meta:
|
|
185
|
-
model = Proxy
|
|
186
|
-
fields = ('host', 'port', 'username', 'password')
|
|
187
|
-
import_id_fields = ['host', 'port']
|
|
188
|
-
skip_unchanged = True
|
|
189
|
-
|
|
190
|
-
def after_import_row(self, row, row_result, **kwargs):
|
|
191
|
-
# Custom logic here
|
|
192
|
-
pass
|
|
193
|
-
|
|
194
|
-
proxy_config = AdminConfig(
|
|
195
|
-
model=Proxy,
|
|
196
|
-
import_export_enabled=True,
|
|
197
|
-
resource_class=ProxyResource, # Manual class
|
|
198
|
-
)
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
### After (Declarative ResourceConfig)
|
|
202
|
-
```python
|
|
203
|
-
proxy_config = AdminConfig(
|
|
204
|
-
model=Proxy,
|
|
205
|
-
import_export_enabled=True,
|
|
206
|
-
resource_config=ResourceConfig(
|
|
207
|
-
fields=['host', 'port', 'username', 'password'],
|
|
208
|
-
import_id_fields=['host', 'port'],
|
|
209
|
-
skip_unchanged=True,
|
|
210
|
-
after_import_row='apps.proxies.tasks.after_import_row_test_proxy',
|
|
211
|
-
),
|
|
212
|
-
)
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
**Advantages:**
|
|
216
|
-
- ✅ No separate Resource class needed
|
|
217
|
-
- ✅ All configuration in one place
|
|
218
|
-
- ✅ Type-safe with Pydantic validation
|
|
219
|
-
- ✅ Reusable across multiple admins
|
|
220
|
-
- ✅ Hook as string path (lazy import)
|
|
221
|
-
- ✅ Auto-generated Resource with full control
|
|
222
|
-
|
|
223
|
-
## Dependencies Added
|
|
224
|
-
|
|
225
|
-
**pyproject.toml updates:**
|
|
226
|
-
```toml
|
|
227
|
-
dependencies = [
|
|
228
|
-
# ... existing ...
|
|
229
|
-
"pytz>=2025.1",
|
|
230
|
-
"httpx>=0.28.1,<1.0",
|
|
231
|
-
]
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
- `pytz` - Timezone support for datetime operations
|
|
235
|
-
- `httpx` - Modern HTTP client for proxy testing
|
|
236
|
-
|
|
237
|
-
## Files Changed
|
|
238
|
-
|
|
239
|
-
### django-cfg Core
|
|
240
|
-
1. `config/resource_config.py` - NEW
|
|
241
|
-
2. `config/background_task_config.py` - NEW
|
|
242
|
-
3. `config/admin_config.py` - MODIFIED (added new configs)
|
|
243
|
-
4. `config/__init__.py` - MODIFIED (exports)
|
|
244
|
-
5. `base/pydantic_admin.py` - MODIFIED (Resource generation)
|
|
245
|
-
6. `__init__.py` - MODIFIED (exports)
|
|
246
|
-
7. `pyproject.toml` - MODIFIED (dependencies)
|
|
247
|
-
|
|
248
|
-
### stockapis Implementation
|
|
249
|
-
1. `apps/proxies/admin/proxy_admin.py` - Uses ResourceConfig
|
|
250
|
-
2. `apps/proxies/admin/actions.py` - NEW (admin actions)
|
|
251
|
-
3. `apps/proxies/services/proxy_tester.py` - NEW (testing logic)
|
|
252
|
-
4. `apps/proxies/tasks.py` - NEW (background tasks)
|
|
253
|
-
5. `apps/proxies/@docs/IMPORT_EXPORT_SETUP.md` - NEW (docs)
|
|
254
|
-
|
|
255
|
-
## Backward Compatibility
|
|
256
|
-
|
|
257
|
-
✅ **100% Backward Compatible**
|
|
258
|
-
|
|
259
|
-
Old code still works:
|
|
260
|
-
```python
|
|
261
|
-
# Old way - still works
|
|
262
|
-
proxy_config = AdminConfig(
|
|
263
|
-
model=Proxy,
|
|
264
|
-
import_export_enabled=True,
|
|
265
|
-
resource_class=MyCustomResource, # Manual class
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
# New way - alternative
|
|
269
|
-
proxy_config = AdminConfig(
|
|
270
|
-
model=Proxy,
|
|
271
|
-
import_export_enabled=True,
|
|
272
|
-
resource_config=ResourceConfig(...), # Declarative
|
|
273
|
-
)
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
Priority:
|
|
277
|
-
1. If `resource_class` provided → use it (legacy)
|
|
278
|
-
2. If `resource_config` provided → auto-generate Resource
|
|
279
|
-
3. If neither → auto-generate basic Resource
|
|
280
|
-
|
|
281
|
-
## Testing
|
|
282
|
-
|
|
283
|
-
```bash
|
|
284
|
-
# Test django-cfg imports
|
|
285
|
-
cd django-cfg-dev
|
|
286
|
-
poetry run python -c "from django_cfg.modules.django_admin import ResourceConfig, BackgroundTaskConfig; print('✅ OK')"
|
|
287
|
-
|
|
288
|
-
# Test stockapis admin
|
|
289
|
-
cd stockapis
|
|
290
|
-
poetry run python manage.py check --tag admin
|
|
291
|
-
|
|
292
|
-
# Test in browser
|
|
293
|
-
poetry run python manage.py runserver
|
|
294
|
-
# Visit: http://localhost:8000/admin/proxies/proxy/
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
## Future Enhancements
|
|
298
|
-
|
|
299
|
-
- [ ] `ResourceConfig.field_widgets` - Custom widgets for fields
|
|
300
|
-
- [ ] `ResourceConfig.validators` - Custom validation rules
|
|
301
|
-
- [ ] `ResourceConfig.transformers` - Data transformation before import
|
|
302
|
-
- [ ] Progress tracking for large imports
|
|
303
|
-
- [ ] Import history and rollback
|
|
304
|
-
- [ ] Scheduled imports via cron
|
|
305
|
-
- [ ] API endpoint for programmatic imports
|
|
306
|
-
|
|
307
|
-
## Migration Guide
|
|
308
|
-
|
|
309
|
-
### For Existing Projects
|
|
310
|
-
|
|
311
|
-
1. Update django-cfg to latest version
|
|
312
|
-
2. Optional: Replace manual Resource classes with ResourceConfig
|
|
313
|
-
3. Optional: Add BackgroundTaskConfig for async operations
|
|
314
|
-
4. Optional: Add after_import_row hooks for post-import processing
|
|
315
|
-
|
|
316
|
-
### Example Migration
|
|
317
|
-
|
|
318
|
-
**Before:**
|
|
319
|
-
```python
|
|
320
|
-
# resources.py
|
|
321
|
-
class ProxyResource(resources.ModelResource):
|
|
322
|
-
class Meta:
|
|
323
|
-
model = Proxy
|
|
324
|
-
fields = ('host', 'port')
|
|
325
|
-
|
|
326
|
-
# admin.py
|
|
327
|
-
config = AdminConfig(
|
|
328
|
-
model=Proxy,
|
|
329
|
-
import_export_enabled=True,
|
|
330
|
-
resource_class=ProxyResource,
|
|
331
|
-
)
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
**After:**
|
|
335
|
-
```python
|
|
336
|
-
# admin.py only - no resources.py needed
|
|
337
|
-
config = AdminConfig(
|
|
338
|
-
model=Proxy,
|
|
339
|
-
import_export_enabled=True,
|
|
340
|
-
resource_config=ResourceConfig(
|
|
341
|
-
fields=['host', 'port'],
|
|
342
|
-
),
|
|
343
|
-
)
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
## Version
|
|
347
|
-
|
|
348
|
-
- **django-cfg**: 1.4.106+
|
|
349
|
-
- **Python**: 3.12+
|
|
350
|
-
- **Django**: 5.2+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|