django-cfg 1.4.118__py3-none-any.whl → 1.4.120__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 CHANGED
@@ -32,7 +32,7 @@ Example:
32
32
  default_app_config = "django_cfg.apps.DjangoCfgConfig"
33
33
 
34
34
  # Version information
35
- __version__ = "1.4.118"
35
+ __version__ = "1.4.120"
36
36
  __license__ = "MIT"
37
37
 
38
38
  # Import registry for organized lazy loading
@@ -45,6 +45,7 @@ DEFAULT_APPS: List[str] = [
45
45
  # Django CFG Core
46
46
  "django_cfg",
47
47
  "django_cfg.modules.django_client",
48
+ "django_cfg.modules.django_admin",
48
49
  ]
49
50
 
50
51
  # Default middleware stack
@@ -57,6 +57,7 @@ from .config import (
57
57
  ImageField,
58
58
  MarkdownField,
59
59
  ResourceConfig,
60
+ ShortUUIDField,
60
61
  TextField,
61
62
  UserField,
62
63
  )
@@ -116,6 +117,7 @@ __all__ = [
116
117
  "DateTimeField",
117
118
  "ImageField",
118
119
  "MarkdownField",
120
+ "ShortUUIDField",
119
121
  "TextField",
120
122
  "UserField",
121
123
  # Advanced
@@ -0,0 +1,19 @@
1
+ """
2
+ Django app configuration for django_admin module.
3
+
4
+ This makes django_admin a proper Django app so templates are automatically discovered.
5
+ """
6
+ from django.apps import AppConfig
7
+
8
+
9
+ class DjangoAdminConfig(AppConfig):
10
+ """Configuration for django_admin module."""
11
+
12
+ default_auto_field = "django.db.models.BigAutoField"
13
+ name = "django_cfg.modules.django_admin"
14
+ label = "django_cfg_admin"
15
+ verbose_name = "Django Admin (django-cfg)"
16
+
17
+ def ready(self):
18
+ """Called when Django is ready."""
19
+ pass
@@ -558,12 +558,14 @@ class PydanticAdminMixin:
558
558
  Override form field for specific database field types.
559
559
 
560
560
  Automatically detects and customizes encrypted fields from django-crypto-fields.
561
+ Respects the show_encrypted_fields_as_plain_text setting from AdminConfig.
562
+ Uses custom widgets with copy-to-clipboard functionality.
561
563
  """
562
564
  # Check if this is an EncryptedTextField or EncryptedCharField
563
565
  field_class_name = db_field.__class__.__name__
564
566
  if 'Encrypted' in field_class_name and ('TextField' in field_class_name or 'CharField' in field_class_name):
565
567
  from django import forms
566
- from django.forms.widgets import PasswordInput
568
+ from ..widgets import EncryptedFieldWidget, EncryptedPasswordWidget
567
569
 
568
570
  # Determine placeholder based on field name
569
571
  placeholder = "Enter value"
@@ -574,16 +576,25 @@ class PydanticAdminMixin:
574
576
  elif 'passphrase' in db_field.name.lower():
575
577
  placeholder = "Enter Passphrase (if required)"
576
578
 
577
- # Return CharField with PasswordInput widget for security
578
- # render_value=True shows masked value (••••••) after save
579
+ # Widget attributes
580
+ widget_attrs = {
581
+ 'placeholder': placeholder,
582
+ }
583
+
584
+ # Decide widget based on config
585
+ show_plain_text = getattr(self.config, 'show_encrypted_fields_as_plain_text', False)
586
+
587
+ if show_plain_text:
588
+ # Show as plain text with copy button
589
+ widget = EncryptedFieldWidget(attrs=widget_attrs, show_copy_button=True)
590
+ else:
591
+ # Show as password (masked) with copy button
592
+ # render_value=True shows masked value (••••••) after save
593
+ widget = EncryptedPasswordWidget(attrs=widget_attrs, render_value=True, show_copy_button=True)
594
+
595
+ # Return CharField with appropriate widget
579
596
  return forms.CharField(
580
- widget=PasswordInput(
581
- attrs={
582
- 'placeholder': placeholder,
583
- 'class': 'appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500'
584
- },
585
- render_value=True # Show masked value after save
586
- ),
597
+ widget=widget,
587
598
  required=not db_field.blank and not db_field.null,
588
599
  help_text=db_field.help_text or "This field is encrypted at rest",
589
600
  label=db_field.verbose_name if hasattr(db_field, 'verbose_name') else db_field.name.replace('_', ' ').title()
@@ -14,6 +14,7 @@ from .field_config import (
14
14
  DateTimeField,
15
15
  ImageField,
16
16
  MarkdownField,
17
+ ShortUUIDField,
17
18
  TextField,
18
19
  UserField,
19
20
  )
@@ -36,6 +37,7 @@ __all__ = [
36
37
  "DateTimeField",
37
38
  "ImageField",
38
39
  "MarkdownField",
40
+ "ShortUUIDField",
39
41
  "TextField",
40
42
  "UserField",
41
43
  ]
@@ -148,6 +148,13 @@ class AdminConfig(BaseModel):
148
148
  description="Markdown documentation configuration"
149
149
  )
150
150
 
151
+ # Encrypted fields options
152
+ show_encrypted_fields_as_plain_text: bool = Field(
153
+ False,
154
+ description="Show encrypted fields (django-crypto-fields) as plain text instead of password masked. "
155
+ "WARNING: This exposes sensitive data in the admin interface. Use only in trusted environments."
156
+ )
157
+
151
158
  def get_display_field_config(self, field_name: str) -> Optional[FieldConfig]:
152
159
  """Get FieldConfig for a specific field."""
153
160
  for field_config in self.display_fields:
@@ -341,3 +341,27 @@ class MarkdownField(FieldConfig):
341
341
  config['header_icon'] = self.header_icon
342
342
 
343
343
  return config
344
+
345
+
346
+ class ShortUUIDField(FieldConfig):
347
+ """
348
+ Short UUID widget configuration for displaying shortened UUIDs.
349
+
350
+ Examples:
351
+ ShortUUIDField(name="id", length=8)
352
+ ShortUUIDField(name="uuid", length=12, copy_on_click=True)
353
+ """
354
+
355
+ ui_widget: Literal["short_uuid"] = "short_uuid"
356
+
357
+ length: int = Field(8, description="Number of characters to display from UUID")
358
+ copy_on_click: bool = Field(True, description="Enable click-to-copy functionality")
359
+ show_full_on_hover: bool = Field(True, description="Show full UUID in tooltip on hover")
360
+
361
+ def get_widget_config(self) -> Dict[str, Any]:
362
+ """Extract short UUID widget configuration."""
363
+ config = super().get_widget_config()
364
+ config['length'] = self.length
365
+ config['copy_on_click'] = self.copy_on_click
366
+ config['show_full_on_hover'] = self.show_full_on_hover
367
+ return config
@@ -0,0 +1,80 @@
1
+ {% comment %}
2
+ Template for encrypted text field with copy button.
3
+ Based on unfold/widgets/text.html with added copy functionality.
4
+ {% endcomment %}
5
+
6
+ <div class="max-w-2xl relative w-full">
7
+ {% if widget.prefix %}
8
+ <span class="absolute left-3 top-0 bottom-0 flex items-center justify-center">
9
+ {{ widget.prefix }}
10
+ </span>
11
+ {% endif %}
12
+
13
+ {% if widget.prefix_icon %}
14
+ <span class="material-symbols-outlined absolute left-3 top-0 bottom-0 flex items-center justify-center text-base-400 dark:text-base-500">
15
+ {{ widget.prefix_icon }}
16
+ </span>
17
+ {% endif %}
18
+
19
+ {% include "django/forms/widgets/input.html" %}
20
+
21
+ {% if widget.suffix %}
22
+ <span class="absolute right-3 top-0 bottom-0 flex items-center justify-center">
23
+ {{ widget.suffix }}
24
+ </span>
25
+ {% endif %}
26
+
27
+ {% if widget.suffix_icon %}
28
+ <span class="material-symbols-outlined absolute right-3 top-0 bottom-0 flex items-center justify-center text-base-400 dark:text-base-500">
29
+ {{ widget.suffix_icon }}
30
+ </span>
31
+ {% endif %}
32
+
33
+ {% if widget.show_copy_button %}
34
+ <button
35
+ type="button"
36
+ onclick="copyEncryptedField(this)"
37
+ class="material-symbols-outlined absolute right-3 top-0 bottom-0 flex items-center justify-center text-base-400 hover:text-primary-600 dark:text-base-500 dark:hover:text-primary-500 cursor-pointer transition-colors"
38
+ title="Copy to clipboard"
39
+ >
40
+ content_copy
41
+ </button>
42
+ {% endif %}
43
+ </div>
44
+
45
+ <script>
46
+ function copyEncryptedField(button) {
47
+ // Get the input field (previous sibling of button's parent)
48
+ const container = button.parentElement;
49
+ const input = container.querySelector('input');
50
+
51
+ if (!input || !input.value) {
52
+ return;
53
+ }
54
+
55
+ // Copy to clipboard
56
+ navigator.clipboard.writeText(input.value).then(() => {
57
+ // Visual feedback - change icon temporarily
58
+ const originalIcon = button.textContent;
59
+ button.textContent = 'check';
60
+ button.classList.add('text-green-600', 'dark:text-green-500');
61
+
62
+ // Reset after 2 seconds
63
+ setTimeout(() => {
64
+ button.textContent = originalIcon;
65
+ button.classList.remove('text-green-600', 'dark:text-green-500');
66
+ }, 2000);
67
+ }).catch(err => {
68
+ console.error('Failed to copy:', err);
69
+ // Show error feedback
70
+ const originalIcon = button.textContent;
71
+ button.textContent = 'error';
72
+ button.classList.add('text-red-600', 'dark:text-red-500');
73
+
74
+ setTimeout(() => {
75
+ button.textContent = originalIcon;
76
+ button.classList.remove('text-red-600', 'dark:text-red-500');
77
+ }, 2000);
78
+ });
79
+ }
80
+ </script>
@@ -0,0 +1,62 @@
1
+ {% comment %}
2
+ Template for encrypted password field with copy button.
3
+ Based on Django's default password widget with Unfold styling and copy button.
4
+ {% endcomment %}
5
+
6
+ <div class="max-w-2xl relative w-full">
7
+ {% include "django/forms/widgets/input.html" %}
8
+
9
+ {% if widget.show_copy_button %}
10
+ <button
11
+ type="button"
12
+ onclick="copyEncryptedPassword(this)"
13
+ class="material-symbols-outlined absolute right-3 top-0 bottom-0 flex items-center justify-center text-base-400 hover:text-primary-600 dark:text-base-500 dark:hover:text-primary-500 cursor-pointer transition-colors"
14
+ title="Copy to clipboard"
15
+ >
16
+ content_copy
17
+ </button>
18
+ {% endif %}
19
+ </div>
20
+
21
+ <script>
22
+ function copyEncryptedPassword(button) {
23
+ // Get the input field (previous sibling of button's parent)
24
+ const container = button.parentElement;
25
+ const input = container.querySelector('input');
26
+
27
+ if (!input || !input.value) {
28
+ return;
29
+ }
30
+
31
+ // For password fields, we need to temporarily change type to text to get the real value
32
+ const originalType = input.type;
33
+ input.type = 'text';
34
+ const value = input.value;
35
+ input.type = originalType;
36
+
37
+ // Copy to clipboard
38
+ navigator.clipboard.writeText(value).then(() => {
39
+ // Visual feedback - change icon temporarily
40
+ const originalIcon = button.textContent;
41
+ button.textContent = 'check';
42
+ button.classList.add('text-green-600', 'dark:text-green-500');
43
+
44
+ // Reset after 2 seconds
45
+ setTimeout(() => {
46
+ button.textContent = originalIcon;
47
+ button.classList.remove('text-green-600', 'dark:text-green-500');
48
+ }, 2000);
49
+ }).catch(err => {
50
+ console.error('Failed to copy:', err);
51
+ // Show error feedback
52
+ const originalIcon = button.textContent;
53
+ button.textContent = 'error';
54
+ button.classList.add('text-red-600', 'dark:text-red-500');
55
+
56
+ setTimeout(() => {
57
+ button.textContent = originalIcon;
58
+ button.classList.remove('text-red-600', 'dark:text-red-500');
59
+ }, 2000);
60
+ });
61
+ }
62
+ </script>
@@ -2,8 +2,11 @@
2
2
  Widget system for Django Admin.
3
3
  """
4
4
 
5
+ from .encrypted_field_widget import EncryptedFieldWidget, EncryptedPasswordWidget
5
6
  from .registry import WidgetRegistry
6
7
 
7
8
  __all__ = [
8
9
  "WidgetRegistry",
10
+ "EncryptedFieldWidget",
11
+ "EncryptedPasswordWidget",
9
12
  ]
@@ -0,0 +1,66 @@
1
+ """
2
+ Custom widget for encrypted fields with copy button.
3
+ """
4
+ from typing import Any, Optional
5
+
6
+ from unfold.widgets import UnfoldAdminPasswordInput, UnfoldAdminTextInputWidget
7
+
8
+
9
+ class EncryptedFieldWidget(UnfoldAdminTextInputWidget):
10
+ """
11
+ Text input widget for encrypted fields with copy-to-clipboard button.
12
+
13
+ Extends UnfoldAdminTextInputWidget to add a copy button on the right side.
14
+ """
15
+
16
+ template_name = "django_admin/widgets/encrypted_field.html"
17
+
18
+ def __init__(self, attrs: Optional[dict[str, Any]] = None, show_copy_button: bool = True) -> None:
19
+ """
20
+ Initialize the widget.
21
+
22
+ Args:
23
+ attrs: Widget attributes
24
+ show_copy_button: Whether to show the copy button (default: True)
25
+ """
26
+ self.show_copy_button = show_copy_button
27
+ super().__init__(attrs=attrs)
28
+
29
+ def get_context(self, name, value, attrs):
30
+ """Add copy button context."""
31
+ context = super().get_context(name, value, attrs)
32
+ context['widget']['show_copy_button'] = self.show_copy_button
33
+ return context
34
+
35
+
36
+ class EncryptedPasswordWidget(UnfoldAdminPasswordInput):
37
+ """
38
+ Password input widget for encrypted fields with copy button.
39
+
40
+ Extends UnfoldAdminPasswordInput to add a copy button on the right side.
41
+ """
42
+
43
+ template_name = "django_admin/widgets/encrypted_password.html"
44
+
45
+ def __init__(
46
+ self,
47
+ attrs: Optional[dict[str, Any]] = None,
48
+ render_value: bool = False,
49
+ show_copy_button: bool = True
50
+ ) -> None:
51
+ """
52
+ Initialize the widget.
53
+
54
+ Args:
55
+ attrs: Widget attributes
56
+ render_value: Whether to render the value (default: False)
57
+ show_copy_button: Whether to show the copy button (default: True)
58
+ """
59
+ self.show_copy_button = show_copy_button
60
+ super().__init__(attrs=attrs, render_value=render_value)
61
+
62
+ def get_context(self, name, value, attrs):
63
+ """Add copy button context."""
64
+ context = super().get_context(name, value, attrs)
65
+ context['widget']['show_copy_button'] = self.show_copy_button
66
+ return context
@@ -208,3 +208,45 @@ def _render_image(obj: Any, field: str, config: Dict[str, Any]) -> str:
208
208
 
209
209
  # Image widget
210
210
  WidgetRegistry.register("image", _render_image)
211
+
212
+
213
+ def _render_short_uuid(obj: Any, field: str, config: Dict[str, Any]) -> str:
214
+ """Render a shortened UUID with tooltip."""
215
+ from django.utils.safestring import mark_safe
216
+
217
+ # Get UUID value
218
+ uuid_value = getattr(obj, field, None)
219
+
220
+ if not uuid_value:
221
+ return config.get('empty_value', "—")
222
+
223
+ # Convert to string and remove dashes
224
+ uuid_str = str(uuid_value).replace('-', '')
225
+
226
+ # Get configuration
227
+ length = config.get('length', 8)
228
+ show_full_on_hover = config.get('show_full_on_hover', True)
229
+ copy_on_click = config.get('copy_on_click', True)
230
+
231
+ # Truncate to specified length
232
+ short_uuid = uuid_str[:length]
233
+
234
+ # Build HTML
235
+ if show_full_on_hover:
236
+ title_attr = f' title="{uuid_value}"'
237
+ else:
238
+ title_attr = ''
239
+
240
+ if copy_on_click:
241
+ # Add click-to-copy functionality
242
+ copy_attr = f' onclick="navigator.clipboard.writeText(\'{uuid_value}\'); this.style.backgroundColor=\'#e8f5e9\'; setTimeout(() => this.style.backgroundColor=\'\', 500);" style="cursor: pointer;"'
243
+ else:
244
+ copy_attr = ''
245
+
246
+ html = f'<code{title_attr}{copy_attr}>{short_uuid}</code>'
247
+
248
+ return mark_safe(html)
249
+
250
+
251
+ # ShortUUID widget
252
+ WidgetRegistry.register("short_uuid", _render_short_uuid)
django_cfg/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "django-cfg"
7
- version = "1.4.118"
7
+ version = "1.4.120"
8
8
  description = "Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features."
9
9
  readme = "README.md"
10
10
  keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "nextjs-admin", "react-admin", "websocket", "centrifugo", "real-time", "typescript-generation", "ai-agents", "enterprise-django", "django-settings", "type-safe-config", "modern-django",]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-cfg
