django-cfg 1.1.77__py3-none-any.whl → 1.1.78__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 CHANGED
@@ -38,7 +38,7 @@ default_app_config = "django_cfg.apps.DjangoCfgConfig"
38
38
  from typing import TYPE_CHECKING
39
39
 
40
40
  # Version information
41
- __version__ = "1.1.77"
41
+ __version__ = "1.1.78"
42
42
  __author__ = "Unrealos Team"
43
43
  __email__ = "info@unrealos.com"
44
44
  __license__ = "MIT"
@@ -39,7 +39,7 @@ class ConstanceField(BaseModel):
39
39
  max_length=500,
40
40
  )
41
41
 
42
- field_type: Literal["str", "int", "float", "bool", "choice"] = Field(
42
+ field_type: Literal["str", "int", "float", "bool", "choice", "longtext", "description"] = Field(
43
43
  default="str",
44
44
  description="Field type for form rendering and validation",
45
45
  )
@@ -77,6 +77,8 @@ class ConstanceField(BaseModel):
77
77
  "float": float,
78
78
  "bool": int, # Use int for boolean (0/1)
79
79
  "choice": str, # Choices are typically strings
80
+ "longtext": str, # Long text fields are strings
81
+ "description": str, # Description fields are strings
80
82
  }
81
83
 
82
84
  field_python_type = type_mapping.get(self.field_type, str)
@@ -107,6 +109,20 @@ class ConstanceField(BaseModel):
107
109
  "django.forms.FloatField",
108
110
  {"widget": "unfold.widgets.UnfoldAdminTextInputWidget"},
109
111
  ],
112
+ "longtext": [
113
+ "django.forms.CharField",
114
+ {
115
+ "widget": "unfold.widgets.UnfoldAdminTextareaWidget",
116
+ "attrs": {"rows": 4, "cols": 80, "class": "max-w-4xl"},
117
+ },
118
+ ],
119
+ "description": [
120
+ "django.forms.CharField",
121
+ {
122
+ "widget": "unfold.widgets.UnfoldAdminTextareaWidget",
123
+ "attrs": {"rows": 3, "cols": 60, "class": "max-w-3xl"},
124
+ },
125
+ ],
110
126
  # Boolean fields will use default Unfold configuration from UNFOLD_CONSTANCE_ADDITIONAL_FIELDS
111
127
  }
112
128
 
@@ -173,15 +189,56 @@ class ConstanceConfig(BaseModel):
173
189
  return {group: tuple(fields) for group, fields in fieldsets.items()}
174
190
 
175
191
  def get_additional_fields_dict(self) -> Dict[str, List[Any]]:
176
- """Generate CONSTANCE_ADDITIONAL_FIELDS dictionary."""
192
+ """Generate CONSTANCE_ADDITIONAL_FIELDS dictionary with enhanced widgets."""
177
193
  from unfold.contrib.constance.settings import UNFOLD_CONSTANCE_ADDITIONAL_FIELDS
178
194
 
179
195
  additional_fields = dict(UNFOLD_CONSTANCE_ADDITIONAL_FIELDS)
180
196
 
181
- # Add custom field configurations
197
+ # Add custom field configurations for better text handling
198
+ additional_fields.update({
199
+ "text": [
200
+ "django.forms.CharField",
201
+ {
202
+ "widget": "unfold.widgets.UnfoldAdminTextInputWidget",
203
+ "attrs": {"class": "max-w-2xl"},
204
+ },
205
+ ],
206
+ "longtext": [
207
+ "django.forms.CharField",
208
+ {
209
+ "widget": "unfold.widgets.UnfoldAdminTextareaWidget",
210
+ "attrs": {"rows": 4, "cols": 80, "class": "max-w-4xl"},
211
+ },
212
+ ],
213
+ "description": [
214
+ "django.forms.CharField",
215
+ {
216
+ "widget": "unfold.widgets.UnfoldAdminTextareaWidget",
217
+ "attrs": {"rows": 3, "cols": 60, "class": "max-w-3xl"},
218
+ },
219
+ ],
220
+ })
221
+
222
+ # Override specific fields with custom widgets
182
223
  for field in self.fields:
