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.

Files changed (24) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/modules/django_admin/__init__.py +6 -0
  3. django_cfg/modules/django_admin/base/pydantic_admin.py +101 -0
  4. django_cfg/modules/django_admin/config/__init__.py +5 -0
  5. django_cfg/modules/django_admin/config/admin_config.py +7 -0
  6. django_cfg/modules/django_admin/config/documentation_config.py +406 -0
  7. django_cfg/modules/django_admin/config/field_config.py +87 -0
  8. django_cfg/modules/django_admin/templates/django_admin/change_form_docs.html +23 -0
  9. django_cfg/modules/django_admin/templates/django_admin/change_list_docs.html +23 -0
  10. django_cfg/modules/django_admin/templates/django_admin/documentation_block.html +303 -0
  11. django_cfg/modules/django_admin/templates/django_admin/markdown_docs_block.html +37 -0
  12. django_cfg/modules/django_admin/utils/__init__.py +3 -0
  13. django_cfg/modules/django_admin/utils/html_builder.py +94 -1
  14. django_cfg/modules/django_admin/utils/markdown_renderer.py +360 -0
  15. django_cfg/modules/django_admin/utils/mermaid_plugin.py +288 -0
  16. django_cfg/pyproject.toml +2 -2
  17. {django_cfg-1.4.109.dist-info → django_cfg-1.4.111.dist-info}/METADATA +2 -1
  18. {django_cfg-1.4.109.dist-info → django_cfg-1.4.111.dist-info}/RECORD +21 -17
  19. django_cfg/modules/django_admin/CHANGELOG_CODE_METHODS.md +0 -153
  20. django_cfg/modules/django_admin/IMPORT_EXPORT_FIX.md +0 -72
  21. django_cfg/modules/django_admin/RESOURCE_CONFIG_ENHANCEMENT.md +0 -350
  22. {django_cfg-1.4.109.dist-info → django_cfg-1.4.111.dist-info}/WHEEL +0 -0
  23. {django_cfg-1.4.109.dist-info → django_cfg-1.4.111.dist-info}/entry_points.txt +0 -0
  24. {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=fOg2eKyhb6AAON0Y5yU5AoTiDjb3u-C0JommQ8cTqgg,1621
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/CHANGELOG_CODE_METHODS.md,sha256=HfE_rUlovx1zX_1hkzQsjwghaFvIvUWjZ_Aume8lhIs,3823
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=v2U-Tm_zWkDsh1CGmYL-xJdsVvHKX7MjUMrFwpY6wpw,20879
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=IjD2pJBb_rboj9V4_4uzBmzIIzm4a5Xy8XIieL5myww,755
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=7ozVnLqJcUrHu-UK-PdOfuX2UOvR1gkigThgQsfO0wY,5030
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/field_config.py,sha256=TXdmz1GHC0N7euzqgB9p5EhFCYzsEobb8VSgS4kG4ho,8928
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=DkMCGBbSe6Xfgdse-C8JkcqTRxtq3k2tMKxt39F1GfA,649
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=d-_laj5Yz7gY0Xkwg1vNIlcK-N8F1Z3atM4-t1584pw,9793
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=yWqSAdesaQe0xiDRPoQw25Opq0yhKyr1nZM-k058-E4,8614
1054
- django_cfg-1.4.109.dist-info/METADATA,sha256=Xml6V2JxDfn4DHkJAC0BLkupFncQDqJsON9sROSJvms,23801
1055
- django_cfg-1.4.109.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1056
- django_cfg-1.4.109.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1057
- django_cfg-1.4.109.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1058
- django_cfg-1.4.109.dist-info/RECORD,,
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+