django-unfold 0.27.0__py3-none-any.whl → 0.28.1__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.27.0.dist-info → django_unfold-0.28.1.dist-info}/METADATA +64 -9
- {django_unfold-0.27.0.dist-info → django_unfold-0.28.1.dist-info}/RECORD +30 -27
- unfold/admin.py +1 -0
- unfold/contrib/forms/widgets.py +2 -0
- unfold/contrib/import_export/forms.py +21 -7
- unfold/contrib/import_export/templates/admin/import_export/change_list_export.html +5 -0
- unfold/contrib/import_export/templates/admin/import_export/export.html +1 -1
- unfold/contrib/simple_history/templates/simple_history/object_history.html +3 -5
- unfold/contrib/simple_history/templates/simple_history/{_object_history_list.html → object_history_list.html} +49 -11
- unfold/dataclasses.py +2 -0
- unfold/fields.py +9 -1
- unfold/static/unfold/css/styles.css +1 -1
- unfold/styles.css +1 -2
- unfold/templates/admin/login.html +4 -0
- unfold/templates/admin/submit_line.html +1 -1
- unfold/templates/unfold/helpers/attrs.html +1 -0
- unfold/templates/unfold/helpers/boolean.html +1 -1
- unfold/templates/unfold/helpers/field_readonly_value.html +1 -1
- unfold/templates/unfold/helpers/fieldset_row.html +2 -2
- unfold/templates/unfold/helpers/navigation.html +1 -1
- unfold/templates/unfold/helpers/search_results.html +10 -10
- unfold/templates/unfold/layouts/base.html +11 -0
- unfold/templates/unfold/layouts/base_simple.html +7 -1
- unfold/templates/unfold/widgets/clearable_file_input_small.html +1 -1
- unfold/templates/unfold/widgets/split_datetime.html +7 -7
- unfold/utils.py +21 -1
- unfold/views.py +18 -6
- unfold/widgets.py +12 -1
- {django_unfold-0.27.0.dist-info → django_unfold-0.28.1.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.27.0.dist-info → django_unfold-0.28.1.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.28.1
|
4
4
|
Summary: Modern Django admin theme for seamless interface development
|
5
5
|
Home-page: https://unfoldadmin.com
|
6
6
|
License: MIT
|
@@ -35,13 +35,13 @@ Description-Content-Type: text/markdown
|
|
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.
|
37
37
|
|
38
|
-
- **Unfold:** demo site is available at [unfoldadmin.com](https://unfoldadmin.com)
|
38
|
+
- **Unfold:** demo site is available at [unfoldadmin.com](https://unfoldadmin.com?utm_medium=github&utm_source=unfold)
|
39
39
|
- **Formula:** repository with demo implementation at [github.com/unfoldadmin/formula](https://github.com/unfoldadmin/formula)
|
40
40
|
- **Turbo:** Django & Next.js boilerplate implementing Unfold at [github.com/unfoldadmin/turbo](https://github.com/unfoldadmin/turbo)
|
41
41
|
|
42
42
|
## Are you using Unfold and need a help?<!-- omit from toc -->
|
43
43
|
|
44
|
-
Did you decide to start using Unfold but you don't have time to make the switch from native Django admin? [Get in touch with us](https://unfoldadmin.com
|
44
|
+
Did you decide to start using Unfold but you don't have time to make the switch from native Django admin? [Get in touch with us](https://unfoldadmin.com/?utm_medium=github&utm_source=unfold) and let's supercharge development by using our know-how.
|
45
45
|
|
46
46
|
## Features <!-- omit from toc -->
|
47
47
|
|
@@ -62,7 +62,7 @@ Did you decide to start using Unfold but you don't have time to make the switch
|
|
62
62
|
- **Third party packages:** default support for multiple popular applications
|
63
63
|
- **Environment label**: distinguish between environments by displaying a label
|
64
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
|
65
|
+
- **Parallel admin**: support for default admin in parallel with Unfold. [Admin migration guide](https://unfoldadmin.com/blog/migrating-django-admin-unfold/?utm_medium=github&utm_source=unfold)
|
66
66
|
- **VS Code**: project configuration and development container is included
|
67
67
|
|
68
68
|
## Table of contents <!-- omit from toc -->
|
@@ -81,6 +81,7 @@ Did you decide to start using Unfold but you don't have time to make the switch
|
|
81
81
|
- [Dropdown filters](#dropdown-filters)
|
82
82
|
- [Numeric filters](#numeric-filters)
|
83
83
|
- [Date/time filters](#datetime-filters)
|
84
|
+
- [Custom admin pages](#custom-admin-pages)
|
84
85
|
- [Nonrelated inlines](#nonrelated-inlines)
|
85
86
|
- [Display decorator](#display-decorator)
|
86
87
|
- [Change form tabs](#change-form-tabs)
|
@@ -645,6 +646,45 @@ class YourModelAdmin(ModelAdmin):
|
|
645
646
|
)
|
646
647
|
```
|
647
648
|
|
649
|
+
## Custom admin pages
|
650
|
+
|
651
|
+
By default, Unfold provides a basic view mixin which helps with creation of basic views which are part of Unfold UI. The implementation requires creation of class based view inheriting from `unfold.views.UnfoldModelAdminViewMixin`. It is important to add `title` and `permissions_required` properties.
|
652
|
+
|
653
|
+
```python
|
654
|
+
# admin.py
|
655
|
+
|
656
|
+
from django.views.generic import TemplateView
|
657
|
+
from unfold.admin import ModelAdmin
|
658
|
+
from unfold.views import UnfoldModelAdminViewMixin
|
659
|
+
|
660
|
+
|
661
|
+
class MyClassBasedView(UnfoldModelAdminViewMixin, TemplateView):
|
662
|
+
title = "Custom Title" # required: custom page header title
|
663
|
+
permissions_required = () # required: tuple of permissions
|
664
|
+
template_name = "some/template/path.html"
|
665
|
+
|
666
|
+
|
667
|
+
class CustomAdmin(ModelAdmin):
|
668
|
+
def get_urls(self):
|
669
|
+
return super().get_urls() + [
|
670
|
+
path(
|
671
|
+
"custom-url-path",
|
672
|
+
MyClassBasedView.as_view(model_admin=self), # IMPORTANT: model_admin is required
|
673
|
+
name="custom_name"
|
674
|
+
),
|
675
|
+
]
|
676
|
+
```
|
677
|
+
|
678
|
+
The template is straightforward, extend from `unfold/layouts/base.html` and the UI will display all Unfold components like header or sidebar with all menu items. Then all content needs to be located in `content` block.
|
679
|
+
|
680
|
+
```django-html
|
681
|
+
{% extends "unfold/layouts/base.html" %}
|
682
|
+
|
683
|
+
{% block content %}
|
684
|
+
Content here
|
685
|
+
{% endblock %}
|
686
|
+
```
|
687
|
+
|
648
688
|
## Nonrelated inlines
|
649
689
|
|
650
690
|
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.
|
@@ -826,7 +866,10 @@ from django_celery_beat.models import (
|
|
826
866
|
PeriodicTask,
|
827
867
|
SolarSchedule,
|
828
868
|
)
|
829
|
-
|
869
|
+
from django_celery_beat.admin import ClockedScheduleAdmin as BaseClockedScheduleAdmin
|
870
|
+
from django_celery_beat.admin import CrontabScheduleAdmin as BaseCrontabScheduleAdmin
|
871
|
+
from django_celery_beat.admin import PeriodicTaskAdmin as BasePeriodicTaskAdmin
|
872
|
+
from django_celery_beat.admin import PeriodicTaskForm, TaskSelectWidget
|
830
873
|
|
831
874
|
admin.site.unregister(PeriodicTask)
|
832
875
|
admin.site.unregister(IntervalSchedule)
|
@@ -834,18 +877,30 @@ admin.site.unregister(CrontabSchedule)
|
|
834
877
|
admin.site.unregister(SolarSchedule)
|
835
878
|
admin.site.unregister(ClockedSchedule)
|
836
879
|
|
837
|
-
|
838
|
-
class
|
880
|
+
|
881
|
+
class UnfoldTaskSelectWidget(UnfoldAdminSelectWidget, TaskSelectWidget):
|
839
882
|
pass
|
840
883
|
|
841
884
|
|
885
|
+
class UnfoldPeriodicTaskForm(PeriodicTaskForm):
|
886
|
+
def __init__(self, *args, **kwargs):
|
887
|
+
super().__init__(*args, **kwargs)
|
888
|
+
self.fields["task"].widget = UnfoldAdminTextInputWidget()
|
889
|
+
self.fields["regtask"].widget = UnfoldTaskSelectWidget()
|
890
|
+
|
891
|
+
|
892
|
+
@admin.register(PeriodicTask)
|
893
|
+
class PeriodicTaskAdmin(BasePeriodicTaskAdmin, ModelAdmin):
|
894
|
+
form = UnfoldPeriodicTaskForm
|
895
|
+
|
896
|
+
|
842
897
|
@admin.register(IntervalSchedule)
|
843
898
|
class IntervalScheduleAdmin(ModelAdmin):
|
844
899
|
pass
|
845
900
|
|
846
901
|
|
847
902
|
@admin.register(CrontabSchedule)
|
848
|
-
class CrontabScheduleAdmin(ModelAdmin):
|
903
|
+
class CrontabScheduleAdmin(BaseCrontabScheduleAdmin, ModelAdmin):
|
849
904
|
pass
|
850
905
|
|
851
906
|
|
@@ -854,7 +909,7 @@ class SolarScheduleAdmin(ModelAdmin):
|
|
854
909
|
pass
|
855
910
|
|
856
911
|
@admin.register(ClockedSchedule)
|
857
|
-
class ClockedScheduleAdmin(ModelAdmin):
|
912
|
+
class ClockedScheduleAdmin(BaseClockedScheduleAdmin, ModelAdmin):
|
858
913
|
pass
|
859
914
|
```
|
860
915
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
unfold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
unfold/admin.py,sha256=
|
2
|
+
unfold/admin.py,sha256=Q8my6Ui10yDW_KqTwfPK8p51u9SAj2n5f8DFF8lxYuk,18991
|
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
|
@@ -26,7 +26,7 @@ unfold/contrib/forms/static/unfold/forms/js/trix.js,sha256=Pao0XiVeDiRawfTkGDg_n
|
|
26
26
|
unfold/contrib/forms/templates/unfold/forms/array.html,sha256=11silyHbsJA0U_ksS8MvfFOJKC_qKTAwXxoMIB78APk,1507
|
27
27
|
unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html,sha256=yS8Zy-UrzvZ5RUYwdprQzREffnYq0NlIbXBfZM2UB04,9700
|
28
28
|
unfold/contrib/forms/templates/unfold/forms/wysiwyg.html,sha256=4ZefV6XrjJlUczcuSw8BhvMJUFSZPSXo1IkgkBivh5g,351
|
29
|
-
unfold/contrib/forms/widgets.py,sha256=
|
29
|
+
unfold/contrib/forms/widgets.py,sha256=D8utpEv-t_VDkppICA_0jtHvke7uElzAgaGMqd7NBHw,2875
|
30
30
|
unfold/contrib/guardian/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
31
31
|
unfold/contrib/guardian/apps.py,sha256=ObJqwh4vHxkD4XfduP5IQAiYiWZxsXUOUqF1_R1GsRI,136
|
32
32
|
unfold/contrib/guardian/templates/admin/guardian/model/change_form.html,sha256=FSJc4MYYWyzZAy8Ay0b7Ov-cUo-oELHOM5fQehM54Lg,403
|
@@ -38,13 +38,14 @@ unfold/contrib/guardian/templates/unfold/guardian/group_form.html,sha256=P8WMC5E
|
|
38
38
|
unfold/contrib/guardian/templates/unfold/guardian/user_form.html,sha256=ci7FRrhTEKbFKKxsJ-07_dWXBYz4mqXPoqu5HfqYLaM,4132
|
39
39
|
unfold/contrib/import_export/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
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=O8BBKJIdqLg3-uUFQyDV9-L6EoJ7EVSX3LJBP1zegGw,2037
|
42
42
|
unfold/contrib/import_export/templates/admin/import_export/base.html,sha256=loL2qcV-f8aAzkHss_I4IkwfgemVW2CjOu_aiBxdwX0,357
|
43
43
|
unfold/contrib/import_export/templates/admin/import_export/change_form.html,sha256=_YorgltKO0CRSaLBAp67XnGMUBWEP_XewsUff0hlrE4,446
|
44
|
+
unfold/contrib/import_export/templates/admin/import_export/change_list_export.html,sha256=vZb2XVoJrDSjM1msiZqkQPMLQGjuqRHuLNdt11hmbQs,164
|
44
45
|
unfold/contrib/import_export/templates/admin/import_export/change_list_export_item.html,sha256=pTDeqPKOlCPKH2dxMIfPnWuc2wVDzB7AzL73WbxSnRY,257
|
45
46
|
unfold/contrib/import_export/templates/admin/import_export/change_list_import_export.html,sha256=JdKd6P2Ot9Ou4yg4CywTauuE1UiTz_mRvDwlx3vj3LI,229
|
46
47
|
unfold/contrib/import_export/templates/admin/import_export/change_list_import_item.html,sha256=XUuRxnsx9YQbKvW-E_JGl_ha7kpTSGSoRefOTTizuX0,233
|
47
|
-
unfold/contrib/import_export/templates/admin/import_export/export.html,sha256=
|
48
|
+
unfold/contrib/import_export/templates/admin/import_export/export.html,sha256=NZ2bZ0yvskp_twIjX9LSROMmicXAU8dDXemxdII9_K0,3447
|
48
49
|
unfold/contrib/import_export/templates/admin/import_export/import.html,sha256=P54_f3s96PV87Bo-FCZfmsn9DkRXLOB36r7HYF6y7GM,2075
|
49
50
|
unfold/contrib/import_export/templates/admin/import_export/import_confirm.html,sha256=M-acK4XSLHuPFD_NJashGYvPPeJrJsC-3LMvHs3lRis,867
|
50
51
|
unfold/contrib/import_export/templates/admin/import_export/import_errors.html,sha256=0DmJvZs31u-E2Y53yySci86cTnG9aUnOzvfYrOo0lYA,1422
|
@@ -59,19 +60,19 @@ unfold/contrib/inlines/checks.py,sha256=8sdyBcxw0erqQvp9sHlpGgy0rXfum-cd2eQE0rXF
|
|
59
60
|
unfold/contrib/inlines/forms.py,sha256=R9OJvrbqNLlKvTxw97JjElCY4CQ3IyRIkjIJUN0gJ9k,1323
|
60
61
|
unfold/contrib/simple_history/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
61
62
|
unfold/contrib/simple_history/apps.py,sha256=eF_KVYb60CAnGgWk2Z1YKYGfgA3TJBMr229qI7e2pgU,153
|
62
|
-
unfold/contrib/simple_history/templates/simple_history/
|
63
|
-
unfold/contrib/simple_history/templates/simple_history/object_history.html,sha256=AZ6uQRr7wKxV_rys5hGTVGYtVS-Fp5eHIqiXYW8FB1c,847
|
63
|
+
unfold/contrib/simple_history/templates/simple_history/object_history.html,sha256=ioBkepWc9-PQe4Kfy32RKOt_zf4fntz5XUfFeV3yQVw,771
|
64
64
|
unfold/contrib/simple_history/templates/simple_history/object_history_form.html,sha256=MOL3Tw3Nk3Rnq1koRV7yeCev4CP06_4xqAIOQk1M7FU,2290
|
65
|
+
unfold/contrib/simple_history/templates/simple_history/object_history_list.html,sha256=-QAWguUzGtD08_LBBk00_5ZYUjnso-q6dLXSeALc0n4,7449
|
65
66
|
unfold/contrib/simple_history/templates/simple_history/submit_line.html,sha256=ns9CEkU4HwKHhhj8qj_9UXvzp0viGtD1tp93GV2WRCs,1703
|
66
|
-
unfold/dataclasses.py,sha256=
|
67
|
+
unfold/dataclasses.py,sha256=XssBT3nfeFO-oekKDWrX6abIyrIW1P8CPzzCv1TRYFM,266
|
67
68
|
unfold/decorators.py,sha256=6E4vPVwK0IQDAiDPg9pgyypRqciX_gR0jwITDcrSc8U,3367
|
68
69
|
unfold/exceptions.py,sha256=gcCj1ox61E137bk_0Cqy4YC3SttdPgB-fiJUqpmyHSE,43
|
69
|
-
unfold/fields.py,sha256=
|
70
|
+
unfold/fields.py,sha256=Zegz_og7aHpdvUdFlUcIYVPB9TjPGjybhR2QNuq1b7A,7422
|
70
71
|
unfold/forms.py,sha256=GXEm3CFwglyuEbGdVyEMJTB45Gs-_RvGGlXJEkPy2kw,3688
|
71
72
|
unfold/settings.py,sha256=--TdTSWdOA8TQGW4-vjJkjy_zEyd_kZwBr3BIuQ8hzI,1208
|
72
73
|
unfold/sites.py,sha256=Gy_i43j2nizW2g8-mas5icvtk-beKism_CznATW6Ia8,12586
|
73
74
|
unfold/static/unfold/css/simplebar.css,sha256=5LLaEM11pKi6JFCOLt4XKuZxTpT9rpdq_tNlaQytFlU,4647
|
74
|
-
unfold/static/unfold/css/styles.css,sha256=
|
75
|
+
unfold/static/unfold/css/styles.css,sha256=bj69qsSGIkN5MXx2_5uFa5DdrU8_9a1ZBY_ICVQtBCI,92189
|
75
76
|
unfold/static/unfold/fonts/inter/Inter-Bold.woff2,sha256=O88EyjAeRPE_QEyKBKpK5wf2epUOEu8wwjj5bnhCZqE,46552
|
76
77
|
unfold/static/unfold/fonts/inter/Inter-Medium.woff2,sha256=O88EyjAeRPE_QEyKBKpK5wf2epUOEu8wwjj5bnhCZqE,46552
|
77
78
|
unfold/static/unfold/fonts/inter/Inter-Regular.woff2,sha256=O88EyjAeRPE_QEyKBKpK5wf2epUOEu8wwjj5bnhCZqE,46552
|
@@ -85,7 +86,7 @@ unfold/static/unfold/js/app.js,sha256=CIitJoFqpeZYPw8icGVXYX9tVRUgqFxcPZ2WjWS8Yl
|
|
85
86
|
unfold/static/unfold/js/chart.js,sha256=22W6cFERR-CElMOKRgMMicueMVP0Vf7FBEBYH8Z8tCk,200633
|
86
87
|
unfold/static/unfold/js/htmx.js,sha256=XOLqvnZiyEx46EW9vaJTBUaaWg8CGVVfXJkVsUmJbpI,42820
|
87
88
|
unfold/static/unfold/js/simplebar.js,sha256=t-uG1FAD6ZoiMeN--wac0XRS7SxoDVG6zvRnGuEp7X8,27176
|
88
|
-
unfold/styles.css,sha256=
|
89
|
+
unfold/styles.css,sha256=RbIWn2OR51DzBgHobHuJKGOUCoKYI6WZsYFjtGjECoE,17835
|
89
90
|
unfold/templates/admin/actions.html,sha256=1tVlUpLoM72K2Ew4vQGcRwPjHuAtO5Jm4QdDsDLOq0I,2625
|
90
91
|
unfold/templates/admin/app_index.html,sha256=lVjMIFsspHQ09LGHKfdfg7TlqlL39AX5LbwoeoZjFhk,1335
|
91
92
|
unfold/templates/admin/app_list.html,sha256=krDzw2EXqqvIi8bJtPhJsNran9H7hwdhM6ZW_IRlDwQ,3038
|
@@ -107,12 +108,12 @@ unfold/templates/admin/filter.html,sha256=dkrFkei-EAlldIU8DrgvSChzWQuUOu6-LS_qlZ
|
|
107
108
|
unfold/templates/admin/includes/fieldset.html,sha256=lMVwBifFWKvLvHqZ6yjP6Xf6BJFzi-EOf5JHIxEHmRI,888
|
108
109
|
unfold/templates/admin/includes/object_delete_summary.html,sha256=Nv69SCzyJHFX14iJFfodxKM0IIpQegKZH0fvKB15QJI,468
|
109
110
|
unfold/templates/admin/index.html,sha256=pkGdKWdD3zzOvkRdELvdb15sleSpfl4eHPA14PAh7z0,684
|
110
|
-
unfold/templates/admin/login.html,sha256=
|
111
|
+
unfold/templates/admin/login.html,sha256=ePmGmS9MrqvR6_6hnRSYdW57XKmMIuVGTL9I6jCw9BM,3792
|
111
112
|
unfold/templates/admin/nav_sidebar.html,sha256=63lUhsHfrjZowplnOEq4BkGD9jlX0bVQw5VrAMJqf5M,1178
|
112
113
|
unfold/templates/admin/object_history.html,sha256=PsbhXFd_3SCB9YkSJeHESp2VqjNlHtUW26mRtaZ-MD0,5069
|
113
114
|
unfold/templates/admin/pagination.html,sha256=KWTPV7_hVSZ1374a-pqHXhnOueNQKu1UnSUYirrWtCk,1173
|
114
115
|
unfold/templates/admin/search_form.html,sha256=8fJlPYHQDCm4Je05wwdNJuJQR6ChLgghWmo-yHSybMs,1099
|
115
|
-
unfold/templates/admin/submit_line.html,sha256=
|
116
|
+
unfold/templates/admin/submit_line.html,sha256=X7xg5vgWfHFWJOuXOLshma_LWWI0H9GHDmMYD1JqfI4,4353
|
116
117
|
unfold/templates/auth/widgets/read_only_password_hash.html,sha256=Li9efo-3cFC5zj9im0SPfc62R4ZNVPQhs24H1U7xmD8,785
|
117
118
|
unfold/templates/registration/logged_out.html,sha256=E7RHtB6AGQwgUIiV7dwJ1DbdfNvEhzJARONnB6jRLhQ,1028
|
118
119
|
unfold/templates/registration/password_change_done.html,sha256=i1ZzfTwZHWNWoN9_xHZDdcgLdTOVbTFFD1HUSuG0LkY,1062
|
@@ -133,14 +134,15 @@ unfold/templates/unfold/helpers/actions_row.html,sha256=1xd39zx38NOoKuDuxAG7PHeu
|
|
133
134
|
unfold/templates/unfold/helpers/add_link.html,sha256=mIgpKrwqBO1oJ4cwPQWSX1oUHBwHJmy5-2TxUHf-1bo,808
|
134
135
|
unfold/templates/unfold/helpers/app_list.html,sha256=lFnW8p9DcZbI9t3_ee9JX9ERHA0NRL2V88zpzuG4jq8,4720
|
135
136
|
unfold/templates/unfold/helpers/app_list_default.html,sha256=vZkw1F7oHOKReNkdHRYjhuNdA1nNdvSD4wbDmf0bnsM,4102
|
136
|
-
unfold/templates/unfold/helpers/
|
137
|
+
unfold/templates/unfold/helpers/attrs.html,sha256=Mwpj72kuwYj8hOT3J2T8qx6f1r_4xwwaS1slHA-82jI,166
|
138
|
+
unfold/templates/unfold/helpers/boolean.html,sha256=6gXAMEJcNVB14je9uN32dxImXP3KMCiWPeKU5EAnawA,569
|
137
139
|
unfold/templates/unfold/helpers/breadcrumb_item.html,sha256=k_1j57UV0WtzFFlMKaewj4NLbR_DhXI6RzCHThblZLw,234
|
138
140
|
unfold/templates/unfold/helpers/display_header.html,sha256=HiuaIJ6Y8DSM99OFypLO_2uQadZIw7tSg1aJJpFEXkM,1161
|
139
141
|
unfold/templates/unfold/helpers/display_label.html,sha256=LS9DWzYjHkYLV27sZDwyXlg2sLJ0AlId9FbjnXpsbfg,317
|
140
142
|
unfold/templates/unfold/helpers/field.html,sha256=Ds-zUHkdyxamfUCVNhxvtM0XoJg9OCA0QcsLbLWv4oo,882
|
141
143
|
unfold/templates/unfold/helpers/field_readonly.html,sha256=O0gHEW46OWt1oUUk0lZiyR-mztWv_7IH6GpKRm2wUw4,235
|
142
|
-
unfold/templates/unfold/helpers/field_readonly_value.html,sha256=
|
143
|
-
unfold/templates/unfold/helpers/fieldset_row.html,sha256=
|
144
|
+
unfold/templates/unfold/helpers/field_readonly_value.html,sha256=8dhZAyIT1VrBLvGz9aP_7eqHFqmqMJdcQ-aIYgalpDI,437
|
145
|
+
unfold/templates/unfold/helpers/fieldset_row.html,sha256=XRaouqP-Z1nA_9Y9uSxIMjsJq_JIHj1SkpDFz1_xFM4,2820
|
144
146
|
unfold/templates/unfold/helpers/fieldsets_tabs.html,sha256=V3bgW75eozaBDty-xfciGafhCWq_Ba5HfQkk92yRc9A,1445
|
145
147
|
unfold/templates/unfold/helpers/form_errors.html,sha256=EwerIJptSCWXvtAJ1IZKfEn98qlShBIGavsTThbklAs,266
|
146
148
|
unfold/templates/unfold/helpers/form_label.html,sha256=SR4U6iK9w4oels6iGY_Da-yN4BbXQVN9zCDlBGGXcw8,310
|
@@ -152,11 +154,11 @@ unfold/templates/unfold/helpers/messages/error.html,sha256=mlHVdfpG2ycsKIDBJB9CZ
|
|
152
154
|
unfold/templates/unfold/helpers/messages/errornote.html,sha256=5ChxoVYkF4jI-cw72gVHh0CRxDnG9ME_U6UQOArax2k,404
|
153
155
|
unfold/templates/unfold/helpers/messages/info.html,sha256=js95Hm3CzqpP6_XJpnA2vG7qt8eyHmJJJcDF0e5PLtQ,143
|
154
156
|
unfold/templates/unfold/helpers/messages.html,sha256=axUkgdYQySOTKEuRBDkohXFf8dM1ioQt5m6iAR7Ls18,701
|
155
|
-
unfold/templates/unfold/helpers/navigation.html,sha256=
|
157
|
+
unfold/templates/unfold/helpers/navigation.html,sha256=NkyTYH4_NIAMMHmZFdIVCjTl3m3sy6xrBLIi0F_YZpw,1070
|
156
158
|
unfold/templates/unfold/helpers/pagination_current_item.html,sha256=4cZ2KLVcP0Y7xuGyXgexDQ07r94cgM5Gnmtv11dkRPQ,69
|
157
159
|
unfold/templates/unfold/helpers/pagination_ellipsis.html,sha256=Ar9Ntf2I_79mIVW5Hadwkn1Kx1Lj3d8eIXNXI1IIBfg,56
|
158
160
|
unfold/templates/unfold/helpers/search.html,sha256=T3JLlzEeHTEpX6qfjNQ0cQPW2rtVIOyE9quEyVHVXsA,1382
|
159
|
-
unfold/templates/unfold/helpers/search_results.html,sha256=
|
161
|
+
unfold/templates/unfold/helpers/search_results.html,sha256=N5CNMJSF91jbJS6OQP0nANlnwVeS9UmnuOJ8QLitl2c,774
|
160
162
|
unfold/templates/unfold/helpers/site_icon.html,sha256=RO-R5yRt6yOx41Z8dpDP4lzwMXFdz8Dp5j8vGUtHzh0,789
|
161
163
|
unfold/templates/unfold/helpers/site_logo.html,sha256=05tqXzHy--pluceRQ2jDUZCFX9DjPHdBqGaieUv9sXk,424
|
162
164
|
unfold/templates/unfold/helpers/submit.html,sha256=oSzq85LRLhdOlbFtFZFhYm6ucT95u6LunTeSTDClszQ,206
|
@@ -165,17 +167,18 @@ unfold/templates/unfold/helpers/tab_list.html,sha256=WMFSx0HEvylI_AOIPtFuWff1ePJ
|
|
165
167
|
unfold/templates/unfold/helpers/theme_switch.html,sha256=skkl6fYUnYLM7fAPivHxWjnOnuQSKa-7aDxh7I8dGIg,2266
|
166
168
|
unfold/templates/unfold/helpers/userlinks.html,sha256=qWjtBt9Q_tU8a874ii0Qqg8t_d-SSYBTB_3QZfNlx9g,634
|
167
169
|
unfold/templates/unfold/helpers/welcomemsg.html,sha256=noRysgSENef4_53pXaTiBCy2or6lQm1ZtmCQVODAB1c,1120
|
168
|
-
unfold/templates/unfold/layouts/
|
170
|
+
unfold/templates/unfold/layouts/base.html,sha256=bAXZDbyiyxNiE-49mqr7pHUFhC2mHZQzIDUY-js_yZ0,379
|
171
|
+
unfold/templates/unfold/layouts/base_simple.html,sha256=T37-ncKZBLNnOlsUQq90rGl3sLdRjFaxWCPG399pkbE,1179
|
169
172
|
unfold/templates/unfold/layouts/skeleton.html,sha256=iXrUiggVp36vmBIia5p32c-9Ruy0PxkFFQogDpvENbk,3419
|
170
173
|
unfold/templates/unfold/widgets/clearable_file_input.html,sha256=gAJsfyCnanOyeN4ypp1y7r76znvITV7FYTyWvPsmlDs,1882
|
171
|
-
unfold/templates/unfold/widgets/clearable_file_input_small.html,sha256=
|
174
|
+
unfold/templates/unfold/widgets/clearable_file_input_small.html,sha256=LPgiKuMAYPiEupFxdlwc1TozNRV1w9ekdRuv-5P4fgw,2531
|
172
175
|
unfold/templates/unfold/widgets/date.html,sha256=WXo2LG1v_gBZBSg-zocj7oujMKI0MWLYCIFfB04HMLQ,122
|
173
176
|
unfold/templates/unfold/widgets/foreign_key_raw_id.html,sha256=BgXi4ziywtkWmZuUye5bbJ6yeEoHDJB_2lkwXX8hc6c,1026
|
174
177
|
unfold/templates/unfold/widgets/radio.html,sha256=3WcmclQNg7R_pRjEHL1dHkGjAzWlWNYnhHkAirC4nuA,646
|
175
178
|
unfold/templates/unfold/widgets/radio_option.html,sha256=IZgPx-aWKJuxrSalJ3K50RFd1vwSpb9Qk0yZwfV78_A,368
|
176
179
|
unfold/templates/unfold/widgets/range.html,sha256=28FBtSUgUcG82vpk_I27Lbs5oWZOV_oMzVhx4wj3-Ik,262
|
177
180
|
unfold/templates/unfold/widgets/related_widget_wrapper.html,sha256=0I6wSu8z_sJPqmX6uZev4mQGIIM336d6kvHdHj36ny4,3831
|
178
|
-
unfold/templates/unfold/widgets/split_datetime.html,sha256=
|
181
|
+
unfold/templates/unfold/widgets/split_datetime.html,sha256=OCH0tSSHnQfdJaV0_D3qVB6UN4baGBALtxA3Hlh8a6U,1244
|
179
182
|
unfold/templates/unfold/widgets/split_datetime_vertical.html,sha256=xinCH4kkQ-yKUqcSI7-m-_UEzOEKWqvLTjUa3i-e8EM,881
|
180
183
|
unfold/templates/unfold/widgets/split_money.html,sha256=AFLUBmzGbY-RXgsfz7gaDxVRhplaIPhXjg_hWYo9xcY,352
|
181
184
|
unfold/templates/unfold/widgets/textarea.html,sha256=-ZLDGrtASero7L-J3VvTNq_KjPAZh_kLVw0Ow3awqXM,144
|
@@ -185,10 +188,10 @@ unfold/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
185
188
|
unfold/templatetags/unfold.py,sha256=HFe0GrTD4va0lLzsZhxqjEOONmehqOWdf5vulkxgfGU,6302
|
186
189
|
unfold/templatetags/unfold_list.py,sha256=5xAjQX0_JnVwDaj-wGkGqbjOAtp-a18koWIKj5VfBz0,13867
|
187
190
|
unfold/typing.py,sha256=1P8PWM2oeaceUJtA5j071RbKEBpHYaux441u7Hd6wv4,643
|
188
|
-
unfold/utils.py,sha256=
|
189
|
-
unfold/views.py,sha256=
|
190
|
-
unfold/widgets.py,sha256=
|
191
|
-
django_unfold-0.
|
192
|
-
django_unfold-0.
|
193
|
-
django_unfold-0.
|
194
|
-
django_unfold-0.
|
191
|
+
unfold/utils.py,sha256=zZdJE4FmwRd7p5a7sJiAoZjBOJitXJduOq7BulyppWM,4803
|
192
|
+
unfold/views.py,sha256=hQCyeeMa9kcJV1IZeeYqj8PGW7J4QWME8n-5n0UGmiU,1003
|
193
|
+
unfold/widgets.py,sha256=w9BnJ6LYaFJJG7eJGJ-NKwqSHh6iUExN4P94WRvZO_c,15575
|
194
|
+
django_unfold-0.28.1.dist-info/LICENSE.md,sha256=Ltk_quRyyvV3J5v3brtOqmibeZSw2Hrb8bY1W3ya0Ik,1077
|
195
|
+
django_unfold-0.28.1.dist-info/METADATA,sha256=twP1ylIumQTVb9QAlPFA-Rn-mgahDJbspHQifMo7zJ0,51430
|
196
|
+
django_unfold-0.28.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
197
|
+
django_unfold-0.28.1.dist-info/RECORD,,
|
unfold/admin.py
CHANGED
@@ -464,6 +464,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
464
464
|
method=method,
|
465
465
|
description=self._get_action_description(method, action),
|
466
466
|
path=self._get_action_url(method, action),
|
467
|
+
attrs=method.attrs if hasattr(method, "attrs") else None,
|
467
468
|
)
|
468
469
|
|
469
470
|
@staticmethod
|
unfold/contrib/forms/widgets.py
CHANGED
@@ -15,23 +15,37 @@ class ImportForm(BaseImportForm):
|
|
15
15
|
def __init__(self, *args, **kwargs):
|
16
16
|
super().__init__(*args, **kwargs)
|
17
17
|
|
18
|
-
self.fields["resource"].widget.attrs["class"] = " ".join(
|
19
|
-
|
20
|
-
|
18
|
+
self.fields["resource"].widget.attrs["class"] = " ".join(
|
19
|
+
[self.fields["resource"].widget.attrs.get("class", ""), *SELECT_CLASSES]
|
20
|
+
)
|
21
|
+
self.fields["import_file"].widget = UnfoldAdminFileFieldWidget(
|
22
|
+
attrs=self.fields["import_file"].widget.attrs
|
23
|
+
)
|
24
|
+
self.fields["format"].widget.attrs["class"] = " ".join(
|
25
|
+
[self.fields["format"].widget.attrs.get("class", ""), *SELECT_CLASSES]
|
26
|
+
)
|
21
27
|
|
22
28
|
|
23
29
|
class ExportForm(BaseExportForm):
|
24
30
|
def __init__(self, *args, **kwargs):
|
25
31
|
super().__init__(*args, **kwargs)
|
26
|
-
self.fields["resource"].widget.attrs["class"] = " ".join(
|
27
|
-
|
32
|
+
self.fields["resource"].widget.attrs["class"] = " ".join(
|
33
|
+
[self.fields["resource"].widget.attrs.get("class", ""), *SELECT_CLASSES]
|
34
|
+
)
|
35
|
+
self.fields["format"].widget.attrs["class"] = " ".join(
|
36
|
+
[self.fields["format"].widget.attrs.get("class", ""), *SELECT_CLASSES]
|
37
|
+
)
|
28
38
|
|
29
39
|
|
30
40
|
class SelectableFieldsExportForm(BaseSelectableFieldsExportForm):
|
31
41
|
def __init__(self, formats, resources, **kwargs):
|
32
42
|
super().__init__(formats, resources, **kwargs)
|
33
|
-
self.fields["resource"].widget.attrs["class"] = " ".join(
|
34
|
-
|
43
|
+
self.fields["resource"].widget.attrs["class"] = " ".join(
|
44
|
+
[self.fields["resource"].widget.attrs.get("class", ""), *SELECT_CLASSES]
|
45
|
+
)
|
46
|
+
self.fields["format"].widget.attrs["class"] = " ".join(
|
47
|
+
[self.fields["format"].widget.attrs.get("class", ""), *SELECT_CLASSES]
|
48
|
+
)
|
35
49
|
|
36
50
|
for _key, field in self.fields.items():
|
37
51
|
if isinstance(field, BooleanField):
|
@@ -54,7 +54,7 @@
|
|
54
54
|
|
55
55
|
<fieldset class="border border-gray-200 mb-4 rounded-md pt-3 px-3 shadow-sm dark:border-gray-800">
|
56
56
|
{% for field in form.visible_fields %}
|
57
|
-
<div {% if field.field.is_selectable_field %}class="selectable-field-export-row" resource-index="{{ field.field.resource_index }}"{%
|
57
|
+
<div {% if field.field.is_selectable_field %}class="selectable-field-export-row" resource-index="{{ field.field.resource_index }}"{% endif %}>
|
58
58
|
{% if field.field.initial_field %}
|
59
59
|
<p class="block font-medium mb-2 text-gray-900 text-sm dark:text-gray-200">
|
60
60
|
{% trans "This exporter will export the following fields" %}
|
@@ -1,8 +1,6 @@
|
|
1
1
|
{% extends "admin/object_history.html" %}
|
2
2
|
|
3
|
-
{% load
|
4
|
-
{% load url from simple_history_compat %}
|
5
|
-
{% load display_list from simple_history_admin_list %}
|
3
|
+
{% load i18n %}
|
6
4
|
|
7
5
|
{% block content %}
|
8
6
|
{% if not revert_disabled %}
|
@@ -11,8 +9,8 @@
|
|
11
9
|
</p>
|
12
10
|
{% endif %}
|
13
11
|
|
14
|
-
{% if
|
15
|
-
{%
|
12
|
+
{% if historical_records %}
|
13
|
+
{% include object_history_list_template %}
|
16
14
|
{% else %}
|
17
15
|
<p class="mb-3 px-3 py-3 rounded-md text-sm last:mb-8 bg-amber-100 text-amber-600 dark:bg-amber-600/20 dark:border-amber-600/10">
|
18
16
|
{% trans "This object doesn't have a change history." %}
|
@@ -31,40 +31,44 @@
|
|
31
31
|
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
32
32
|
{% trans 'Change reason' %}
|
33
33
|
</th>
|
34
|
+
|
35
|
+
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
36
|
+
{% trans 'Changes' %}
|
37
|
+
</th>
|
34
38
|
</tr>
|
35
39
|
</thead>
|
36
40
|
|
37
41
|
<tbody>
|
38
|
-
{% for
|
42
|
+
{% for record in historical_records %}
|
39
43
|
<tr class="block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
40
44
|
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-400 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Object' %}">
|
41
|
-
<a href="{% url opts|admin_urlname:'simple_history' object.pk
|
42
|
-
{{
|
45
|
+
<a href="{% url opts|admin_urlname:'simple_history' object.pk record.pk %}">
|
46
|
+
{{ record.history_object }}
|
43
47
|
</a>
|
44
48
|
</td>
|
45
49
|
|
46
50
|
{% for column in history_list_display %}
|
47
51
|
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-400 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans column %}">
|
48
|
-
{{
|
52
|
+
{{ record|getattribute:column }}
|
49
53
|
</th>
|
50
54
|
{% endfor %}
|
51
55
|
|
52
56
|
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-400 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Date/time' %}">
|
53
|
-
{{
|
57
|
+
{{ record.history_date }}
|
54
58
|
</td>
|
55
59
|
|
56
60
|
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-400 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Comment' %}">
|
57
|
-
{{
|
61
|
+
{{ record.get_history_type_display }}
|
58
62
|
</td>
|
59
63
|
|
60
64
|
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-400 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Changed by' %}">
|
61
|
-
{% if
|
62
|
-
{% url admin_user_view
|
65
|
+
{% if record.history_user %}
|
66
|
+
{% url admin_user_view record.history_user_id as admin_user_url %}
|
63
67
|
|
64
68
|
{% if admin_user_url %}
|
65
|
-
<a href="{{ admin_user_url }}">{{
|
69
|
+
<a href="{{ admin_user_url }}">{{ record.history_user }}</a>
|
66
70
|
{% else %}
|
67
|
-
{{
|
71
|
+
{{ record.history_user }}
|
68
72
|
{% endif %}
|
69
73
|
{% else %}
|
70
74
|
{% trans "None" %}
|
@@ -72,7 +76,41 @@
|
|
72
76
|
</td>
|
73
77
|
|
74
78
|
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-400 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Change reason' %}">
|
75
|
-
{{
|
79
|
+
{{ record.history_change_reason }}
|
80
|
+
</td>
|
81
|
+
|
82
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-400 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Changes' %}">
|
83
|
+
{% block history_delta_changes %}
|
84
|
+
{% if record.history_delta_changes %}
|
85
|
+
<ul>
|
86
|
+
{% for change in record.history_delta_changes %}
|
87
|
+
<li class="flex flex-row gap-2">
|
88
|
+
<strong class="font-medium text-gray-700 dark:text-gray-200">
|
89
|
+
{{ change.field }}:
|
90
|
+
</strong>
|
91
|
+
|
92
|
+
<div class="flex flex-row items-center gap-2">
|
93
|
+
{% if change.old %}
|
94
|
+
<span>
|
95
|
+
{{ change.old }}
|
96
|
+
</span>
|
97
|
+
{% endif %}
|
98
|
+
|
99
|
+
{% if change.old and change.new %}
|
100
|
+
<span class="align-text-top material-symbols-outlined md-18 text-gray-300 group-hover:text-gray-400 dark:text-gray-600">arrow_right_alt</span>
|
101
|
+
{% endif %}
|
102
|
+
|
103
|
+
{% if change.new %}
|
104
|
+
<span>
|
105
|
+
{{ change.new }}
|
106
|
+
</span>
|
107
|
+
{% endif %}
|
108
|
+
</div>
|
109
|
+
</li>
|
110
|
+
{% endfor %}
|
111
|
+
</ul>
|
112
|
+
{% endif %}
|
113
|
+
{% endblock %}
|
76
114
|
</td>
|
77
115
|
</tr>
|
78
116
|
{% endfor %}
|
unfold/dataclasses.py
CHANGED
unfold/fields.py
CHANGED
@@ -18,7 +18,7 @@ from django.utils.safestring import SafeText, mark_safe
|
|
18
18
|
from django.utils.text import capfirst
|
19
19
|
|
20
20
|
from .settings import get_config
|
21
|
-
from .utils import display_for_field
|
21
|
+
from .utils import display_for_field, prettify_json
|
22
22
|
from .widgets import CHECKBOX_LABEL_CLASSES, LABEL_CLASSES
|
23
23
|
|
24
24
|
|
@@ -138,6 +138,14 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
|
138
138
|
and value is not None
|
139
139
|
):
|
140
140
|
result_repr = self.get_admin_url(f.remote_field, value)
|
141
|
+
elif isinstance(f, models.JSONField):
|
142
|
+
formatted_output = prettify_json(value)
|
143
|
+
|
144
|
+
if formatted_output:
|
145
|
+
return formatted_output
|
146
|
+
|
147
|
+
result_repr = display_for_field(value, f, self.empty_value_display)
|
148
|
+
return conditional_escape(result_repr)
|
141
149
|
elif isinstance(f, models.URLField):
|
142
150
|
return format_html(
|
143
151
|
'<a href="{}" class="text-primary-600 underline whitespace-nowrap">{}</a>',
|