183
224
  if field.field_type == "choice" and field.choices:
184
225
  additional_fields[field.name] = field.to_constance_field_config()
226
+ elif field.field_type in ["longtext", "description"]:
227
+ # Use field-specific configuration to override Unfold defaults
228
+ additional_fields[field.name] = field.to_constance_field_config()
229
+
230
+ # CRITICAL FIX: Override fields that should use textarea based on field_type
231
+ # This ensures longtext and description fields use textarea regardless of their Python type
232
+ for field in self.fields:
233
+ if field.field_type in ["longtext", "description"]:
234
+ # Force textarea widget for these field types, overriding any type-based defaults
235
+ additional_fields[field.name] = [
236
+ "django.forms.CharField",
237
+ {
238
+ "widget": "unfold.widgets.UnfoldAdminTextareaWidget",
239
+ "attrs": field.to_constance_field_config()[1]["attrs"],
240
+ },
241
+ ]
185
242
 
186
243
  return additional_fields
187
244
 
@@ -0,0 +1,113 @@
1
+ {% extends "admin/base_site.html" %}
2
+
3
+ {% load admin_list static i18n %}
4
+
5
+ {% block extrahead %}
6
+ {% url 'admin:jsi18n' as jsi18nurl %}
7
+ <script type="text/javascript" src="{{ jsi18nurl|default:'../../jsi18n/' }}"></script>
8
+ {{ block.super }}
9
+ {{ media.js }}
10
+ <script type="text/javascript" src="{% static 'admin/js/constance.js' %}"></script>
11
+
12
+ <!-- Enhanced Constance UI Styles -->
13
+ <style>
14
+ /* Smooth transitions for all interactive elements */
15
+ .constance-expandable {
16
+ transition: all 0.2s ease-in-out;
17
+ }
18
+
19
+ /* Better hover states */
20
+ .constance tr:hover {
21
+ background-color: rgba(0, 0, 0, 0.02);
22
+ }
23
+
24
+ .dark .constance tr:hover {
25
+ background-color: rgba(255, 255, 255, 0.02);
26
+ }
27
+
28
+ /* Improved focus states for accessibility */
29
+ .constance a:focus,
30
+ .constance button:focus {
31
+ outline: 2px solid rgb(59, 130, 246);
32
+ outline-offset: 2px;
33
+ border-radius: 4px;
34
+ }
35
+
36
+ /* Better spacing for long text */
37
+ .constance .break-words {
38
+ word-break: break-word;
39
+ overflow-wrap: break-word;
40
+ hyphens: auto;
41
+ }
42
+
43
+ /* Smooth animations for expand/collapse */
44
+ .constance [x-show] {
45
+ transition: opacity 0.2s ease-in-out;
46
+ }
47
+
48
+ /* Icon rotation animation */
49
+ .constance .material-symbols-outlined {
50
+ transition: transform 0.3s ease-in-out;
51
+ }
52
+ </style>
53
+ {% endblock %}
54
+
55
+ {% block content %}
56
+ <div id="content-main" class="constance">
57
+ <div id="changelist" class="flex flex-col gap-8 w-full">
58
+ <form id="changelist-form" action="" method="post" enctype="multipart/form-data" novalidate class="flex flex-col gap-8 w-full">
59
+ {% csrf_token %}
60
+
61
+ {% include "unfold/helpers/form_errors.html" with errors=form.non_field_errors %}
62
+
63
+ {% for field in form.hidden_fields %}
64
+ {{ field }}
65
+ {% endfor %}
66
+
67
+ <!-- Collapse/Expand All Controls -->
68
+ <div class="flex justify-between items-center mb-4" x-data="{ allExpanded: true }">
69
+ <div class="text-sm text-base-600 dark:text-base-400">
70
+ {% trans "Configuration Settings" %}
71
+ </div>
72
+ <div class="flex gap-2">
73
+ <button type="button"
74
+ @click="allExpanded = false; $dispatch('collapse-all')"
75
+ class="px-3 py-1 text-xs bg-base-100 hover:bg-base-200 dark:bg-base-800 dark:hover:bg-base-700 border border-base-300 dark:border-base-600 rounded transition-colors">
76
+ {% trans "Collapse All" %}
77
+ </button>
78
+ <button type="button"
79
+ @click="allExpanded = true; $dispatch('expand-all')"
80
+ class="px-3 py-1 text-xs bg-base-100 hover:bg-base-200 dark:bg-base-800 dark:hover:bg-base-700 border border-base-300 dark:border-base-600 rounded transition-colors">
81
+ {% trans "Expand All" %}
82
+ </button>
83
+ </div>
84
+ </div>
85
+
86
+ <div class="border border-base-200 rounded-default overflow-x-auto simplebar-horizontal-scrollbar-top dark:border-base-800" data-simplebar data-simplebar-auto-hide="false">
87
+ <table class="w-full border-collapse">
88
+ {% if fieldsets %}
89
+ {% for fieldset in fieldsets %}
90
+ {% with config_values=fieldset.config_values %}
91
+ {% include "admin/constance/includes/results_list.html" %}
92
+ {% endwith %}
93
+ {% endfor %}
94
+ {% else %}
95
+ {% include "admin/constance/includes/results_list.html" %}
96
+ {% endif %}
97
+
98
+ <tfoot>
99
+ <tr>
100
+ <td class="border-t border-base-200 px-3 py-2 dark:border-base-800" colspan="100%">
101
+ <div class="flex justify-end">
102
+ {% trans "Save" as title %}
103
+ {% include "unfold/helpers/submit.html" with title=title %}
104
+ </div>
105
+ </td>
106
+ </tr>
107
+ </tfoot>
108
+ </table>
109
+ </div>
110
+ </form>
111
+ </div>
112
+ </div>
113
+ {% endblock %}
@@ -0,0 +1,24 @@
1
+ {% load i18n %}
2
+
3
+ {% if item.default %}
4
+ {% if item.default|length > 80 %}
5
+ <!-- Long default value with smart truncation -->
6
+ <div x-data="{ expanded: false }" class="constance-expandable">
7
+ <div x-show="!expanded" @click="expanded = true" class="cursor-pointer hover:bg-base-50 dark:hover:bg-base-800 p-1 rounded transition-colors">
8
+ {% include "unfold/helpers/label.html" with text=item.default|truncatechars:60 %}
9
+ <br><small class="text-base-400 dark:text-base-500 text-xs">{% trans "Click to expand" %}</small>
10
+ </div>
11
+ <div x-show="expanded" @click="expanded = false" class="cursor-pointer">
12
+ <div class="max-w-xs break-words whitespace-pre-wrap text-xs bg-base-100 dark:bg-base-800 p-2 rounded border border-base-200 dark:border-base-700 shadow-sm">
13
+ {{ item.default }}
14
+ </div>
15
+ <small class="text-base-400 dark:text-base-500 text-xs">{% trans "Click to collapse" %}</small>
16
+ </div>
17
+ </div>
18
+ {% else %}
19
+ <!-- Short default value - normal display -->
20
+ {% include "unfold/helpers/label.html" with text=item.default %}
21
+ {% endif %}
22
+ {% else %}
23
+ <span class="text-base-400 dark:text-base-500">-</span>
24
+ {% endif %}
@@ -0,0 +1,15 @@
1
+ {% load i18n %}
2
+
3
+ {% if fieldset.title %}
4
+ <tr x-on:click="rowsOpen = !rowsOpen" class="cursor-pointer hover:bg-base-25 dark:hover:bg-base-900 transition-colors">
5
+ <th class="bg-base-50 border-t border-base-200 font-semibold px-3 py-2 text-left text-font-important-light dark:text-font-important-dark dark:border-base-800 dark:bg-white/[.04] {% if forloop.first %}border-t-0{% endif %}" colspan="100%">
6
+ <div class="flex items-center justify-between">
7
+ <span>{{ fieldset.title }}</span>
8
+
9
+ <span class="material-symbols-outlined select-none transition-transform duration-300 ease-in-out" title="{% trans "Toggle" %}" x-bind:class="rowsOpen ? 'rotate-180' : ''">
10
+ expand_more
11
+ </span>
12
+ </div>
13
+ </th>
14
+ </tr>
15
+ {% endif %}
@@ -0,0 +1,16 @@
1
+ {% load admin_list static i18n %}
2
+
3
+ <tbody x-data="{ rowsOpen: {% if forloop.first %}true{% else %}false{% endif %} }"
4
+ @collapse-all.window="rowsOpen = false"
5
+ @expand-all.window="rowsOpen = true">
6
+ <!-- Fieldset Header -->
7
+ {% include "admin/constance/includes/fieldset_header.html" %}
8
+
9
+ <!-- Table Headers -->
10
+ {% include "admin/constance/includes/table_headers.html" %}
11
+
12
+ <!-- Setting Rows -->
13
+ {% for item in config_values %}
14
+ {% include "admin/constance/includes/setting_row.html" %}
15
+ {% endfor %}
16
+ </tbody>
@@ -0,0 +1,50 @@
1
+ {% load i18n %}
2
+
3
+ <tr class="border-t border-base-200 h-[55px] *:px-3 *:py-2 *:align-middle dark:border-base-800 hover:bg-base-25 dark:hover:bg-base-900 transition-colors" x-show="rowsOpen" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 transform -translate-y-1" x-transition:enter-end="opacity-100 transform translate-y-0">
4
+ <!-- Name/Description Column -->
5
+ <td class="font-normal text-left align-top">
6
+ <div class="text-sm text-font-default-light dark:text-font-default-dark">
7
+ {{ item.help_text|linebreaksbr }}
8
+ </div>
9
+ </td>
10
+
11
+ <!-- Value Column -->
12
+ <td class="{% if item.form_field.errors %}group errors{% endif %} align-top">
13
+ {{ item.form_field }}
14
+ {% include "unfold/helpers/form_errors.html" with errors=item.form_field.errors %}
15
+ </td>
16
+
17
+ <!-- Default Value Column -->
18
+ <td class="align-top">
19
+ {% include "admin/constance/includes/default_value.html" %}
20
+ </td>
21
+
22
+ <!-- Code Column -->
23
+ <td class="align-top">
24
+ <a class="item-name item-anchor" href="#{{ item.name|slugify }}" tabindex="-1" id="{{ item.name|slugify }}" title="Link to this setting">
25
+ {% include "unfold/helpers/label.html" with text=item.name type="info" %}
26
+ </a>
27
+ </td>
28
+
29
+ <!-- Modified Column -->
30
+ <td class="align-top text-center">
31
+ {% include "unfold/helpers/boolean.html" with value=item.modified %}
32
+ </td>
33
+
34
+ <!-- Reset Column -->
35
+ <td class="w-px align-top text-center">
36
+ {% if not item.is_file %}
37
+ <a href="#"
38
+ tabindex="-1"
39
+ class="flex items-center gap-1 reset-link whitespace-nowrap hover:bg-base-100 dark:hover:bg-base-800 p-1 rounded transition-colors"
40
+ data-default="{% spaceless %}{% if item.is_checkbox %}{% if item.raw_default %} true {% else %} false {% endif %}{% elif item.is_date %}{{ item.raw_default|date:"U" }}{% elif item.is_datetime %}{{ item.raw_default|date:"U" }}{% else %}{{ item.default }}{% endif %}{% endspaceless %}"
41
+ data-field-type="{% spaceless %}{% if item.is_checkbox %}checkbox{% elif item.is_datetime %}datetime{% elif item.is_date %}date{% endif %}{% endspaceless %}"
42
+ data-field-id="{{ item.form_field.auto_id }}"
43
+ title="{% trans "Reset to default" %}">
44
+ <span class="material-symbols-outlined text-base-400 hover:text-base-700 dark:text-base-500 dark:hover:text-base-200 transition-colors">
45
+ refresh
46
+ </span>
47
+ </a>
48
+ {% endif %}
49
+ </td>
50
+ </tr>
@@ -0,0 +1,10 @@
1
+ {% load i18n %}
2
+
3
+ <tr class="{% if fieldset.title %}border-t{% endif %} border-base-200 dark:border-base-800 *:font-semibold *:px-3 *:py-2 *:text-left *:text-font-important-light dark:*:text-font-important-dark" x-show="rowsOpen" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 transform -translate-y-2" x-transition:enter-end="opacity-100 transform translate-y-0">
4
+ <th class="w-1/4">{% trans "Name" %}</th>
5
+ <th class="w-1/3">{% trans "Value" %}</th>
6
+ <th class="w-1/5">{% trans "Default" %}</th>
7
+ <th class="w-1/6">{% trans "Code" %}</th>
8
+ <th class="w-16">{% trans "Modified" %}</th>
9
+ <th class="w-12"></th>
10
+ </tr>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.1.77
3
+ Version: 1.1.78
4
4
  Summary: 🚀 Production-ready Django configuration framework with type-safe settings, smart automation, and modern developer experience