3
- Version: 1.4.118
3
+ Version: 1.4.120
4
4
  Summary: Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features.
5
5
  Project-URL: Homepage, https://djangocfg.com
6
6
  Project-URL: Documentation, https://djangocfg.com
@@ -1,5 +1,5 @@
1
1
  django_cfg/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- django_cfg/__init__.py,sha256=czBPTVeDvyLUMK-04V3RRLhBFzXwK7DP_dDyzQq7ZNw,1621
2
+ django_cfg/__init__.py,sha256=2aQpPujqEk1Kt4OCSixn0JVIQ-ppeqKrE6o-xJY4Ltg,1621
3
3
  django_cfg/apps.py,sha256=72m3uuvyqGiLx6gOfE-BD3P61jddCCERuBOYpxTX518,1605
4
4
  django_cfg/config.py,sha256=xdwFE8bocOEnMjqDOwr1_M02oUgdaMG_2M6F_IuB9GQ,1351
5
5
  django_cfg/apps/__init__.py,sha256=JtDmEYt1OcleWM2ZaeX0LKDnRQzPOavfaXBWG4ECB5Q,26
@@ -536,7 +536,7 @@ django_cfg/cli/commands/create_project.py,sha256=cENwRIG7zX_95lY7Kt_xKGULZWqfwni
536
536
  django_cfg/cli/commands/info.py,sha256=JNLt9gUTZopR-D7FUDIeEyOQR9-2LTpHntHi_h-7Mho,4794
