django-unfold 0.25.0__py3-none-any.whl → 0.27.0__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_unfold-0.25.0.dist-info → django_unfold-0.27.0.dist-info}/METADATA +61 -7
- {django_unfold-0.25.0.dist-info → django_unfold-0.27.0.dist-info}/RECORD +44 -31
- unfold/admin.py +13 -157
- unfold/contrib/forms/templates/unfold/forms/array.html +31 -0
- unfold/contrib/forms/widgets.py +58 -3
- unfold/contrib/import_export/forms.py +17 -0
- unfold/contrib/import_export/templates/admin/import_export/change_form.html +10 -0
- unfold/contrib/import_export/templates/admin/import_export/export.html +38 -3
- unfold/contrib/import_export/templates/admin/import_export/import_form.html +8 -12
- unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html +1 -1
- unfold/contrib/inlines/__init__.py +0 -0
- unfold/contrib/inlines/admin.py +141 -0
- unfold/contrib/inlines/apps.py +6 -0
- unfold/contrib/inlines/checks.py +18 -0
- unfold/contrib/inlines/forms.py +43 -0
- unfold/decorators.py +3 -0
- unfold/fields.py +200 -0
- unfold/forms.py +6 -0
- unfold/static/unfold/css/simplebar.css +230 -0
- unfold/static/unfold/css/styles.css +1 -1
- unfold/static/unfold/js/simplebar.js +10 -0
- unfold/styles.css +9 -1
- unfold/templates/admin/app_list.html +1 -1
- unfold/templates/admin/change_form.html +11 -11
- unfold/templates/admin/change_list_results.html +2 -2
- unfold/templates/admin/edit_inline/stacked.html +6 -6
- unfold/templates/admin/edit_inline/tabular.html +7 -9
- unfold/templates/admin/includes/fieldset.html +2 -32
- unfold/templates/unfold/helpers/app_list.html +1 -1
- unfold/templates/unfold/helpers/display_header.html +11 -8
- unfold/templates/unfold/helpers/field.html +20 -6
- unfold/templates/unfold/helpers/field_readonly.html +1 -3
- unfold/templates/unfold/helpers/field_readonly_value.html +1 -0
- unfold/templates/unfold/helpers/fieldset_row.html +53 -0
- unfold/templates/unfold/helpers/fieldsets_tabs.html +4 -4
- unfold/templates/unfold/helpers/form_label.html +1 -1
- unfold/templates/unfold/layouts/skeleton.html +2 -0
- unfold/templates/unfold/widgets/clearable_file_input.html +1 -1
- unfold/templates/unfold/widgets/foreign_key_raw_id.html +15 -0
- unfold/templates/unfold/widgets/textarea.html +1 -7
- unfold/templates/unfold/widgets/textarea_expandable.html +7 -0
- unfold/widgets.py +36 -3
- unfold/contrib/import_export/admin.py +0 -37
- {django_unfold-0.25.0.dist-info → django_unfold-0.27.0.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.25.0.dist-info → django_unfold-0.27.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: django-unfold
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.27.0
|
4
4
|
Summary: Modern Django admin theme for seamless interface development
|
5
5
|
Home-page: https://unfoldadmin.com
|
6
6
|
License: MIT
|
@@ -30,7 +30,7 @@ Description-Content-Type: text/markdown
|
|
30
30
|
|
31
31
|
[](https://github.com/unfoldadmin/django-unfold/actions?query=workflow%3Arelease)
|
32
32
|
[](https://pypi.org/project/django-unfold/)
|
33
|
-

|
34
34
|

|
35
35
|
|
36
36
|
Unfold is theme for Django admin incorporating most common practises for building full-fledged admin areas. It is designed to work at the top of default administration provided by Django.
|
@@ -52,14 +52,17 @@ Did you decide to start using Unfold but you don't have time to make the switch
|
|
52
52
|
- **Dependencies:** completely based only on `django.contrib.admin`
|
53
53
|
- **Actions:** multiple ways how to define actions within different parts of admin
|
54
54
|
- **WYSIWYG:** built-in support for WYSIWYG (Trix)
|
55
|
+
- **Array widget:** built-in widget for `django.contrib.postgres.fields.ArrayField`
|
55
56
|
- **Filters:** custom dropdown, numeric, datetime, and text fields
|
56
57
|
- **Dashboard:** custom components for rapid dashboard development
|
57
58
|
- **Model tabs:** define custom tab navigations for models
|
58
59
|
- **Fieldset tabs:** merge several fielsets into tabs in change form
|
59
60
|
- **Colors:** possibility to override default color scheme
|
61
|
+
- **Changeform modes:** display fields in changeform in compressed mode
|
60
62
|
- **Third party packages:** default support for multiple popular applications
|
61
63
|
- **Environment label**: distinguish between environments by displaying a label
|
62
|
-
- **
|
64
|
+
- **Nonrelated inlines**: displays nonrelated model as inline in changeform
|
65
|
+
- **Parallel admin**: support for default admin in parallel with Unfold. [Admin migration guide](https://unfoldadmin.com/blog/migrating-django-admin-unfold/)
|
63
66
|
- **VS Code**: project configuration and development container is included
|
64
67
|
|
65
68
|
## Table of contents <!-- omit from toc -->
|
@@ -78,6 +81,7 @@ Did you decide to start using Unfold but you don't have time to make the switch
|
|
78
81
|
- [Dropdown filters](#dropdown-filters)
|
79
82
|
- [Numeric filters](#numeric-filters)
|
80
83
|
- [Date/time filters](#datetime-filters)
|
84
|
+
- [Nonrelated inlines](#nonrelated-inlines)
|
81
85
|
- [Display decorator](#display-decorator)
|
82
86
|
- [Change form tabs](#change-form-tabs)
|
83
87
|
- [Third party packages](#third-party-packages)
|
@@ -114,6 +118,7 @@ INSTALLED_APPS = [
|
|
114
118
|
"unfold", # before django.contrib.admin
|
115
119
|
"unfold.contrib.filters", # optional, if special filters are needed
|
116
120
|
"unfold.contrib.forms", # optional, if special form elements are needed
|
121
|
+
"unfold.contrib.inlines", # optional, if special inlines are needed
|
117
122
|
"unfold.contrib.import_export", # optional, if django-import-export package is used
|
118
123
|
"unfold.contrib.guardian", # optional, if django-guardian package is used
|
119
124
|
"unfold.contrib.simple_history", # optional, if django-simple-history package is used
|
@@ -318,13 +323,17 @@ def permission_callback(request):
|
|
318
323
|
|
319
324
|
from django import models
|
320
325
|
from django.contrib import admin
|
326
|
+
from django.contrib.postgres.fields import ArrayField
|
321
327
|
from django.db import models
|
322
328
|
from unfold.admin import ModelAdmin
|
323
|
-
from unfold.contrib.forms.widgets import WysiwygWidget
|
329
|
+
from unfold.contrib.forms.widgets import ArrayWidget, WysiwygWidget
|
324
330
|
|
325
331
|
|
326
332
|
@admin.register(MyModel)
|
327
333
|
class CustomAdminClass(ModelAdmin):
|
334
|
+
# Display fields in changeform in compressed mode
|
335
|
+
compressed_fields = True # Default: False
|
336
|
+
|
328
337
|
# Preprocess content of readonly fields before render
|
329
338
|
readonly_preprocess_fields = {
|
330
339
|
"model_field_name": "html.unescape",
|
@@ -343,6 +352,9 @@ class CustomAdminClass(ModelAdmin):
|
|
343
352
|
formfield_overrides = {
|
344
353
|
models.TextField: {
|
345
354
|
"widget": WysiwygWidget,
|
355
|
+
},
|
356
|
+
ArrayField: {
|
357
|
+
"widget": ArrayWidget,
|
346
358
|
}
|
347
359
|
}
|
348
360
|
```
|
@@ -633,6 +645,41 @@ class YourModelAdmin(ModelAdmin):
|
|
633
645
|
)
|
634
646
|
```
|
635
647
|
|
648
|
+
## Nonrelated inlines
|
649
|
+
|
650
|
+
To display inlines which are not related (no foreign key pointing at the main model) to the model instance in changeform, you can use nonrelated inlines which are included in `unfold.contrib.inlines` module. Make sure this module is included in `INSTALLED_APPS` in settings.py.
|
651
|
+
|
652
|
+
```python
|
653
|
+
from django.contrib.auth.models import User
|
654
|
+
from unfold.admin import ModelAdmin
|
655
|
+
from unfold.contrib.inlines.admin import NonrelatedTabularInline
|
656
|
+
from .models import OtherModel
|
657
|
+
|
658
|
+
class OtherNonrelatedInline(NonrelatedTabularInline): # NonrelatedStackedInline is available as well
|
659
|
+
model = OtherModel
|
660
|
+
fields = ["field1", "field2"] # Ignore property to display all fields
|
661
|
+
|
662
|
+
def get_form_queryset(self, obj):
|
663
|
+
"""
|
664
|
+
Gets all nonrelated objects needed for inlines. Method must be implemented.
|
665
|
+
"""
|
666
|
+
return self.model.objects.all()
|
667
|
+
|
668
|
+
def save_new_instance(self, parent, instance):
|
669
|
+
"""
|
670
|
+
Extra save method which can for example update inline instances based on current
|
671
|
+
main model object. Method must be implemented.
|
672
|
+
"""
|
673
|
+
pass
|
674
|
+
|
675
|
+
|
676
|
+
@admin.register(User)
|
677
|
+
class UserAdmin(ModelAdmin):
|
678
|
+
inlines = [OtherNonrelatedInline]
|
679
|
+
```
|
680
|
+
|
681
|
+
**NOTE:** credit for this functionality goes to [django-nonrelated-inlines](https://github.com/bhomnick/django-nonrelated-inlines)
|
682
|
+
|
636
683
|
## Display decorator
|
637
684
|
|
638
685
|
Unfold introduces it's own `unfold.decorators.display` decorator. By default it has exactly same behavior as native `django.contrib.admin.decorators.display` but it adds same customizations which helps to extends default logic.
|
@@ -697,12 +744,15 @@ class UserAdmin(ModelAdmin):
|
|
697
744
|
"""
|
698
745
|
return [
|
699
746
|
"First main heading",
|
700
|
-
"Smaller additional description",
|
701
|
-
"AB",
|
747
|
+
"Smaller additional description", # Use None in case you don't need it
|
748
|
+
"AB", # Short text which will appear in front of
|
702
749
|
# Image instead of initials. Initials are ignored if image is available
|
703
750
|
{
|
704
751
|
"path": "some/path/picture.jpg,
|
705
752
|
"squared": True, # Picture is displayed in square format, if empty circle
|
753
|
+
"borderless": True # Picture will be displayed without border
|
754
|
+
"width": 64, # Removes default width. Use together with height
|
755
|
+
"height": 48, # Removes default height. Use together with width
|
706
756
|
}
|
707
757
|
]
|
708
758
|
```
|
@@ -822,15 +872,18 @@ Adding support for django-guardian is quote straightforward in Unfold, just add
|
|
822
872
|
|
823
873
|
from unfold.admin import ModelAdmin
|
824
874
|
from import_export.admin import ImportExportModelAdmin
|
825
|
-
from unfold.contrib.import_export.forms import ExportForm, ImportForm
|
875
|
+
from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm
|
826
876
|
|
827
877
|
class ExampleAdmin(ModelAdmin, ImportExportModelAdmin):
|
828
878
|
import_form_class = ImportForm
|
829
879
|
export_form_class = ExportForm
|
880
|
+
# export_form_class = SelectableFieldsExportForm
|
830
881
|
```
|
831
882
|
|
832
883
|
When implementing `import_export.admin.ExportActionModelAdmin` class in admin panel, import_export plugin adds its own implementation of action form which is not incorporating Unfold CSS classes. For this reason, `unfold.contrib.import_export.admin` contains class with the same name `ExportActionModelAdmin` which inherits behavior of parent form and adds appropriate CSS classes.
|
833
884
|
|
885
|
+
**Note:** This class has been removed and in new version (4.x) of django-import-export it is not needed.
|
886
|
+
|
834
887
|
```python
|
835
888
|
admin.py
|
836
889
|
|
@@ -1135,6 +1188,7 @@ The container has already a node preinstalled so it is possible to compile a new
|
|
1135
1188
|
|
1136
1189
|
## Credits
|
1137
1190
|
|
1191
|
+
- [django-nonrelated-inlines](https://github.com/bhomnick/django-nonrelated-inlines) - Django admin inlines for unrelated models
|
1138
1192
|
- [TailwindCSS](https://tailwindcss.com/) - CSS framework
|
1139
1193
|
- [HTMX](https://htmx.org/) - AJAX communication with backend
|
1140
1194
|
- [Material Icons](https://fonts.google.com/icons) - Icons from Google Fonts
|
@@ -1,5 +1,5 @@
|
|
1
1
|
unfold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
unfold/admin.py,sha256=
|
2
|
+
unfold/admin.py,sha256=T14cMt5yn-uQQWDS5_Ebb2Jjhsc0pStaKtsutNoJeWg,18921
|
3
3
|
unfold/apps.py,sha256=SlBXPYrUd2uXn67qFbRvbXSUk3XFWrF4-5WELgDCvho,381
|
4
4
|
unfold/checks.py,sha256=Smgji9w19hnYjJElJ_FJnnyTEAE-E-OUB6otHu7lasY,1670
|
5
5
|
unfold/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -23,9 +23,10 @@ unfold/contrib/forms/apps.py,sha256=Di0TMzVuRpVxLG-8Bjdq5ALCSf5r7u2xVhD0jU6H5Sc,
|
|
23
23
|
unfold/contrib/forms/static/unfold/forms/css/trix.css,sha256=TH9WdnaZrmwI8hAEydwjobdrBzSw_KYdRTSQDuD-8hE,20027
|
24
24
|
unfold/contrib/forms/static/unfold/forms/js/trix.config.js,sha256=spkNBlJVk_pqido_rM6yywQxkJ3Kqb7DMLiBgpKksdA,858
|
25
25
|
unfold/contrib/forms/static/unfold/forms/js/trix.js,sha256=Pao0XiVeDiRawfTkGDg_np6CxB-oXPrUDI9akWc87oc,174157
|
26
|
+
unfold/contrib/forms/templates/unfold/forms/array.html,sha256=11silyHbsJA0U_ksS8MvfFOJKC_qKTAwXxoMIB78APk,1507
|
26
27
|
unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html,sha256=yS8Zy-UrzvZ5RUYwdprQzREffnYq0NlIbXBfZM2UB04,9700
|
27
28
|
unfold/contrib/forms/templates/unfold/forms/wysiwyg.html,sha256=4ZefV6XrjJlUczcuSw8BhvMJUFSZPSXo1IkgkBivh5g,351
|
28
|
-
unfold/contrib/forms/widgets.py,sha256=
|
29
|
+
unfold/contrib/forms/widgets.py,sha256=yWug9YQ6FWI7hqvNZqhCwJhlfC_gMh9GELVGTE2Tw9k,2813
|
29
30
|
unfold/contrib/guardian/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
31
|
unfold/contrib/guardian/apps.py,sha256=ObJqwh4vHxkD4XfduP5IQAiYiWZxsXUOUqF1_R1GsRI,136
|
31
32
|
unfold/contrib/guardian/templates/admin/guardian/model/change_form.html,sha256=FSJc4MYYWyzZAy8Ay0b7Ov-cUo-oELHOM5fQehM54Lg,403
|
@@ -36,21 +37,26 @@ unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.htm
|
|
36
37
|
unfold/contrib/guardian/templates/unfold/guardian/group_form.html,sha256=P8WMC5EejUHV5AxEiIQ2LOGzefLHk5J5UHiNq9wnBgY,4145
|
37
38
|
unfold/contrib/guardian/templates/unfold/guardian/user_form.html,sha256=ci7FRrhTEKbFKKxsJ-07_dWXBYz4mqXPoqu5HfqYLaM,4132
|
38
39
|
unfold/contrib/import_export/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
39
|
-
unfold/contrib/import_export/admin.py,sha256=h6CKRuvloEdxcVScycTSAShXEfzEAsL75uMj2ullhOM,1151
|
40
40
|
unfold/contrib/import_export/apps.py,sha256=SdJu6Qh90VqGWY19FSDhhpUqhTbaIYsJKny3zX5baHI,149
|
41
|
-
unfold/contrib/import_export/forms.py,sha256=
|
41
|
+
unfold/contrib/import_export/forms.py,sha256=cmUUiULJo771rbxf-uCarrbRNwvzCIketbP_7eFnvGc,1496
|
42
42
|
unfold/contrib/import_export/templates/admin/import_export/base.html,sha256=loL2qcV-f8aAzkHss_I4IkwfgemVW2CjOu_aiBxdwX0,357
|
43
|
+
unfold/contrib/import_export/templates/admin/import_export/change_form.html,sha256=_YorgltKO0CRSaLBAp67XnGMUBWEP_XewsUff0hlrE4,446
|
43
44
|
unfold/contrib/import_export/templates/admin/import_export/change_list_export_item.html,sha256=pTDeqPKOlCPKH2dxMIfPnWuc2wVDzB7AzL73WbxSnRY,257
|
44
45
|
unfold/contrib/import_export/templates/admin/import_export/change_list_import_export.html,sha256=JdKd6P2Ot9Ou4yg4CywTauuE1UiTz_mRvDwlx3vj3LI,229
|
45
46
|
unfold/contrib/import_export/templates/admin/import_export/change_list_import_item.html,sha256=XUuRxnsx9YQbKvW-E_JGl_ha7kpTSGSoRefOTTizuX0,233
|
46
|
-
unfold/contrib/import_export/templates/admin/import_export/export.html,sha256=
|
47
|
+
unfold/contrib/import_export/templates/admin/import_export/export.html,sha256=a0gL1YvlFTYQEpJuab-Ue_YL86UO5VkfREqy5CepZUg,3481
|
47
48
|
unfold/contrib/import_export/templates/admin/import_export/import.html,sha256=P54_f3s96PV87Bo-FCZfmsn9DkRXLOB36r7HYF6y7GM,2075
|
48
49
|
unfold/contrib/import_export/templates/admin/import_export/import_confirm.html,sha256=M-acK4XSLHuPFD_NJashGYvPPeJrJsC-3LMvHs3lRis,867
|
49
50
|
unfold/contrib/import_export/templates/admin/import_export/import_errors.html,sha256=0DmJvZs31u-E2Y53yySci86cTnG9aUnOzvfYrOo0lYA,1422
|
50
|
-
unfold/contrib/import_export/templates/admin/import_export/import_form.html,sha256
|
51
|
+
unfold/contrib/import_export/templates/admin/import_export/import_form.html,sha256=mMuMTAbgMbJ1IyCQPBkzmob4m5lJmZ69_QSo3GvvSiY,990
|
51
52
|
unfold/contrib/import_export/templates/admin/import_export/import_preview.html,sha256=pNuLDW6zc5yOF1jurL2EgR0j05RL9ZVJLZiV4R21GJc,2413
|
52
53
|
unfold/contrib/import_export/templates/admin/import_export/import_validation.html,sha256=1wQOiXN_Ga9VO6GGyl__KEiuJlCh4gTqzZdzIbmKxG0,4880
|
53
|
-
unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html,sha256=
|
54
|
+
unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html,sha256=7LzgBiT73c4GHVZx3-ap_cuMz57EnIJ1ESGA5xR31mM,871
|
55
|
+
unfold/contrib/inlines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
56
|
+
unfold/contrib/inlines/admin.py,sha256=p55cNAFOwEFToyFdXUtGl6Vt6j89s9i42V4kHYSZ5Ws,5928
|
57
|
+
unfold/contrib/inlines/apps.py,sha256=Z9JBnzywq-DanZbD56fG0ndBfLXbojzkjVBleqoOBSU,136
|
58
|
+
unfold/contrib/inlines/checks.py,sha256=8sdyBcxw0erqQvp9sHlpGgy0rXfum-cd2eQE0rXFKQ0,559
|
59
|
+
unfold/contrib/inlines/forms.py,sha256=R9OJvrbqNLlKvTxw97JjElCY4CQ3IyRIkjIJUN0gJ9k,1323
|
54
60
|
unfold/contrib/simple_history/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
61
|
unfold/contrib/simple_history/apps.py,sha256=eF_KVYb60CAnGgWk2Z1YKYGfgA3TJBMr229qI7e2pgU,153
|
56
62
|
unfold/contrib/simple_history/templates/simple_history/_object_history_list.html,sha256=aXOQ1zwsRBlFmzODsZApvMtb8t1IPXim6i4plXUR5XE,5112
|
@@ -58,12 +64,14 @@ unfold/contrib/simple_history/templates/simple_history/object_history.html,sha25
|
|
58
64
|
unfold/contrib/simple_history/templates/simple_history/object_history_form.html,sha256=MOL3Tw3Nk3Rnq1koRV7yeCev4CP06_4xqAIOQk1M7FU,2290
|
59
65
|
unfold/contrib/simple_history/templates/simple_history/submit_line.html,sha256=ns9CEkU4HwKHhhj8qj_9UXvzp0viGtD1tp93GV2WRCs,1703
|
60
66
|
unfold/dataclasses.py,sha256=JJdGYzQ8MpeOe2FQPJqrMn_UJcxUz1VJgHCuCtkZCA8,199
|
61
|
-
unfold/decorators.py,sha256=
|
67
|
+
unfold/decorators.py,sha256=6E4vPVwK0IQDAiDPg9pgyypRqciX_gR0jwITDcrSc8U,3367
|
62
68
|
unfold/exceptions.py,sha256=gcCj1ox61E137bk_0Cqy4YC3SttdPgB-fiJUqpmyHSE,43
|
63
|
-
unfold/
|
69
|
+
unfold/fields.py,sha256=01CRr526Fautw58ik5RAGAihwAeLB5mxO6PR-oFto1o,7055
|
70
|
+
unfold/forms.py,sha256=GXEm3CFwglyuEbGdVyEMJTB45Gs-_RvGGlXJEkPy2kw,3688
|
64
71
|
unfold/settings.py,sha256=--TdTSWdOA8TQGW4-vjJkjy_zEyd_kZwBr3BIuQ8hzI,1208
|
65
72
|
unfold/sites.py,sha256=Gy_i43j2nizW2g8-mas5icvtk-beKism_CznATW6Ia8,12586
|
66
|
-
unfold/static/unfold/css/
|
73
|
+
unfold/static/unfold/css/simplebar.css,sha256=5LLaEM11pKi6JFCOLt4XKuZxTpT9rpdq_tNlaQytFlU,4647
|
74
|
+
unfold/static/unfold/css/styles.css,sha256=CU9SCfH3nREI3C6yP9DFAvvr_23lSg9H6rAwM0_w958,91898
|
67
75
|
unfold/static/unfold/fonts/inter/Inter-Bold.woff2,sha256=O88EyjAeRPE_QEyKBKpK5wf2epUOEu8wwjj5bnhCZqE,46552
|
68
76
|
unfold/static/unfold/fonts/inter/Inter-Medium.woff2,sha256=O88EyjAeRPE_QEyKBKpK5wf2epUOEu8wwjj5bnhCZqE,46552
|
69
77
|
unfold/static/unfold/fonts/inter/Inter-Regular.woff2,sha256=O88EyjAeRPE_QEyKBKpK5wf2epUOEu8wwjj5bnhCZqE,46552
|
@@ -76,26 +84,27 @@ unfold/static/unfold/js/alpine.persist.js,sha256=84PZYnPi25AFm7wIWRe1gzA74c5Rv2V
|
|
76
84
|
unfold/static/unfold/js/app.js,sha256=CIitJoFqpeZYPw8icGVXYX9tVRUgqFxcPZ2WjWS8Ylk,5288
|
77
85
|
unfold/static/unfold/js/chart.js,sha256=22W6cFERR-CElMOKRgMMicueMVP0Vf7FBEBYH8Z8tCk,200633
|
78
86
|
unfold/static/unfold/js/htmx.js,sha256=XOLqvnZiyEx46EW9vaJTBUaaWg8CGVVfXJkVsUmJbpI,42820
|
79
|
-
unfold/
|
87
|
+
unfold/static/unfold/js/simplebar.js,sha256=t-uG1FAD6ZoiMeN--wac0XRS7SxoDVG6zvRnGuEp7X8,27176
|
88
|
+
unfold/styles.css,sha256=JA2ybI-pzRM4ZOKshI187oJ5Z3gMNWHQAuEfwq5Xb_E,17854
|
80
89
|
unfold/templates/admin/actions.html,sha256=1tVlUpLoM72K2Ew4vQGcRwPjHuAtO5Jm4QdDsDLOq0I,2625
|
81
90
|
unfold/templates/admin/app_index.html,sha256=lVjMIFsspHQ09LGHKfdfg7TlqlL39AX5LbwoeoZjFhk,1335
|
82
|
-
unfold/templates/admin/app_list.html,sha256=
|
91
|
+
unfold/templates/admin/app_list.html,sha256=krDzw2EXqqvIi8bJtPhJsNran9H7hwdhM6ZW_IRlDwQ,3038
|
83
92
|
unfold/templates/admin/auth/user/add_form.html,sha256=iLig-vd2YExXsj0xGBwYhZ4kGUihwYtLtNVhzObgSzg,478
|
84
93
|
unfold/templates/admin/auth/user/change_password.html,sha256=-Wa9ml3yss-kDz0YQxCiwoxs91KQD8eetCt5l6xekWM,2892
|
85
94
|
unfold/templates/admin/base.html,sha256=MGqtCcydXZPnp6dasaWktyd9D6rdUYX01rFGAv7Zkm4,2226
|
86
95
|
unfold/templates/admin/base_site.html,sha256=3ckWrcAdd7Pw1hk6Zwyknab_Qb-rteV9-mXhMnfo6VI,361
|
87
|
-
unfold/templates/admin/change_form.html,sha256
|
96
|
+
unfold/templates/admin/change_form.html,sha256=s7iHvcH1coJQu7QaDk6vx_tvgxMG8SQWEy4J9bBllzY,5361
|
88
97
|
unfold/templates/admin/change_form_object_tools.html,sha256=eyeH-i2HgEM0Yi-OJA2D1VnKJyC19A_my1IDGxxoP8Y,593
|
89
98
|
unfold/templates/admin/change_list.html,sha256=18GDZswc1c0xtw2BcKti9SX95Ar9e1BX_HSY0K79g_8,5102
|
90
99
|
unfold/templates/admin/change_list_object_tools.html,sha256=cmMiT2nT20Ph5yfpj9aHPr76Z-JP4aSXp0o-Rnad28s,147
|
91
|
-
unfold/templates/admin/change_list_results.html,sha256=
|
100
|
+
unfold/templates/admin/change_list_results.html,sha256=Ie2NGfIiU0HHy1a5wJ-YJV1doZkibVRCmR3DRZdoV2I,5395
|
92
101
|
unfold/templates/admin/date_hierarchy.html,sha256=BfUPbsLpHZVa40BHBahz1H9RSVuz36Jc3yrlobOiIpw,1306
|
93
102
|
unfold/templates/admin/delete_confirmation.html,sha256=hpa2E14oZEXBBs6W1qdNQuF650TIO2Rhr52Q6UfwVeQ,5166
|
94
103
|
unfold/templates/admin/delete_selected_confirmation.html,sha256=Foka2yvwAMEZre-Kh1KNadRzrCotdKM2U4e6AJQYZu8,4941
|
95
|
-
unfold/templates/admin/edit_inline/stacked.html,sha256=
|
96
|
-
unfold/templates/admin/edit_inline/tabular.html,sha256=
|
104
|
+
unfold/templates/admin/edit_inline/stacked.html,sha256=HG-Dj42gcKNofHRVjg0ltIER2oJGYUd9GN_B7lDv7rQ,4580
|
105
|
+
unfold/templates/admin/edit_inline/tabular.html,sha256=aMy6kk3ZDBtgZFf1pO30vHWmONvCYor9yoiR9Day4xI,13042
|
97
106
|
unfold/templates/admin/filter.html,sha256=dkrFkei-EAlldIU8DrgvSChzWQuUOu6-LS_qlZxdfFw,1708
|
98
|
-
unfold/templates/admin/includes/fieldset.html,sha256=
|
107
|
+
unfold/templates/admin/includes/fieldset.html,sha256=lMVwBifFWKvLvHqZ6yjP6Xf6BJFzi-EOf5JHIxEHmRI,888
|
99
108
|
unfold/templates/admin/includes/object_delete_summary.html,sha256=Nv69SCzyJHFX14iJFfodxKM0IIpQegKZH0fvKB15QJI,468
|
100
109
|
unfold/templates/admin/index.html,sha256=pkGdKWdD3zzOvkRdELvdb15sleSpfl4eHPA14PAh7z0,684
|
101
110
|
unfold/templates/admin/login.html,sha256=WdOfFLofwBWj9VKCq1U22uLY19J2YQY6vRaE4OOSKfQ,3681
|
@@ -122,17 +131,19 @@ unfold/templates/unfold/components/title.html,sha256=PSiNK-s8jUJfu6f9zCcGOOyLiKx
|
|
122
131
|
unfold/templates/unfold/helpers/account_links.html,sha256=Kc1FobAVN3B34x9vcMoEXyuQ45d5SjfjaJSo3UErL3Q,1934
|
123
132
|
unfold/templates/unfold/helpers/actions_row.html,sha256=1xd39zx38NOoKuDuxAG7PHeu5x2OTIraQGFkm15Erqg,1681
|
124
133
|
unfold/templates/unfold/helpers/add_link.html,sha256=mIgpKrwqBO1oJ4cwPQWSX1oUHBwHJmy5-2TxUHf-1bo,808
|
125
|
-
unfold/templates/unfold/helpers/app_list.html,sha256=
|
134
|
+
unfold/templates/unfold/helpers/app_list.html,sha256=lFnW8p9DcZbI9t3_ee9JX9ERHA0NRL2V88zpzuG4jq8,4720
|
126
135
|
unfold/templates/unfold/helpers/app_list_default.html,sha256=vZkw1F7oHOKReNkdHRYjhuNdA1nNdvSD4wbDmf0bnsM,4102
|
127
136
|
unfold/templates/unfold/helpers/boolean.html,sha256=p_WOlytoXvDwta76WgcV4JSWKpBgKf4amhqmHF798F8,564
|
128
137
|
unfold/templates/unfold/helpers/breadcrumb_item.html,sha256=k_1j57UV0WtzFFlMKaewj4NLbR_DhXI6RzCHThblZLw,234
|
129
|
-
unfold/templates/unfold/helpers/display_header.html,sha256=
|
138
|
+
unfold/templates/unfold/helpers/display_header.html,sha256=HiuaIJ6Y8DSM99OFypLO_2uQadZIw7tSg1aJJpFEXkM,1161
|
130
139
|
unfold/templates/unfold/helpers/display_label.html,sha256=LS9DWzYjHkYLV27sZDwyXlg2sLJ0AlId9FbjnXpsbfg,317
|
131
|
-
unfold/templates/unfold/helpers/field.html,sha256=
|
132
|
-
unfold/templates/unfold/helpers/field_readonly.html,sha256=
|
133
|
-
unfold/templates/unfold/helpers/
|
140
|
+
unfold/templates/unfold/helpers/field.html,sha256=Ds-zUHkdyxamfUCVNhxvtM0XoJg9OCA0QcsLbLWv4oo,882
|
141
|
+
unfold/templates/unfold/helpers/field_readonly.html,sha256=O0gHEW46OWt1oUUk0lZiyR-mztWv_7IH6GpKRm2wUw4,235
|
142
|
+
unfold/templates/unfold/helpers/field_readonly_value.html,sha256=wdzHVKaI96S-S1MaV-odKXDdT_MRkfWMcXdUBhRlTDY,490
|
143
|
+
unfold/templates/unfold/helpers/fieldset_row.html,sha256=HHGzZ09Irtj4PUGXZkbAAvJcBFGcvJqjR7k-TZzpVyU,2730
|
144
|
+
unfold/templates/unfold/helpers/fieldsets_tabs.html,sha256=V3bgW75eozaBDty-xfciGafhCWq_Ba5HfQkk92yRc9A,1445
|
134
145
|
unfold/templates/unfold/helpers/form_errors.html,sha256=EwerIJptSCWXvtAJ1IZKfEn98qlShBIGavsTThbklAs,266
|
135
|
-
unfold/templates/unfold/helpers/form_label.html,sha256=
|
146
|
+
unfold/templates/unfold/helpers/form_label.html,sha256=SR4U6iK9w4oels6iGY_Da-yN4BbXQVN9zCDlBGGXcw8,310
|
136
147
|
unfold/templates/unfold/helpers/header.html,sha256=jklASqVEJRa22jxdXq_X2UZb2uYn_ywaZbI2N4nBv20,998
|
137
148
|
unfold/templates/unfold/helpers/help_text.html,sha256=iBXw0LWfoYT5PoPLJek9ixv4SuK7hfVkjPgmv2ODW-s,146
|
138
149
|
unfold/templates/unfold/helpers/history.html,sha256=0_Imm7Fc1PqjjXEmHh8QEQg_qIqBZWx2BeK5o3axfvY,2017
|
@@ -155,10 +166,11 @@ unfold/templates/unfold/helpers/theme_switch.html,sha256=skkl6fYUnYLM7fAPivHxWjn
|
|
155
166
|
unfold/templates/unfold/helpers/userlinks.html,sha256=qWjtBt9Q_tU8a874ii0Qqg8t_d-SSYBTB_3QZfNlx9g,634
|
156
167
|
unfold/templates/unfold/helpers/welcomemsg.html,sha256=noRysgSENef4_53pXaTiBCy2or6lQm1ZtmCQVODAB1c,1120
|
157
168
|
unfold/templates/unfold/layouts/base_simple.html,sha256=rki7n7QagHFAaCEn488pTOj9dpNL9AwwzKps8Ipiubk,993
|
158
|
-
unfold/templates/unfold/layouts/skeleton.html,sha256=
|
159
|
-
unfold/templates/unfold/widgets/clearable_file_input.html,sha256=
|
169
|
+
unfold/templates/unfold/layouts/skeleton.html,sha256=iXrUiggVp36vmBIia5p32c-9Ruy0PxkFFQogDpvENbk,3419
|
170
|
+
unfold/templates/unfold/widgets/clearable_file_input.html,sha256=gAJsfyCnanOyeN4ypp1y7r76znvITV7FYTyWvPsmlDs,1882
|
160
171
|
unfold/templates/unfold/widgets/clearable_file_input_small.html,sha256=rqUnHF4jwL8_RySUuq2aXgj-0P_usgo1HeVT_IcfyFY,2531
|
161
172
|
unfold/templates/unfold/widgets/date.html,sha256=WXo2LG1v_gBZBSg-zocj7oujMKI0MWLYCIFfB04HMLQ,122
|
173
|
+
unfold/templates/unfold/widgets/foreign_key_raw_id.html,sha256=BgXi4ziywtkWmZuUye5bbJ6yeEoHDJB_2lkwXX8hc6c,1026
|
162
174
|
unfold/templates/unfold/widgets/radio.html,sha256=3WcmclQNg7R_pRjEHL1dHkGjAzWlWNYnhHkAirC4nuA,646
|
163
175
|
unfold/templates/unfold/widgets/radio_option.html,sha256=IZgPx-aWKJuxrSalJ3K50RFd1vwSpb9Qk0yZwfV78_A,368
|
164
176
|
unfold/templates/unfold/widgets/range.html,sha256=28FBtSUgUcG82vpk_I27Lbs5oWZOV_oMzVhx4wj3-Ik,262
|
@@ -166,7 +178,8 @@ unfold/templates/unfold/widgets/related_widget_wrapper.html,sha256=0I6wSu8z_sJPq
|
|
166
178
|
unfold/templates/unfold/widgets/split_datetime.html,sha256=eXLFZyCv84LCTFWAUhNO3xAIzWvGBvI1ZpYbB38_HOI,862
|
167
179
|
unfold/templates/unfold/widgets/split_datetime_vertical.html,sha256=xinCH4kkQ-yKUqcSI7-m-_UEzOEKWqvLTjUa3i-e8EM,881
|
168
180
|
unfold/templates/unfold/widgets/split_money.html,sha256=AFLUBmzGbY-RXgsfz7gaDxVRhplaIPhXjg_hWYo9xcY,352
|
169
|
-
unfold/templates/unfold/widgets/textarea.html,sha256
|
181
|
+
unfold/templates/unfold/widgets/textarea.html,sha256=-ZLDGrtASero7L-J3VvTNq_KjPAZh_kLVw0Ow3awqXM,144
|
182
|
+
unfold/templates/unfold/widgets/textarea_expandable.html,sha256=4xIGWb20DuwiwumndByrAyh7xF-ReXKLulSpDImX_cs,459
|
170
183
|
unfold/templates/unfold/widgets/time.html,sha256=WXo2LG1v_gBZBSg-zocj7oujMKI0MWLYCIFfB04HMLQ,122
|
171
184
|
unfold/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
172
185
|
unfold/templatetags/unfold.py,sha256=HFe0GrTD4va0lLzsZhxqjEOONmehqOWdf5vulkxgfGU,6302
|
@@ -174,8 +187,8 @@ unfold/templatetags/unfold_list.py,sha256=5xAjQX0_JnVwDaj-wGkGqbjOAtp-a18koWIKj5
|
|
174
187
|
unfold/typing.py,sha256=1P8PWM2oeaceUJtA5j071RbKEBpHYaux441u7Hd6wv4,643
|
175
188
|
unfold/utils.py,sha256=5OIgDcwvIJQbwbnnqHx61cHh-2T1h184mTAuNq5WXLI,4088
|
176
189
|
unfold/views.py,sha256=Ml3XlEoHLcbEWof59Dw8ihKBMcmp-gBAibThtBFj55A,708
|
177
|
-
unfold/widgets.py,sha256=
|
178
|
-
django_unfold-0.
|
179
|
-
django_unfold-0.
|
180
|
-
django_unfold-0.
|
181
|
-
django_unfold-0.
|
190
|
+
unfold/widgets.py,sha256=iiI73XznYH34LgXBHu1oVtYb76lLO3HiOD9blmm89rY,15236
|
191
|
+
django_unfold-0.27.0.dist-info/LICENSE.md,sha256=Ltk_quRyyvV3J5v3brtOqmibeZSw2Hrb8bY1W3ya0Ik,1077
|
192
|
+
django_unfold-0.27.0.dist-info/METADATA,sha256=llKdxPkbhzG4E-UJji8aTaQkjm5YTf9DBP4OSC9pa6c,49139
|
193
|
+
django_unfold-0.27.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
194
|
+
django_unfold-0.27.0.dist-info/RECORD,,
|
unfold/admin.py
CHANGED
@@ -7,54 +7,35 @@ from django.contrib.admin import ModelAdmin as BaseModelAdmin
|
|
7
7
|
from django.contrib.admin import StackedInline as BaseStackedInline
|
8
8
|
from django.contrib.admin import TabularInline as BaseTabularInline
|
9
9
|
from django.contrib.admin import display, helpers
|
10
|
-
from django.contrib.admin.utils import lookup_field
|
11
10
|
from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
|
12
|
-
from django.core.exceptions import ObjectDoesNotExist
|
13
11
|
from django.db import models
|
14
|
-
from django.db.models import
|
15
|
-
BLANK_CHOICE_DASH,
|
16
|
-
ForeignObjectRel,
|
17
|
-
JSONField,
|
18
|
-
ManyToManyRel,
|
19
|
-
Model,
|
20
|
-
OneToOneField,
|
21
|
-
)
|
12
|
+
from django.db.models import BLANK_CHOICE_DASH, Model
|
22
13
|
from django.db.models.fields import Field
|
23
14
|
from django.db.models.fields.related import ForeignKey, ManyToManyField
|
24
15
|
from django.forms import Form
|
25
16
|
from django.forms.fields import TypedChoiceField
|
26
|
-
from django.forms.models import
|
27
|
-
ModelChoiceField,
|
28
|
-
ModelMultipleChoiceField,
|
29
|
-
)
|
30
|
-
from django.forms.utils import flatatt
|
17
|
+
from django.forms.models import ModelChoiceField, ModelMultipleChoiceField
|
31
18
|
from django.forms.widgets import SelectMultiple
|
32
19
|
from django.http import HttpRequest, HttpResponse
|
33
20
|
from django.shortcuts import redirect
|
34
|
-
from django.template.defaultfilters import linebreaksbr
|
35
21
|
from django.template.response import TemplateResponse
|
36
22
|
from django.urls import URLPattern, path, reverse
|
37
|
-
from django.utils.
|
38
|
-
from django.utils.module_loading import import_string
|
39
|
-
from django.utils.safestring import SafeText, mark_safe
|
40
|
-
from django.utils.text import capfirst
|
23
|
+
from django.utils.safestring import mark_safe
|
41
24
|
from django.utils.translation import gettext_lazy as _
|
42
25
|
from django.views import View
|
43
26
|
|
44
27
|
from .checks import UnfoldModelAdminChecks
|
45
28
|
from .dataclasses import UnfoldAction
|
46
29
|
from .exceptions import UnfoldException
|
30
|
+
from .fields import UnfoldAdminField, UnfoldAdminReadonlyField
|
47
31
|
from .forms import ActionForm
|
48
|
-
from .settings import get_config
|
49
32
|
from .typing import FieldsetsType
|
50
|
-
from .utils import display_for_field
|
51
33
|
from .widgets import (
|
52
|
-
CHECKBOX_LABEL_CLASSES,
|
53
|
-
LABEL_CLASSES,
|
54
34
|
SELECT_CLASSES,
|
55
35
|
UnfoldAdminBigIntegerFieldWidget,
|
56
36
|
UnfoldAdminDecimalFieldWidget,
|
57
37
|
UnfoldAdminEmailInputWidget,
|
38
|
+
UnfoldAdminFileFieldWidget,
|
58
39
|
UnfoldAdminImageFieldWidget,
|
59
40
|
UnfoldAdminImageSmallFieldWidget,
|
60
41
|
UnfoldAdminIntegerFieldWidget,
|
@@ -71,6 +52,7 @@ from .widgets import (
|
|
71
52
|
UnfoldAdminUUIDInputWidget,
|
72
53
|
UnfoldBooleanSwitchWidget,
|
73
54
|
UnfoldBooleanWidget,
|
55
|
+
UnfoldForeignKeyRawIdWidget,
|
74
56
|
)
|
75
57
|
|
76
58
|
try:
|
@@ -88,8 +70,6 @@ try:
|
|
88
70
|
except ImportError:
|
89
71
|
HAS_MONEY = False
|
90
72
|
|
91
|
-
checkbox = UnfoldBooleanWidget({"class": "action-select"}, lambda value: False)
|
92
|
-
|
93
73
|
FORMFIELD_OVERRIDES = {
|
94
74
|
models.DateTimeField: {
|
95
75
|
"form_class": forms.SplitDateTimeField,
|
@@ -109,6 +89,7 @@ FORMFIELD_OVERRIDES = {
|
|
109
89
|
models.BigIntegerField: {"widget": UnfoldAdminBigIntegerFieldWidget},
|
110
90
|
models.DecimalField: {"widget": UnfoldAdminDecimalFieldWidget},
|
111
91
|
models.FloatField: {"widget": UnfoldAdminDecimalFieldWidget},
|
92
|
+
models.FileField: {"widget": UnfoldAdminFileFieldWidget},
|
112
93
|
models.ImageField: {"widget": UnfoldAdminImageFieldWidget},
|
113
94
|
models.JSONField: {"widget": UnfoldAdminTextareaWidget},
|
114
95
|
models.DurationField: {"widget": UnfoldAdminTextInputWidget},
|
@@ -138,139 +119,10 @@ FORMFIELD_OVERRIDES_INLINE.update(
|
|
138
119
|
}
|
139
120
|
)
|
140
121
|
|
141
|
-
|
142
|
-
class UnfoldAdminField(helpers.AdminField):
|
143
|
-
def label_tag(self) -> SafeText:
|
144
|
-
classes = []
|
145
|
-
|
146
|
-
if not self.field.field.widget.__class__.__name__.startswith(
|
147
|
-
"Unfold"
|
148
|
-
) and not self.field.field.widget.template_name.startswith("unfold"):
|
149
|
-
return super().label_tag()
|
150
|
-
|
151
|
-
# TODO load config from current AdminSite (override Fieldline.__iter__ method)
|
152
|
-
for lang, flag in get_config()["EXTENSIONS"]["modeltranslation"][
|
153
|
-
"flags"
|
154
|
-
].items():
|
155
|
-
if f"[{lang}]" in self.field.label:
|
156
|
-
self.field.label = self.field.label.replace(f"[{lang}]", flag)
|
157
|
-
break
|
158
|
-
|
159
|
-
contents = conditional_escape(self.field.label)
|
160
|
-
|
161
|
-
if self.is_checkbox:
|
162
|
-
classes.append(" ".join(CHECKBOX_LABEL_CLASSES))
|
163
|
-
else:
|
164
|
-
classes.append(" ".join(LABEL_CLASSES))
|
165
|
-
|
166
|
-
if self.field.field.required:
|
167
|
-
classes.append("required")
|
168
|
-
|
169
|
-
attrs = {"class": " ".join(classes)} if classes else {}
|
170
|
-
required = mark_safe(' <span class="text-red-600">*</span>')
|
171
|
-
|
172
|
-
return self.field.label_tag(
|
173
|
-
contents=mark_safe(contents),
|
174
|
-
attrs=attrs,
|
175
|
-
label_suffix=required if self.field.field.required else "",
|
176
|
-
)
|
177
|
-
|
122
|
+
checkbox = UnfoldBooleanWidget({"class": "action-select"}, lambda value: False)
|
178
123
|
|
179
124
|
helpers.AdminField = UnfoldAdminField
|
180
125
|
|
181
|
-
|
182
|
-
class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
183
|
-
def label_tag(self) -> SafeText:
|
184
|
-
if not isinstance(self.model_admin, ModelAdmin):
|
185
|
-
return super().label_tag()
|
186
|
-
|
187
|
-
attrs = {
|
188
|
-
"class": " ".join(LABEL_CLASSES + ["mb-2"]),
|
189
|
-
}
|
190
|
-
|
191
|
-
label = self.field["label"]
|
192
|
-
|
193
|
-
return format_html(
|
194
|
-
"<label{}>{}{}</label>",
|
195
|
-
flatatt(attrs),
|
196
|
-
capfirst(label),
|
197
|
-
self.form.label_suffix,
|
198
|
-
)
|
199
|
-
|
200
|
-
def is_json(self) -> bool:
|
201
|
-
field, obj, model_admin = (
|
202
|
-
self.field["field"],
|
203
|
-
self.form.instance,
|
204
|
-
self.model_admin,
|
205
|
-
)
|
206
|
-
|
207
|
-
try:
|
208
|
-
f, attr, value = lookup_field(field, obj, model_admin)
|
209
|
-
except (AttributeError, ValueError, ObjectDoesNotExist):
|
210
|
-
return False
|
211
|
-
|
212
|
-
return isinstance(f, JSONField)
|
213
|
-
|
214
|
-
def contents(self) -> str:
|
215
|
-
contents = self._get_contents()
|
216
|
-
contents = self._preprocess_field(contents)
|
217
|
-
return contents
|
218
|
-
|
219
|
-
def _get_contents(self) -> str:
|
220
|
-
from django.contrib.admin.templatetags.admin_list import _boolean_icon
|
221
|
-
|
222
|
-
field, obj, model_admin = (
|
223
|
-
self.field["field"],
|
224
|
-
self.form.instance,
|
225
|
-
self.model_admin,
|
226
|
-
)
|
227
|
-
try:
|
228
|
-
f, attr, value = lookup_field(field, obj, model_admin)
|
229
|
-
except (AttributeError, ValueError, ObjectDoesNotExist):
|
230
|
-
result_repr = self.empty_value_display
|
231
|
-
else:
|
232
|
-
if field in self.form.fields:
|
233
|
-
widget = self.form[field].field.widget
|
234
|
-
# This isn't elegant but suffices for contrib.auth's
|
235
|
-
# ReadOnlyPasswordHashWidget.
|
236
|
-
if getattr(widget, "read_only", False):
|
237
|
-
return widget.render(field, value)
|
238
|
-
if f is None:
|
239
|
-
if getattr(attr, "boolean", False):
|
240
|
-
result_repr = _boolean_icon(value)
|
241
|
-
else:
|
242
|
-
if hasattr(value, "__html__"):
|
243
|
-
result_repr = value
|
244
|
-
else:
|
245
|
-
result_repr = linebreaksbr(value)
|
246
|
-
else:
|
247
|
-
if isinstance(f.remote_field, ManyToManyRel) and value is not None:
|
248
|
-
result_repr = ", ".join(map(str, value.all()))
|
249
|
-
elif (
|
250
|
-
isinstance(f.remote_field, (ForeignObjectRel, OneToOneField))
|
251
|
-
and value is not None
|
252
|
-
):
|
253
|
-
result_repr = self.get_admin_url(f.remote_field, value)
|
254
|
-
else:
|
255
|
-
result_repr = display_for_field(value, f, self.empty_value_display)
|
256
|
-
return conditional_escape(result_repr)
|
257
|
-
result_repr = linebreaksbr(result_repr)
|
258
|
-
return conditional_escape(result_repr)
|
259
|
-
|
260
|
-
def _preprocess_field(self, contents: str) -> str:
|
261
|
-
if (
|
262
|
-
hasattr(self.model_admin, "readonly_preprocess_fields")
|
263
|
-
and self.field["field"] in self.model_admin.readonly_preprocess_fields
|
264
|
-
):
|
265
|
-
func = self.model_admin.readonly_preprocess_fields[self.field["field"]]
|
266
|
-
if isinstance(func, str):
|
267
|
-
contents = import_string(func)(contents)
|
268
|
-
elif callable(func):
|
269
|
-
contents = func(contents)
|
270
|
-
|
271
|
-
return contents
|
272
|
-
|
273
|
-
|
274
126
|
helpers.AdminReadonlyField = UnfoldAdminReadonlyField
|
275
127
|
|
276
128
|
|
@@ -305,10 +157,14 @@ class ModelAdminMixin:
|
|
305
157
|
def formfield_for_foreignkey(
|
306
158
|
self, db_field: ForeignKey, request: HttpRequest, **kwargs
|
307
159
|
) -> Optional[ModelChoiceField]:
|
160
|
+
db = kwargs.get("using")
|
161
|
+
|
308
162
|
# Overrides widgets for all related fields
|
309
163
|
if "widget" not in kwargs:
|
310
164
|
if db_field.name in self.raw_id_fields:
|
311
|
-
kwargs["widget"] =
|
165
|
+
kwargs["widget"] = UnfoldForeignKeyRawIdWidget(
|
166
|
+
db_field.remote_field, self.admin_site, using=db
|
167
|
+
)
|
312
168
|
elif (
|
313
169
|
db_field.name not in self.get_autocomplete_fields(request)
|
314
170
|
and db_field.name not in self.radio_fields
|
@@ -0,0 +1,31 @@
|
|
1
|
+
{% load i18n %}
|
2
|
+
|
3
|
+
<div class="flex flex-col gap-4" x-data="{items: []}">
|
4
|
+
{% for subwidget in widget.subwidgets %}
|
5
|
+
<div class="flex flex-row">
|
6
|
+
{% with widget=subwidget %}
|
7
|
+
{% include widget.template_name %}
|
8
|
+
{% endwith %}
|
9
|
+
|
10
|
+
<a x-on:click="$el.parentElement.remove()" class="bg-white border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-red-600 text-sm w-9.5 dark:bg-gray-900 dark:border-gray-700 dark:text-red-500">
|
11
|
+
<span class="material-symbols-outlined text-sm">delete</span>
|
12
|
+
</a>
|
13
|
+
</div>
|
14
|
+
{% endfor %}
|
15
|
+
|
16
|
+
<template x-for="(item, index) in items" :key="item.key">
|
17
|
+
<div class="flex flex-row">
|
18
|
+
{% include template.template_name with widget=template %}
|
19
|
+
|
20
|
+
<a x-on:click="items.splice(index, 1)" class="bg-white border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-red-600 text-sm w-9.5 dark:bg-gray-900 dark:border-gray-700 dark:text-red-500">
|
21
|
+
<span class="material-symbols-outlined text-sm">delete</span>
|
22
|
+
</a>
|
23
|
+
</div>
|
24
|
+
</template>
|
25
|
+
|
26
|
+
<div class="flex flex-row">
|
27
|
+
<div x-on:click="items.push({ key: new Date().getTime()})" class="bg-primary-600 border border-transparent cursor-pointer font-medium inline-block px-3 py-2 rounded-md text-sm text-white w-full lg:w-auto">
|
28
|
+
{% trans "Add new item" %}
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
</div>
|