5
5
  Project-URL: Homepage, https://github.com/markolofsen/django-cfg
6
6
  Project-URL: Documentation, https://django-cfg.readthedocs.io
@@ -1,5 +1,5 @@
1
1
  django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- django_cfg/__init__.py,sha256=OCsVk7qkjeX4hsDgPEvFJT8f1SzWHFgyBsuIi4vBpWU,14288
2
+ django_cfg/__init__.py,sha256=MbG9uWTmuWMaiiEr0N5-NjZPefox3akijvvn-GRu9qw,14288
3
3
  django_cfg/apps.py,sha256=k84brkeXJI7EgKZLEpTkM9YFZofKI4PzhFOn1cl9Msc,1656
4
4
  django_cfg/exceptions.py,sha256=RTQEoU3PfR8lqqNNv5ayd_HY2yJLs3eioqUy8VM6AG4,10378
5
5
  django_cfg/integration.py,sha256=jUO-uZXLmBXy9iugqgsl_xnYA_xoH3LZg5RxZbobVrc,4988
@@ -186,7 +186,7 @@ django_cfg/middleware/public_endpoints.py,sha256=EHJKzlYwakeU3hSPBJ53FZn8Ub-jwEN
186
186
  django_cfg/middleware/user_activity.py,sha256=P2V_RwiT6F7-F06B-2_V04gf7qFonxM03xVxMFsOk5o,6144