537
537
  django_cfg/core/__init__.py,sha256=bZ3hgN9SX6l3DXEN-cMyOGwwXMu4UFkWyR0ds_yEnVs,1784
538
538
  django_cfg/core/config.py,sha256=0xwXf0cPiRGM8paUPXjECHdYqwuRLy4WkunavWl9Igw,679
539
- django_cfg/core/constants.py,sha256=ool-h1MXDtgRKj8-e8aYcM6XY_Uzy1dgx-xLy0gsk10,2686
539
+ django_cfg/core/constants.py,sha256=D3-9XIMpwoYDgye7P_ixurS1IzV3R4fUvmSclXo8uf4,2725
540
540
  django_cfg/core/exceptions.py,sha256=-jHOxN_X_k9BGI50zBLloVawCMe-uIruiyLHto12B-k,2215
541
541
  django_cfg/core/validation.py,sha256=QmwGlNDa0MFG2OUmgykIUAaEotT55YNh-wAWSVxOAos,6165
542
542
  django_cfg/core/backends/__init__.py,sha256=5Qfza7oKQ8_UIHOs4ibhYvwtgAjngUqLrlwjKl_q124,38
@@ -674,16 +674,17 @@ django_cfg/models/tasks/config.py,sha256=0Tc7O2mVQLc8IYXMm__cdeP0ytYn_NucL2xyA95
674
674
  django_cfg/models/tasks/utils.py,sha256=9TEbdxgd0N_O2T_7GGwkyPeDg4H7tSKLHoHG_n8edqg,4459
