django-cfg 1.4.106__py3-none-any.whl → 1.4.108__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 (137) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/views/profile.py +19 -9
  3. django_cfg/apps/centrifugo/views/admin_api.py +4 -7
  4. django_cfg/apps/centrifugo/views/monitoring.py +3 -6
  5. django_cfg/apps/centrifugo/views/testing_api.py +3 -6
  6. django_cfg/apps/dashboard/services/system_health_service.py +16 -11
  7. django_cfg/apps/dashboard/views/activity_views.py +3 -5
  8. django_cfg/apps/dashboard/views/apizones_views.py +4 -5
  9. django_cfg/apps/dashboard/views/charts_views.py +4 -5
  10. django_cfg/apps/dashboard/views/overview_views.py +4 -5
  11. django_cfg/apps/dashboard/views/statistics_views.py +4 -5
  12. django_cfg/apps/dashboard/views/system_views.py +4 -5
  13. django_cfg/apps/knowbase/__init__.py +2 -2
  14. django_cfg/apps/knowbase/apps.py +2 -8
  15. django_cfg/apps/knowbase/views/base.py +9 -4
  16. django_cfg/apps/support/views/api.py +16 -7
  17. django_cfg/apps/tasks/__init__.py +61 -2
  18. django_cfg/apps/tasks/admin/__init__.py +3 -10
  19. django_cfg/apps/tasks/admin/config.py +98 -0
  20. django_cfg/apps/tasks/admin/task_log.py +265 -0
  21. django_cfg/apps/tasks/apps.py +7 -9
  22. django_cfg/apps/tasks/filters/__init__.py +10 -0
  23. django_cfg/apps/tasks/filters/task_log.py +121 -0
  24. django_cfg/apps/tasks/migrations/0001_initial.py +196 -0
  25. django_cfg/apps/tasks/models/__init__.py +4 -0
  26. django_cfg/apps/tasks/models/task_log.py +246 -0
  27. django_cfg/apps/tasks/serializers/__init__.py +28 -0
  28. django_cfg/apps/tasks/serializers/task_log.py +249 -0
  29. django_cfg/apps/tasks/services/__init__.py +10 -0
  30. django_cfg/apps/tasks/services/client/__init__.py +7 -0
  31. django_cfg/apps/tasks/services/client/client.py +234 -0
  32. django_cfg/apps/tasks/services/config_helper.py +63 -0
  33. django_cfg/apps/tasks/services/sync.py +204 -0
  34. django_cfg/apps/tasks/urls.py +7 -13
  35. django_cfg/apps/tasks/views/__init__.py +4 -10
  36. django_cfg/apps/tasks/views/task_log.py +41 -0
  37. django_cfg/apps/tasks/views/task_log_base.py +41 -0
  38. django_cfg/apps/tasks/views/task_log_overview.py +100 -0
  39. django_cfg/apps/tasks/views/task_log_related.py +41 -0
  40. django_cfg/apps/tasks/views/task_log_stats.py +91 -0
  41. django_cfg/apps/tasks/views/task_log_timeline.py +81 -0
  42. django_cfg/apps/urls.py +0 -1
  43. django_cfg/cli/commands/info.py +1 -1
  44. django_cfg/cli/utils.py +1 -1
  45. django_cfg/core/base/config_model.py +1 -1
  46. django_cfg/core/builders/apps_builder.py +1 -1
  47. django_cfg/core/generation/integration_generators/__init__.py +1 -1
  48. django_cfg/core/generation/integration_generators/tasks.py +14 -18
  49. django_cfg/core/generation/security_generators/crypto_fields.py +2 -1
  50. django_cfg/core/integration/display/startup.py +1 -1
  51. django_cfg/mixins/__init__.py +12 -0
  52. django_cfg/mixins/admin_api.py +37 -0
  53. django_cfg/mixins/client_api.py +39 -0
  54. django_cfg/models/django/constance.py +2 -8
  55. django_cfg/models/django/crypto_fields.py +13 -48
  56. django_cfg/models/tasks/__init__.py +8 -10
  57. django_cfg/models/tasks/backends.py +76 -207
  58. django_cfg/models/tasks/config.py +20 -127
  59. django_cfg/models/tasks/utils.py +17 -29
  60. django_cfg/modules/django_admin/RESOURCE_CONFIG_ENHANCEMENT.md +350 -0
  61. django_cfg/modules/django_admin/__init__.py +4 -0
  62. django_cfg/modules/django_admin/base/pydantic_admin.py +70 -15
  63. django_cfg/modules/django_admin/config/__init__.py +4 -0
  64. django_cfg/modules/django_admin/config/admin_config.py +13 -1
  65. django_cfg/modules/django_admin/config/background_task_config.py +76 -0
  66. django_cfg/modules/django_admin/config/resource_config.py +129 -0
  67. django_cfg/modules/django_client/management/commands/generate_client.py +13 -1
  68. django_cfg/modules/django_unfold/navigation.py +121 -22
  69. django_cfg/pyproject.toml +2 -2
  70. django_cfg/registry/core.py +1 -1
  71. django_cfg/static/frontend/admin.zip +0 -0
  72. {django_cfg-1.4.106.dist-info → django_cfg-1.4.108.dist-info}/METADATA +5 -3
  73. {django_cfg-1.4.106.dist-info → django_cfg-1.4.108.dist-info}/RECORD +77 -111
  74. django_cfg/apps/tasks/admin/actions.py +0 -29
  75. django_cfg/apps/tasks/admin/tasks_admin.py +0 -154
  76. django_cfg/apps/tasks/api/serializers.py +0 -82
  77. django_cfg/apps/tasks/api/views.py +0 -571
  78. django_cfg/apps/tasks/serializers.py +0 -82
  79. django_cfg/apps/tasks/static/tasks/css/dashboard-alpine.css +0 -299
  80. django_cfg/apps/tasks/static/tasks/css/dashboard.css +0 -120
  81. django_cfg/apps/tasks/static/tasks/js/alpine/README.md +0 -47
  82. django_cfg/apps/tasks/static/tasks/js/alpine/actions/index.js +0 -8
  83. django_cfg/apps/tasks/static/tasks/js/alpine/actions/management.js +0 -123
  84. django_cfg/apps/tasks/static/tasks/js/alpine/actions/pagination.js +0 -21
  85. django_cfg/apps/tasks/static/tasks/js/alpine/actions/tasks.js +0 -101
  86. django_cfg/apps/tasks/static/tasks/js/alpine/actions/workers.js +0 -59
  87. django_cfg/apps/tasks/static/tasks/js/alpine/computed.js +0 -35
  88. django_cfg/apps/tasks/static/tasks/js/alpine/index.js +0 -148
  89. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/index.js +0 -36
  90. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/overview.js +0 -37
  91. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/queues.js +0 -27
  92. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/tasks.js +0 -32
  93. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/workers.js +0 -21
  94. django_cfg/apps/tasks/static/tasks/js/alpine/state.js +0 -36
  95. django_cfg/apps/tasks/static/tasks/js/alpine/utils/formatters.js +0 -42
  96. django_cfg/apps/tasks/static/tasks/js/alpine/utils/helpers.js +0 -68
  97. django_cfg/apps/tasks/static/tasks/js/dashboard-alpine.js +0 -725
  98. django_cfg/apps/tasks/tasks/__init__.py +0 -10
  99. django_cfg/apps/tasks/tasks/demo_tasks.py +0 -127
  100. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +0 -71
  101. django_cfg/apps/tasks/templates/tasks/components/overview_content.html +0 -94
  102. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +0 -44
  103. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +0 -45
  104. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -151
  105. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +0 -61
  106. django_cfg/apps/tasks/templates/tasks/components/tasks_mjs_integration.html +0 -269
  107. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +0 -60
  108. django_cfg/apps/tasks/templates/tasks/layout/base.html +0 -20
  109. django_cfg/apps/tasks/templates/tasks/pages/dashboard-improved.html +0 -168
  110. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +0 -77
  111. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +0 -40
  112. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +0 -40
  113. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +0 -86
  114. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +0 -90
  115. django_cfg/apps/tasks/urls_admin.py +0 -15
  116. django_cfg/apps/tasks/utils/__init__.py +0 -1
  117. django_cfg/apps/tasks/utils/simulator.py +0 -353
  118. django_cfg/apps/tasks/views/api.py +0 -571
  119. django_cfg/apps/tasks/views/dashboard.py +0 -89
  120. django_cfg/management/commands/rundramatiq.py +0 -24
  121. django_cfg/management/commands/rundramatiq_simulator.py +0 -22
  122. django_cfg/management/commands/task_clear.py +0 -25
  123. django_cfg/management/commands/task_status.py +0 -24
  124. django_cfg/modules/django_tasks/__init__.py +0 -29
  125. django_cfg/modules/django_tasks/dramatiq_setup.py +0 -20
  126. django_cfg/modules/django_tasks/factory.py +0 -127
  127. django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
  128. django_cfg/modules/django_tasks/management/commands/rundramatiq.py +0 -253
  129. django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +0 -436
  130. django_cfg/modules/django_tasks/management/commands/task_clear.py +0 -226
  131. django_cfg/modules/django_tasks/management/commands/task_status.py +0 -257
  132. django_cfg/modules/django_tasks/service.py +0 -281
  133. django_cfg/modules/django_tasks/settings.py +0 -107
  134. /django_cfg/{modules/django_tasks/management → apps/tasks/migrations}/__init__.py +0 -0
  135. {django_cfg-1.4.106.dist-info → django_cfg-1.4.108.dist-info}/WHEEL +0 -0
  136. {django_cfg-1.4.106.dist-info → django_cfg-1.4.108.dist-info}/entry_points.txt +0 -0
  137. {django_cfg-1.4.106.dist-info → django_cfg-1.4.108.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,350 @@
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+
@@ -46,6 +46,7 @@ Example:
46
46
  from .config import (
47
47
  ActionConfig,
48
48
  AdminConfig,
49
+ BackgroundTaskConfig,
49
50
  BadgeField,
50
51
  BooleanField,
51
52
  CurrencyField,
@@ -53,6 +54,7 @@ from .config import (
53
54
  FieldConfig,
54
55
  FieldsetConfig,
55
56
  ImageField,
57
+ ResourceConfig,
56
58
  TextField,
57
59
  UserField,
58
60
  )
@@ -101,6 +103,8 @@ __all__ = [
101
103
  "FieldConfig",
102
104
  "FieldsetConfig",
103
105
  "ActionConfig",
106
+ "ResourceConfig",
107
+ "BackgroundTaskConfig",
104
108
  # Specialized Field Types
105
109
  "BadgeField",
106
110
  "BooleanField",
@@ -154,37 +154,92 @@ class PydanticAdminMixin:
154
154
 
155
155
  @classmethod
156
156
  def _generate_resource_class(cls, config: AdminConfig):
157
- """Auto-generate a ModelResource class for import/export."""
157
+ """
158
+ Auto-generate a ModelResource class for import/export.
159
+
160
+ Uses ResourceConfig if provided, otherwise generates basic Resource.
161
+ """
158
162
  from import_export import resources
159
163
 
160
164
  target_model = config.model
165
+ resource_config = config.resource_config
161
166
 
162
- # Get all model fields
163
- model_fields = []
164
- for field in target_model._meta.get_fields():
165
- # Skip relations and auto fields that shouldn't be imported
166
- if field.concrete and not field.many_to_many:
167
- # Skip password fields for security
168
- if 'password' not in field.name.lower():
169
- model_fields.append(field.name)
170
-
171
- # Create dynamic resource class with explicit Meta attributes
167
+ # Determine fields to include
168
+ if resource_config and resource_config.fields:
169
+ # Use explicitly specified fields
170
+ model_fields = resource_config.fields
171
+ else:
172
+ # Auto-detect fields from model
173
+ model_fields = []
174
+ for field in target_model._meta.get_fields():
175
+ # Skip relations and auto fields that shouldn't be imported
176
+ if field.concrete and not field.many_to_many:
177
+ # Skip password fields for security
178
+ if 'password' not in field.name.lower():
179
+ model_fields.append(field.name)
180
+
181
+ # Apply exclusions if specified
182
+ if resource_config and resource_config.exclude:
183
+ model_fields = [f for f in model_fields if f not in resource_config.exclude]
184
+
185
+ # Build Meta attributes
172
186
  meta_attrs = {
173
187
  'model': target_model,
174
188
  'fields': tuple(model_fields),
175
- 'import_id_fields': ['id'] if 'id' in model_fields else [],
176
- 'skip_unchanged': True,
177
- 'report_skipped': True,
178
189
  }
179
190
 
191
+ # Add ResourceConfig settings
192
+ if resource_config:
193
+ meta_attrs['import_id_fields'] = resource_config.import_id_fields
194
+ meta_attrs['skip_unchanged'] = resource_config.skip_unchanged
195
+ meta_attrs['report_skipped'] = resource_config.report_skipped
196
+ meta_attrs['use_transactions'] = resource_config.use_transactions
197
+
198
+ if resource_config.export_order:
199
+ meta_attrs['export_order'] = tuple(resource_config.export_order)
200
+ else:
201
+ # Default settings
202
+ meta_attrs['import_id_fields'] = ['id'] if 'id' in model_fields else []
203
+ meta_attrs['skip_unchanged'] = True
204
+ meta_attrs['report_skipped'] = True
205
+
180
206
  # Create Meta class
181
207
  ResourceMeta = type('Meta', (), meta_attrs)
182
208
 
209
+ # Build Resource class attributes (methods + Meta)
210
+ resource_attrs = {'Meta': ResourceMeta}
211
+
212
+ # Add hooks from ResourceConfig
213
+ if resource_config:
214
+ # before_import hook
215
+ if resource_config.before_import:
216
+ hook = resource_config.get_callable('before_import')
217
+ if hook:
218
+ resource_attrs['before_import'] = hook
219
+
220
+ # after_import hook
221
+ if resource_config.after_import:
222
+ hook = resource_config.get_callable('after_import')
223
+ if hook:
224
+ resource_attrs['after_import'] = hook
225
+
226
+ # before_import_row hook
227
+ if resource_config.before_import_row:
228
+ hook = resource_config.get_callable('before_import_row')
229
+ if hook:
230
+ resource_attrs['before_import_row'] = hook
231
+
232
+ # after_import_row hook
233
+ if resource_config.after_import_row:
234
+ hook = resource_config.get_callable('after_import_row')
235
+ if hook:
236
+ resource_attrs['after_import_row'] = hook
237
+
183
238
  # Create Resource class
184
239
  AutoGeneratedResource = type(
185
240
  f'{target_model.__name__}Resource',
186
241
  (resources.ModelResource,),
187
- {'Meta': ResourceMeta}
242
+ resource_attrs
188
243
  )
189
244
 
190
245
  return AutoGeneratedResource
@@ -4,6 +4,7 @@ Configuration models for declarative Django Admin.
4
4
 
5
5
  from .action_config import ActionConfig
6
6
  from .admin_config import AdminConfig
7
+ from .background_task_config import BackgroundTaskConfig
7
8
  from .field_config import (
8
9
  FieldConfig,
9
10
  BadgeField,
@@ -15,12 +16,15 @@ from .field_config import (
15
16
  UserField,
16
17
  )
17
18
  from .fieldset_config import FieldsetConfig
19
+ from .resource_config import ResourceConfig
18
20
 
19
21
  __all__ = [
20
22
  "AdminConfig",
21
23
  "FieldConfig",
22
24
  "FieldsetConfig",
23
25
  "ActionConfig",
26
+ "ResourceConfig",
27
+ "BackgroundTaskConfig",
24
28
  # Specialized Field Types
25
29
  "BadgeField",
26
30
  "BooleanField",
@@ -8,8 +8,10 @@ from django.db import models
8
8
  from pydantic import BaseModel, ConfigDict, Field
9
9
 
10
10
  from .action_config import ActionConfig
11
+ from .background_task_config import BackgroundTaskConfig
11
12
  from .field_config import FieldConfig
12
13
  from .fieldset_config import FieldsetConfig
14
+ from .resource_config import ResourceConfig
13
15
 
14
16
 
15
17
  class AdminConfig(BaseModel):
@@ -127,7 +129,17 @@ class AdminConfig(BaseModel):
127
129
 
128
130
  # Import/Export options
129
131
  import_export_enabled: bool = Field(False, description="Enable import/export functionality")
130
- resource_class: Optional[Type] = Field(None, description="Resource class for import/export")
132
+ resource_class: Optional[Type] = Field(None, description="Custom Resource class for import/export")
133
+ resource_config: Optional[ResourceConfig] = Field(
134
+ None,
135
+ description="Declarative resource configuration (alternative to resource_class)"
136
+ )
137
+
138
+ # Background task processing
139
+ background_task_config: Optional[BackgroundTaskConfig] = Field(
140
+ None,
141
+ description="Configuration for background task processing"
142
+ )
131
143
 
132
144
  def get_display_field_config(self, field_name: str) -> Optional[FieldConfig]:
133
145
  """Get FieldConfig for a specific field."""
@@ -0,0 +1,76 @@
1
+ """
2
+ Background task configuration for async operations.
3
+ """
4
+
5
+ from typing import Literal, Optional
6
+
7
+ from pydantic import BaseModel, ConfigDict, Field
8
+
9
+
10
+ class BackgroundTaskConfig(BaseModel):
11
+ """
12
+ Configuration for background task processing.
13
+
14
+ Used for async operations like testing imported items,
15
+ bulk processing, or long-running operations.
16
+
17
+ Example:
18
+ ```python
19
+ background_config = BackgroundTaskConfig(
20
+ enabled=True,
21
+ task_runner='rearq',
22
+ batch_size=100,
23
+ timeout=300,
24
+ )
25
+ ```
26
+ """
27
+
28
+ model_config = ConfigDict(
29
+ validate_assignment=True,
30
+ extra="forbid"
31
+ )
32
+
33
+ enabled: bool = Field(
34
+ True,
35
+ description="Enable background task processing"
36
+ )
37
+
38
+ task_runner: Literal['rearq', 'celery', 'django_q', 'sync'] = Field(
39
+ 'rearq',
40
+ description="Task runner to use for background operations"
41
+ )
42
+
43
+ batch_size: int = Field(
44
+ 100,
45
+ ge=1,
46
+ le=10000,
47
+ description="Number of items to process in each batch"
48
+ )
49
+
50
+ timeout: int = Field(
51
+ 300,
52
+ ge=1,
53
+ le=3600,
54
+ description="Task timeout in seconds (max 1 hour)"
55
+ )
56
+
57
+ retry_on_failure: bool = Field(
58
+ True,
59
+ description="Automatically retry failed tasks"
60
+ )
61
+
62
+ max_retries: int = Field(
63
+ 3,
64
+ ge=0,
65
+ le=10,
66
+ description="Maximum number of retry attempts"
67
+ )
68
+
69
+ priority: Literal['high', 'normal', 'low'] = Field(
70
+ 'normal',
71
+ description="Task priority in queue"
72
+ )
73
+
74
+ def should_use_background(self) -> bool:
75
+ """Check if background processing should be used."""
76
+ return self.enabled and self.task_runner != 'sync'