187
187
  django_cfg/models/__init__.py,sha256=du24ZdqKdvc3VNXjgufEz_6B6gxdHtlqVHG4PJWaOQM,419
188
188
  django_cfg/models/cache.py,sha256=Oq6VwVgWAscMM3B91sEvbCX4rXNrP4diSt68_R4XCQk,12131
189
- django_cfg/models/constance.py,sha256=6SGtLr9MlUezK5pKaRaIj1Lc875Yror0Symb67A1snE,7119
189
+ django_cfg/models/constance.py,sha256=goR5gjvXIuuBWJJgUgdwzJXMvsGtnGFp8EwEPpviUWM,9694
190
190
  django_cfg/models/database.py,sha256=rewKwvBKwzsH-NBQZqIXv2xHheoPcz4KiDdyM-qCC3Q,16902
191
191
  django_cfg/models/drf.py,sha256=zOCj8yiNFMxWVIYIBWnaipJ8DywAGSNsz-LlsTGHsGQ,9362
192
192
  django_cfg/models/jwt.py,sha256=3R_dpLmVZIcH4zdtwA4qKnuCB8RZQACrgsbbgWY2q4c,9025
@@ -243,6 +243,12 @@ django_cfg/modules/unfold/system_monitor.py,sha256=5EeofnHgVMwZACzrMO8JPgGKtM9Cf
243
243
  django_cfg/modules/unfold/tailwind.py,sha256=eo5t0opF_-HX3ztnWA6BUOf35aVN0qUFYRkuabiH2KA,9237