675
675
  django_cfg/modules/__init__.py,sha256=Ip9WMpzImEwIAywpFwU056_v0O9oIGG7nCT1YSArxkw,316
676
676
  django_cfg/modules/base.py,sha256=Grmgxc5dvnAEM1sudWEWO4kv8L0Ks-y32nxTk2vwdjQ,6272
677
- django_cfg/modules/django_admin/__init__.py,sha256=ncTMbxR7ccVd5xS-sZE_yJODjVMFq8HDFaDnP6lyvIg,3328
677
+ django_cfg/modules/django_admin/__init__.py,sha256=0g5SJPtmMhM57MMZG4msvhKbgDTqabf5r95_EsQMQuY,3370
678
+ django_cfg/modules/django_admin/apps.py,sha256=xJjgIRgS_I1ehyq7ZbFpZY_L4umRa5ZPQP3mf0alUaU,526
678
679
  django_cfg/modules/django_admin/base/__init__.py,sha256=tzre09bnD_SlS-pA30WzYZRxyvch7eLq3q0wLEcZOmc,118
679
- django_cfg/modules/django_admin/base/pydantic_admin.py,sha256=6tB_8hZLT3ZzwUu6rlvzhkQ_BkDq8niaTE3TIa0IcgM,24977
680
+ django_cfg/modules/django_admin/base/pydantic_admin.py,sha256=xewt_ld40NntdjtZ-k-VwinhfDkIRk71nwu5DVDQzeQ,25364
680
681
  django_cfg/modules/django_admin/base/unfold_admin.py,sha256=iqpRWSkzW5HktXDuuG7G3J6RoIfW48dWPMJTa7Yk08g,729
681
- django_cfg/modules/django_admin/config/__init__.py,sha256=HDuJxhAS27EiL1pXLNvMePREB2gfoS2i_0Ur1ITCjAM,926
682
+ django_cfg/modules/django_admin/config/__init__.py,sha256=yhoZcTjgl40bDnaPe1MFdNLqYQj02cdE48aVrfGCmXU,968
682
683
  django_cfg/modules/django_admin/config/action_config.py,sha256=JjS01JxLT-FzUVq7RlKaB7L38wmVL8uibXO_iXZcljo,1668
683
- django_cfg/modules/django_admin/config/admin_config.py,sha256=TnPI-kLZj6uoIH7opCD70HQ6I_yJ7jPKnphS0h9P42s,5242
684
+ django_cfg/modules/django_admin/config/admin_config.py,sha256=L48YHLt-JyNyL-XOltqIZVh1ALoG4wXQsvi_WIbhuFw,5577
684
685
  django_cfg/modules/django_admin/config/background_task_config.py,sha256=7-8B1rhpeafanVxtjFQUx0mVjcA5xmxZIxqKzaBwMX0,1760
685
686
  django_cfg/modules/django_admin/config/documentation_config.py,sha256=lI_gMSWCtyKmdyttLNdgbg_zbGgrwXA-QoLxVOXJj9A,14189