244
244
  django_cfg/templates/__init__.py,sha256=IzLjt-a7VIJ0OutmAE1_-w0_LpL2u0MgGpnIabjZuW8,19
245
245
  django_cfg/templates/admin/index.html,sha256=kehH-W9fTTUo36G9X82vM7L3Mr_lTiMkfp0qxPJAaSg,1247
246
+ django_cfg/templates/admin/constance/change_list.html,sha256=A6VN_gOQPzNvWvsOzfNFMXh-2qgfyWt4urmX95MIUuk,4820
247
+ django_cfg/templates/admin/constance/includes/default_value.html,sha256=dHu28vMi0OOn4KmRseMwWiWq51fz7EFdz4RZB6_4jrk,1301
248
+ django_cfg/templates/admin/constance/includes/fieldset_header.html,sha256=kAyS0jbWNwk7aBcI7G4rx-kvVw315m21kxzSDii-qig,838
249
+ django_cfg/templates/admin/constance/includes/results_list.html,sha256=OqWyyTuM7iWNjLc1yVp3YHFJk3APxk_Qto0JtssMX2Q,558
250
+ django_cfg/templates/admin/constance/includes/setting_row.html,sha256=Q4djpNLtZhh_dh_ZoX1PPXN9YUv5AMG49HATErl274o,2601
251
+ django_cfg/templates/admin/constance/includes/table_headers.html,sha256=_flMPr9mSZOO66Jg0F4OEXlGklRyH-C0_ImfGu-eOLM,677
246
252
  django_cfg/templates/admin/layouts/dashboard_with_tabs.html,sha256=_4n6d4awC3_UhI3BHrDT9JZXz_-HV0JVrBpBEeN2eFo,18137