686
- django_cfg/modules/django_admin/config/field_config.py,sha256=LeHoumm-lmiYIu0dBmoyCdTkddQdcv20_KrD3WRhkQ0,11743
687
+ django_cfg/modules/django_admin/config/field_config.py,sha256=J-CTdhJFP0CLw8T0cOb1FvYwTPpdawv9qVam69QbO4s,12638
687
688
  django_cfg/modules/django_admin/config/fieldset_config.py,sha256=5BPUWO_HvS6YhPU_vqQPzRT2y3OIPrBCioFuer5-mrA,1249
688
689
  django_cfg/modules/django_admin/config/resource_config.py,sha256=6ELWSuHlrpNqV2wO2sL_o2mE2DXLd_solcsdN5O6eQg,3962
689
690
  django_cfg/modules/django_admin/icons/README.md,sha256=j-NUixSC1QJh7PqYKxLZpPrTxKrAnx0urQraXgv4JHI,5612
@@ -699,6 +700,8 @@ django_cfg/modules/django_admin/templates/django_admin/change_form_docs.html,sha
699
700
  django_cfg/modules/django_admin/templates/django_admin/change_list_docs.html,sha256=MfH6mf9v6E3y97TjvjvZ2A7RMhOkKd6CtxEw3tPV5WE,802
700
701
  django_cfg/modules/django_admin/templates/django_admin/documentation_block.html,sha256=fDUZYDWuB68NOm4fjTr0OLfqG2m-UpE-MZL1upkUNHc,14174
701
702
  django_cfg/modules/django_admin/templates/django_admin/markdown_docs_block.html,sha256=nO9oEyDYozYF-euaLzE0Cn_Yo_LGUlta-BsRgShDG10,1992
703
+ django_cfg/modules/django_admin/templates/django_admin/widgets/encrypted_field.html,sha256=-Kd4twY0sav2M5x_0oDj8ElA7CBccToVHFiwGa2WF-w,2774
704
+ django_cfg/modules/django_admin/templates/django_admin/widgets/encrypted_password.html,sha256=BdiM0CqRyBH1aHGf6iLSfSKOoADY5qaqetbC9AktzaI,2154
702
705
  django_cfg/modules/django_admin/utils/CODE_BLOCK_DOCS.md,sha256=rp5qMG-Ci30fIs6EyZctjEbhQwxfNq9e36B4CTZOnR0,9456
703
706
  django_cfg/modules/django_admin/utils/__init__.py,sha256=5V1Og6fpIpRqDIoqF7jYnBmPl9TjV5kByghzT6iOpPs,745
704
707
  django_cfg/modules/django_admin/utils/badges.py,sha256=eZ1UThdwvv2cHAIDc4vTrD5xAet7fmeb9h9yj4ZXJ-c,6328
@@ -707,8 +710,9 @@ django_cfg/modules/django_admin/utils/displays.py,sha256=f-FT1mD-X56X6xLDJ9FuCi4
707
710
  django_cfg/modules/django_admin/utils/html_builder.py,sha256=E0ysbbImpZqSr7YFw3FYryG0jjJfffIOqFzefk-HMl8,14500