247
253
  django_cfg/templates/admin/snippets/components/activity_tracker.html,sha256=WtZyHUmAQwVjvL3PV8OTu5_-FGhTUcPyBUXi8K-c6Eg,692
248
254
  django_cfg/templates/admin/snippets/components/charts_section.html,sha256=SZV2GLkSkYbXaWnUntbwxBhVXJRPHpjNcJrapqv3jO4,1932
@@ -265,8 +271,8 @@ django_cfg/templates/emails/base_email.html,sha256=TWcvYa2IHShlF_E8jf1bWZStRO0v8
265
271
  django_cfg/utils/__init__.py,sha256=64wwXJuXytvwt8Ze_erSR2HmV07nGWJ6DV5wloRBvYE,435
266
272
  django_cfg/utils/path_resolution.py,sha256=eML-6-RIGTs5TePktIQN8nxfDUEFJ3JA0AzWBcihAbs,13894
267
273
  django_cfg/utils/smart_defaults.py,sha256=-qaoiOQ1HKDOzwK2uxoNlmrOX6l8zgGlVPgqtdj3y4g,22319
268
- django_cfg-1.1.77.dist-info/METADATA,sha256=OPGpVFREbpTyiNho7I1j828EfYzK5IUWozpLSgQx4U8,45783
269
- django_cfg-1.1.77.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
270
- django_cfg-1.1.77.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
271
- django_cfg-1.1.77.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
272
- django_cfg-1.1.77.dist-info/RECORD,,
274
+ django_cfg-1.1.78.dist-info/METADATA,sha256=0AgHcZubcEDhURU4YIZaF6kauXiRNDS15vU0F6-oWP8,45783
275
+ django_cfg-1.1.78.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
276
+ django_cfg-1.1.78.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
277
+ django_cfg-1.1.78.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
278
+ django_cfg-1.1.78.dist-info/RECORD,,