708
711
  django_cfg/modules/django_admin/utils/markdown_renderer.py,sha256=eMYde8cDo9y5CO5qNjy7juYe_OPpSpFB1-d84ylpbk4,14324
709
712
  django_cfg/modules/django_admin/utils/mermaid_plugin.py,sha256=37x_0FQE5IyR6TiaDZDrQQHeQmAsPtlZBnq5N8eAKqA,8889
710
- django_cfg/modules/django_admin/widgets/__init__.py,sha256=mmPw5FMYR21GDGFMr-MOCcdM4G2_ZR60ClInHjdnTBE,115
711
- django_cfg/modules/django_admin/widgets/registry.py,sha256=q0Yyaze5ZTYLJslPyX9e4Few_FGLnGBQwtNln9Okyt4,5610
713
+ django_cfg/modules/django_admin/widgets/__init__.py,sha256=SVVytNURG_TlWePwhws9FYeDe_xp4Ob_CYVA88Y08es,256
714
+ django_cfg/modules/django_admin/widgets/encrypted_field_widget.py,sha256=h4bSAI2sLjKuBFDufWhtQQQ-eMIAqUzsUezi1p0ERuM,2146
715
+ django_cfg/modules/django_admin/widgets/registry.py,sha256=P2kQiB3eYXhjRiCXlcImHAoCDyQjZNNoF_-FyQyBZ1w,6864
712
716
  django_cfg/modules/django_client/__init__.py,sha256=iHaGKbsyR2wMmVCWNsETC7cwB60fZudvnFMiK1bchW8,529
713
717
  django_cfg/modules/django_client/apps.py,sha256=xfkw2aXy08xXlkFhbCiTFveMmRwlDk3SQOAWdqXraFM,1952
714
718
  django_cfg/modules/django_client/pytest.ini,sha256=yC1PeQKS4nWQD-jVo5fWF9y1Uv6rywcH0mtHREsiAp0,668
@@ -1066,9 +1070,9 @@ django_cfg/utils/version_check.py,sha256=WO51J2m2e-wVqWCRwbultEwu3q1lQasV67Mw2aa
1066
1070
  django_cfg/CHANGELOG.md,sha256=jtT3EprqEJkqSUh7IraP73vQ8PmKUMdRtznQsEnqDZk,2052
1067
1071
  django_cfg/CONTRIBUTING.md,sha256=DU2kyQ6PU0Z24ob7O_OqKWEYHcZmJDgzw-lQCmu6uBg,3041
1068
1072
  django_cfg/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1069
- django_cfg/pyproject.toml,sha256=fZ9FGyvoUZbCwaNqWJDQwUNYeN11UYgds7UsrzwGFO0,8665
1070
- django_cfg-1.4.118.dist-info/METADATA,sha256=NcD294dwA8ckrblk-0er6UcqtHdYYren4RsZh77BqkU,23876
1071
- django_cfg-1.4.118.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1072
- django_cfg-1.4.118.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1073
- django_cfg-1.4.118.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1074
- django_cfg-1.4.118.dist-info/RECORD,,
1073
+ django_cfg/pyproject.toml,sha256=620u5Gcr1JnM0aTc2dDPlLe4epX8WqX5tLTogwr_CrE,8665
1074
+ django_cfg-1.4.120.dist-info/METADATA,sha256=3deYIU64Wm0FWERqzn5Q8ICvGOqOVsC68azVQtUww1k,23876
1075
+ django_cfg-1.4.120.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
1076
+ django_cfg-1.4.120.dist-info/entry_points.txt,sha256=Ucmde4Z2wEzgb4AggxxZ0zaYDb9HpyE5blM3uJ0_VNg,56
1077
+ django_cfg-1.4.120.dist-info/licenses/LICENSE,sha256=xHuytiUkSZCRG3N11nk1X6q1_EGQtv6aL5O9cqNRhKE,1071
1078
+ django_cfg-1.4.120.dist-info/RECORD,,