django-unfold 0.65.0__py3-none-any.whl → 0.67.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.
Files changed (33) hide show
  1. {django_unfold-0.65.0.dist-info → django_unfold-0.67.0.dist-info}/METADATA +4 -2
  2. {django_unfold-0.65.0.dist-info → django_unfold-0.67.0.dist-info}/RECORD +33 -31
  3. {django_unfold-0.65.0.dist-info → django_unfold-0.67.0.dist-info}/WHEEL +1 -1
  4. unfold/admin.py +1 -1
  5. unfold/contrib/constance/settings.py +1 -0
  6. unfold/contrib/filters/admin/numeric_filters.py +2 -0
  7. unfold/contrib/filters/forms.py +25 -4
  8. unfold/contrib/filters/static/unfold/filters/js/admin-numeric-filter.js +62 -28
  9. unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +2 -11
  10. unfold/contrib/location_field/templates/location_field/map_widget.html +1 -1
  11. unfold/fields.py +41 -0
  12. unfold/sites.py +43 -13
  13. unfold/static/unfold/css/styles.css +1 -1
  14. unfold/static/unfold/js/app.js +117 -14
  15. unfold/static/unfold/js/select2.init.js +2 -9
  16. unfold/styles.css +25 -15
  17. unfold/templates/admin/nav_sidebar.html +1 -1
  18. unfold/templates/unfold/components/button.html +1 -1
  19. unfold/templates/unfold/components/card.html +15 -6
  20. unfold/templates/unfold/components/layer.html +1 -0
  21. unfold/templates/unfold/components/progress.html +14 -8
  22. unfold/templates/unfold/components/tracker.html +1 -1
  23. unfold/templates/unfold/helpers/app_list.html +1 -1
  24. unfold/templates/unfold/helpers/boolean.html +1 -1
  25. unfold/templates/unfold/helpers/command.html +1 -1
  26. unfold/templates/unfold/helpers/command_results.html +31 -12
  27. unfold/templates/unfold/helpers/field_readonly_value.html +1 -1
  28. unfold/templates/unfold/helpers/field_readonly_value_file.html +18 -0
  29. unfold/templates/unfold/helpers/search.html +1 -0
  30. unfold/templates/unfold/widgets/clearable_file_input_small.html +1 -1
  31. unfold/templatetags/unfold.py +44 -7
  32. unfold/widgets.py +3 -3
  33. {django_unfold-0.65.0.dist-info → django_unfold-0.67.0.dist-info/licenses}/LICENSE.md +0 -0
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: django-unfold
3
- Version: 0.65.0
3
+ Version: 0.67.0
4
4
  Summary: Modern Django admin theme for seamless interface development
5
5
  License: MIT
6
+ License-File: LICENSE.md
6
7
  Keywords: django,admin,tailwind,theme
7
8
  Requires-Python: >=3.9
8
9
  Classifier: Environment :: Web Environment
@@ -17,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.10
17
18
  Classifier: Programming Language :: Python :: 3.11
18
19
  Classifier: Programming Language :: Python :: 3.12
19
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
20
22
  Requires-Dist: django (>=4.2)
21
23
  Project-URL: Homepage, https://unfoldadmin.com
22
24
  Project-URL: Repository, https://github.com/unfoldadmin/django-unfold
@@ -1,12 +1,12 @@
1
1
  unfold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- unfold/admin.py,sha256=5U_q83esGoCCkZJGOsNKRDFXlHTUbrk3tDWizOFgce4,7402
2
+ unfold/admin.py,sha256=UeAG6y53kX5P7esCzCcqNztQ9WCujtscrjCyULeapvQ,7422
3
3
  unfold/apps.py,sha256=SlBXPYrUd2uXn67qFbRvbXSUk3XFWrF4-5WELgDCvho,381
4
4
  unfold/checks.py,sha256=e5HEhbXRcu3FwZDT_d-IbimeVpjJ6jgCUPXWvsUjEw0,2601
5
5
  unfold/components.py,sha256=vqkQzseYUvLXDohmTVAlbKopALjyX4WA9yglvdfhqu4,1283
6
6
  unfold/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  unfold/contrib/constance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  unfold/contrib/constance/apps.py,sha256=636jGQ_C15fp5WRccmuSeZCZ4GOQ2PhXSEe4769uqrQ,139
9
- unfold/contrib/constance/settings.py,sha256=SuEMNoEHjCiC3TrdQirKP6Uw17nuTaReKOTGiFjgAR4,785
9
+ unfold/contrib/constance/settings.py,sha256=Jm5bFB8t02mKPDS67VZJuw0a6QpMBtfNwTZeofhudkc,816
10
10
  unfold/contrib/constance/templates/admin/constance/change_list.html,sha256=qIt5tQBT6i5f3duGMcMmUvrzcQzaUtZ5ZfcPh61VeWg,2342
11
11
  unfold/contrib/constance/templates/admin/constance/includes/results_list.html,sha256=F65allHYz2QNzvR5sdwabjZBwXMkfTuofHWjhO98X2I,3781
12
12
  unfold/contrib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -16,14 +16,14 @@ unfold/contrib/filters/admin/choice_filters.py,sha256=wI_m5k8SLwHkWoFeCPvI0bN_Ru
16
16
  unfold/contrib/filters/admin/datetime_filters.py,sha256=cVari_7jsfeYxJoRBhcAUOr6v9XdtDtMuMs-S1Irafo,7757
17
17
  unfold/contrib/filters/admin/dropdown_filters.py,sha256=exiIyU6c_as7XBJsaAPPx5b8eY3IDbuCFGPJu1b0ux4,3856
18
18
  unfold/contrib/filters/admin/mixins.py,sha256=Z8BrdqbnjM1Ldr7qfhb6aWeOXKc5NqUgp-1NDU7SqS4,5839
19
- unfold/contrib/filters/admin/numeric_filters.py,sha256=8GNmesgMmMIfEZb2IrGbaBoJuvB2FpIc7wPIPBpHat4,6420
19
+ unfold/contrib/filters/admin/numeric_filters.py,sha256=V2yKPngFQ-PjpaE-VG2CK4jcJ-UZylEhtgSXRGx_MxQ,6490
20
20
  unfold/contrib/filters/admin/text_filters.py,sha256=19cOEGpSaXafcM8QuGiYaWmNi5UIQnCXEhKG-vCTfe8,2088
21
21
  unfold/contrib/filters/apps.py,sha256=wEySJy0gMLzFLb9XNKE-RexiO05X7NaQ5QmxZyziJ_k,136
22
- unfold/contrib/filters/forms.py,sha256=xROYON0ZaTLOE7gIhHXEgDAcYz0eY6LO2uxc3oXzfcA,8097
22
+ unfold/contrib/filters/forms.py,sha256=cebAmMPKtofdwvUhAVaoP5V3rNhwYe0Df5k06TJwZfo,8560
23
23
  unfold/contrib/filters/static/unfold/filters/css/nouislider/LICENSE,sha256=DR_G6Ykr-xlGx4DwQFeKTRcGAYL3ch33e44GisGdHOQ,1069
24
24
  unfold/contrib/filters/static/unfold/filters/css/nouislider/nouislider.min.css,sha256=rddL_jOGGVEY6wR-aw0VYovAfz5fPeAIsulrlSNb1hc,4221
25
25
  unfold/contrib/filters/static/unfold/filters/js/DateTimeShortcuts.js,sha256=jgFNBDf6aHvUlyv0LEDHggXO-xA8pWOCWFWcVupdA30,19332
26
- unfold/contrib/filters/static/unfold/filters/js/admin-numeric-filter.js,sha256=nTkiiJk4Abn9d6KigxPSEsereFhFq-v5n_boiiY1eII,1668
26
+ unfold/contrib/filters/static/unfold/filters/js/admin-numeric-filter.js,sha256=TnLzgMRzcePLHSMpvwCEA8CowifobGuCIS6IXRAU5XM,2363
27
27
  unfold/contrib/filters/static/unfold/filters/js/nouislider/LICENSE,sha256=DR_G6Ykr-xlGx4DwQFeKTRcGAYL3ch33e44GisGdHOQ,1069
28
28
  unfold/contrib/filters/static/unfold/filters/js/nouislider/nouislider.min.js,sha256=aIEt5UlLNnEv4-HPyxcLqu9dTS7gXiMm35Mm0rFmQ0Q,26683
29
29
  unfold/contrib/filters/static/unfold/filters/js/wnumb/LICENSE,sha256=ifeAa0-l35lCSesni0e-3xcD2BXwP7GXG6TdQiMW16I,1069
@@ -33,7 +33,7 @@ unfold/contrib/filters/templates/unfold/filters/filters_datetime_range.html,sha2
33
33
  unfold/contrib/filters/templates/unfold/filters/filters_field.html,sha256=UTlSZlpg-gAc_a-EJLLF0NI_ofuSHQ2kMMoAs99nL2E,164
34
34
  unfold/contrib/filters/templates/unfold/filters/filters_numeric_range.html,sha256=TnklQxXhe17UaImoiHahhtN6d1xVdXt2dhIMQESJY68,661
35
35
  unfold/contrib/filters/templates/unfold/filters/filters_numeric_single.html,sha256=uELVrDzFgmzGwkm37SpZeXXbfpijDnSBIJVtf6evWjM,598
36
- unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html,sha256=ihRdegeKkP2R3XQ_Z3_wujkjnc7ZByGqhfuDw4zjtNU,1880
36
+ unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html,sha256=82H0q9Bta_ng4k6BYpV5ECspmBwqAUXnVGKdrEOLc18,1165
37
37
  unfold/contrib/forms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  unfold/contrib/forms/apps.py,sha256=Di0TMzVuRpVxLG-8Bjdq5ALCSf5r7u2xVhD0jU6H5Sc,132
39
39
  unfold/contrib/forms/static/unfold/forms/css/trix/LICENSE,sha256=-g0UVDdcvHcBvBPZFsOuceYTuK5xgyFkHmeOCUCTk8Q,1053
@@ -79,7 +79,7 @@ unfold/contrib/inlines/checks.py,sha256=0vkHUh-MtNSTrcOjCLbZmtI4961fFOygOv_dBAps
79
79
  unfold/contrib/inlines/forms.py,sha256=noZdrBgvsu9tCBbRW3f9p7o6xKSuXtWR14qbZPdZBPA,1395
80
80
  unfold/contrib/location_field/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
81
  unfold/contrib/location_field/apps.py,sha256=qbNLfWXWf9q_gZxnuwFNVj20liSk_OT29uWMAi1she8,150
82
- unfold/contrib/location_field/templates/location_field/map_widget.html,sha256=HXNqZ7TRh8zxq7jxGv5V5De7jdB_L6smXSWX_KXujJs,179
82
+ unfold/contrib/location_field/templates/location_field/map_widget.html,sha256=vIBWNxIiCZipMnnPkZBFEZa-F3qXTKz9t4DbMmiBSSM,184
83
83
  unfold/contrib/simple_history/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
84
  unfold/contrib/simple_history/apps.py,sha256=eF_KVYb60CAnGgWk2Z1YKYGfgA3TJBMr229qI7e2pgU,153
85
85
  unfold/contrib/simple_history/templates/simple_history/object_history.html,sha256=unZQE70be7cK6v0YPlITFeaosmiLt3kiei-pmmCjBg0,1711
@@ -90,7 +90,7 @@ unfold/dataclasses.py,sha256=XDQp7fcvto7YC8A68tc6TUhXY7s3je9nm3AVUdxrJ7Y,859
90
90
  unfold/decorators.py,sha256=jPiYFy1p0WGFn68VZhAZkYCPDF7argb9SDFaH6aemHk,4922
91
91
  unfold/enums.py,sha256=9sTfyEycMWeoGPBa-ft6ZhQGnW1vkFg108RMBVf9nko,187
92
92
  unfold/exceptions.py,sha256=gcCj1ox61E137bk_0Cqy4YC3SttdPgB-fiJUqpmyHSE,43
93
- unfold/fields.py,sha256=wvkdeMCIYs3vNB_QDtRo191tXoYVrP-uRFfzFCTYY3c,7314
93
+ unfold/fields.py,sha256=qdnwYbl2NXbTdv1S_nct08UBWJTvoQ2hIrnzNxahJfg,8378
94
94
  unfold/forms.py,sha256=0u4TFw7oQ96S7njzJ2G79Qzt1X-4P4px-kfMgjVt49o,7762
95
95
  unfold/layout.py,sha256=Q-dFTA6UlvvPCA4PsGyTkHQmBLO7PL56xBfZi-bdt0c,1459
96
96
  unfold/mixins/__init__.py,sha256=YlhoDs9Y_DXQ8Ejr3enxuHMS1-Ij9fwukxNwpeOS4Co,190
@@ -100,12 +100,12 @@ unfold/overrides.py,sha256=ERVY9boZiXgjDYcTEZcIp7KWoeBZ45VM_zN31yAOjyQ,2922
100
100
  unfold/paginator.py,sha256=bZiEY-1kqZy_6CYGs9fHo6WntMgpnFCH-LJPoXkEESk,533
101
101
  unfold/sections.py,sha256=Lcg3xCZ76fqjm4vD4x7YdE5VsbTKluL-sX5QTEBlHxA,2379
102
102
  unfold/settings.py,sha256=o_yjhM1OgW2KSrae4oeUqjLw3lI9HZ1lkKGUW4nuC4Q,3432
103
- unfold/sites.py,sha256=wLkyX5YZ3xPL4z6_G24OzB1SpnwyVfFJ43pNbkuCH2U,20272
103
+ unfold/sites.py,sha256=NIO2vjCmSabI4aWZmpEbjgiSv4m3q0qKPoQjaAdxwIA,21380
104
104
  unfold/static/admin/js/admin/RelatedObjectLookups.js,sha256=ubKKhWmX_QborwPnaLHuQshTwQw6tK1pAViWdKIjw4E,9222
105
105
  unfold/static/admin/js/inlines.js,sha256=UXtFzCWHZWIJbxRghlNsNsz8HuRHjcaH1Mvb5e6uJms,16222
106
106
  unfold/static/unfold/css/simplebar/LICENSE,sha256=okNJf6UFl2NrEiC4iqORNBa16IKgUgV88acdcl9FbtY,1081
107
107
  unfold/static/unfold/css/simplebar/simplebar.css,sha256=5LLaEM11pKi6JFCOLt4XKuZxTpT9rpdq_tNlaQytFlU,4647
108
- unfold/static/unfold/css/styles.css,sha256=KrvoWomHoeiubyHqvKFVKGsSaQHMs5Sku5bgq2YFp8M,159921
108
+ unfold/static/unfold/css/styles.css,sha256=4JvNtrvUk6tlpGSyQpnbxCCa3YPMmyKnSqEDUVT0-Po,211751
109
109
  unfold/static/unfold/fonts/inter/Inter-Bold.woff2,sha256=-oiBJ7baAVtlVp8DUfO1w5GtkokElR8cIOn4RiqNleo,114840
110
110
  unfold/static/unfold/fonts/inter/Inter-Medium.woff2,sha256=D_PpRhThST61VjFP0keubEqFp3g7TMhr5TmUDPg_Kkg,114348
111
111
  unfold/static/unfold/fonts/inter/Inter-Regular.woff2,sha256=4G9rG8VTqupORmgCPtCrChRxKcMQf1Ebx9A9NhsK4IU,111268
@@ -121,15 +121,15 @@ unfold/static/unfold/js/alpine/alpine.js,sha256=PtHu0lJIiSHfZeNj1nFd6wTX-Squ255S
121
121
  unfold/static/unfold/js/alpine/alpine.persist.js,sha256=jFBwr6faTqqhp3sVi4_VTxJ0FpaF9YGZN1ZGLl_5QYM,837
122
122
  unfold/static/unfold/js/alpine/alpine.resize.js,sha256=mXR2L6neT584cfNLY5_imDq5DKjW1noXys7fhb1yxY4,703
123
123
  unfold/static/unfold/js/alpine/alpine.sort.js,sha256=q6LxgConwu8M8R03mZSBcsKwJv6fF7oomP8sqLxVQ0w,37891
124
- unfold/static/unfold/js/app.js,sha256=F7xblFopfjmdEAttR0ShM8yzE2g_mGWt1mPuh8e-Cj4,13454
124
+ unfold/static/unfold/js/app.js,sha256=r04hhe9FYimSvd_SLbP6AwgjaxE2HioFG3YLYaFH4YU,16282
125
125
  unfold/static/unfold/js/chart/LICENSE,sha256=QahKosq6ZF-WahjZwgVrc-bTqB2AvABGvAARomNNTM4,1093
126
126
  unfold/static/unfold/js/chart/chart.js,sha256=22W6cFERR-CElMOKRgMMicueMVP0Vf7FBEBYH8Z8tCk,200633
127
127
  unfold/static/unfold/js/htmx/LICENSE,sha256=09JFb3ZBTyRWEEZg69Za_xwEzXlmuUK9q9Y_PNsxajg,642
128
128
  unfold/static/unfold/js/htmx/htmx.js,sha256=acrv0NqSJpBm5yXX_hdeJrnVDJYuMFZFnAxHcVTNudM,50918
129
- unfold/static/unfold/js/select2.init.js,sha256=E1BhZ_a16_wxMb3zFq346HIrzvfQz8PRSB_o5CH_i2E,1196
129
+ unfold/static/unfold/js/select2.init.js,sha256=SeUUXgwLi26Tmh1Sgl3al4WeqlvmbZgixmTtwhJMMq0,1009
130
130
  unfold/static/unfold/js/simplebar/LICENSE,sha256=okNJf6UFl2NrEiC4iqORNBa16IKgUgV88acdcl9FbtY,1081
131
131
  unfold/static/unfold/js/simplebar/simplebar.js,sha256=t-uG1FAD6ZoiMeN--wac0XRS7SxoDVG6zvRnGuEp7X8,27176
132
- unfold/styles.css,sha256=c62G1eNFzth-br50OZIl8vBMOWK_6myffjHt1JFj08I,21231
132
+ unfold/styles.css,sha256=6-pbdH8mHbV60EZ8RUR_gxzI6IN4-CaYbKZuzEslX7A,21608
133
133
  unfold/templates/admin/actions.html,sha256=iuuubcAILkOoYBgV6cpXqFIyZUh4zMqyFp0JaDFlPKU,3096
134
134
  unfold/templates/admin/app_index.html,sha256=hcXcaEwqKYTaClnGzlBEsTzy3YJn_37AiFs4YVbvX8A,585
135
135
  unfold/templates/admin/app_list.html,sha256=-DEZKFdmkdTq3rix_cgZurPZ1r8PHg5pBL3i7ZtzVdI,2972
@@ -152,7 +152,7 @@ unfold/templates/admin/includes/fieldset.html,sha256=UWcatsmHzfdcjZP58P94YL7BI3F
152
152
  unfold/templates/admin/includes/object_delete_summary.html,sha256=FgwfapZCKWtnFtTHqh1MRuxxLot4bAw92WCcR_V8ImI,444
153
153
  unfold/templates/admin/index.html,sha256=n9uEm32hjM3g5d2hkjRlgwcYcAcmVw6nBVS__h8BIRU,539
154
154
  unfold/templates/admin/login.html,sha256=E963dlzjZlkNc6WPJg9DiqijWAhRp1QLVCxFHkRca3g,2468
155
- unfold/templates/admin/nav_sidebar.html,sha256=xvCaXOamXawmf80NhE0OwjjjuNwoyoePS--lsdqDeIE,772
155
+ unfold/templates/admin/nav_sidebar.html,sha256=NiNcLF-0eZwwv2arsikChzwbH7FTI1vEch427KddFlk,772
156
156
  unfold/templates/admin/object_history.html,sha256=4S9xpBtywUdFPUmvAuH2lMOG6jxCFZ-rFZYnhbajkRw,3726
157
157
  unfold/templates/admin/pagination.html,sha256=-9kjc_lKEeqvDVHMrnTfnlXDxMSkM8GNRGZGuI9D8os,912
158
158
  unfold/templates/admin/search_form.html,sha256=7ckco8llu5paptroZgr3jZw54DtowX-NiwFvxsCV898,1713
@@ -161,35 +161,36 @@ unfold/templates/auth/widgets/read_only_password_hash.html,sha256=MlaNXJ2PLEbv8N
161
161
  unfold/templates/registration/logged_out.html,sha256=ytjIz3abnpERwUp1F8zn89HyrimSrb9wgUna3Iz0CJE,1202
162
162
  unfold/templates/registration/password_change_done.html,sha256=TjzRQ476BNkz3To2S7MhEXxA-zoqCI8Op_FjY0cRod0,364
163
163
  unfold/templates/registration/password_change_form.html,sha256=E9fRkSh7n5N7UQIy0oWpcvI1mVPQ0nYGlBrM_WTi9kw,1517
164
- unfold/templates/unfold/components/button.html,sha256=RlNNIa_JmcsNFH151ECHkbB2TxpgeCPRh-eW1Bc0Xtk,933
165
- unfold/templates/unfold/components/card.html,sha256=fwko37u8KJIDQwiD2v8RoVuvZu8qBPyPl2TambOcFnc,1349
164
+ unfold/templates/unfold/components/button.html,sha256=oJEamHBxDIjoQ1xqWuJWDMY5ItUo8jpkZrqFp_cCH5U,933
165
+ unfold/templates/unfold/components/card.html,sha256=inSlz0IbriTQwA-I9sAa8A15NAfjqOWRka28vB-Eu_Q,1565
166
166
  unfold/templates/unfold/components/chart/bar.html,sha256=nDdDCUXvFd793OpNgNPQo0vBmvYngMsQyPHVTf1vfOw,297
167
167
  unfold/templates/unfold/components/chart/cohort.html,sha256=L00WR-igYv0C3t2yT_nZqeI-M5gVHpoaGv5RooRojqU,2402
168
168
  unfold/templates/unfold/components/chart/line.html,sha256=DHaZUFTlCs5cX7ioYdTzoN4ITHWNcEoNSUp7v3hlo_E,298
169
169
  unfold/templates/unfold/components/container.html,sha256=WBk1rGHnTsObi3pJCpL3dfFAUQ2abDmokU0Lee7R9CI,83
170
170
  unfold/templates/unfold/components/flex.html,sha256=_hBsQLJ6-7MfEfXNp5Wy-ykNtPMnnNg_io2IMGegn3s,134
171
171
  unfold/templates/unfold/components/icon.html,sha256=Qlhcf41FOwNVJ-nvA0RJ_Dn6JW6PUFXO6tOR3Xtf6e8,99
172
+ unfold/templates/unfold/components/layer.html,sha256=c2EY2rr64rJ5BPj97UdFAJTNWAommGoYL9J8gmjzEPE,15
172
173
  unfold/templates/unfold/components/navigation.html,sha256=0imW9pQs9kUZy2iROeAsGJDXC5tMRq9ABRcN85fJPeQ,866
173
- unfold/templates/unfold/components/progress.html,sha256=5D9z7PDo68jI_33e-MVPM0V3tywswn5fWc_x39XKQ_4,972
174
+ unfold/templates/unfold/components/progress.html,sha256=voyLSflK08x7SP9mdsEixAsGiZj7YVfnZlfqntdw8ok,1480
174
175
  unfold/templates/unfold/components/separator.html,sha256=yvHywT_MFkx5dNC2K53OFHRGxLDsEjRpptujXyGeoTY,168
175
176
  unfold/templates/unfold/components/table.html,sha256=5Ghmaw3mHhKOlBKKRBfhmSx0-HkRx1sHpX6NDItgdv4,7911
176
177
  unfold/templates/unfold/components/text.html,sha256=-GjxvdiaBQIaNfPSzT6SSIwnc3R27FkSDQMoF3FDPso,102
177
178
  unfold/templates/unfold/components/title.html,sha256=aMYSO30ybFYqPnqwlJVh2uNmcgnVrRycZkHTjJTjpX8,171
178
- unfold/templates/unfold/components/tracker.html,sha256=krlvsb06lpfdOIANsyrBOoLq1sgv6rXUGr-7MmP5Le0,292
179
+ unfold/templates/unfold/components/tracker.html,sha256=V3gOYUrjZPFpwMPGGrocgXTpE-PxqDhIlpomvdWO42g,329
179
180
  unfold/templates/unfold/helpers/account_links.html,sha256=Tq30bAkOU8QRR6CaB1CvTfTgkoPP5dz2v0OHYq0pRSE,2348
180
181
  unfold/templates/unfold/helpers/actions_row.html,sha256=RqhZsxgIFR-r1casv5CueL81uxNl8rA3XuCMDP--zLA,2586
181
182
  unfold/templates/unfold/helpers/add_link.html,sha256=vd-oy-xMIDiVmFVvwDL3-S360hOOmyAdfqcOTdDU1hI,870
182
- unfold/templates/unfold/helpers/app_list.html,sha256=baydU9R_xmH9pWgs8B7PUcwTWcLxHDhD1ehs75c5PD0,5879
183
+ unfold/templates/unfold/helpers/app_list.html,sha256=r1umyGnHwEamkdPCX3NJIs9xXKAw25gcq3n9JB4wL1M,5894
183
184
  unfold/templates/unfold/helpers/app_list_default.html,sha256=0TR2ZTy0KUqa6YR8IgihA_4RQqBIFOroYpRHHIyD4H8,4015
184
185
  unfold/templates/unfold/helpers/attrs.html,sha256=Mwpj72kuwYj8hOT3J2T8qx6f1r_4xwwaS1slHA-82jI,166
185
- unfold/templates/unfold/helpers/boolean.html,sha256=GyLsuvfp7SJSvwdN_wkruuL6yf3Rpb6zWl_WycTC8a8,811
186
+ unfold/templates/unfold/helpers/boolean.html,sha256=zK8xL-fv1rktvUQSYL6O-yGGjpr3w5TDBb3uyw1bF3o,812
186
187
  unfold/templates/unfold/helpers/breadcrumb_item.html,sha256=9T6ojN7LOXIlgwSwlIwHgy5U22eQYtarCqcmzsQIoPU,201
187
188
  unfold/templates/unfold/helpers/change_list_actions.html,sha256=xuFMZel6kH014H9sl0hKvQo7LP0UxwlhFQHIUeyQxVM,643
188
189
  unfold/templates/unfold/helpers/change_list_filter.html,sha256=WwU-AG4U5qZQ_sFs1z_jaaYBWqIv3ASmtKIQIhAp2gs,1908
189
190
  unfold/templates/unfold/helpers/change_list_filter_actions.html,sha256=K99hHgpCLaAHxv-eqIf4nOUfq6CeTQHekEeudKTRYME,2036
190
- unfold/templates/unfold/helpers/command.html,sha256=s5W_RZWz8kACMQzgf91sB3xFJES3OsQG5MoTPbj16f8,2826
191
+ unfold/templates/unfold/helpers/command.html,sha256=VDy7b6mlDL9kpt2VrCuUQwzjwUOPztUL3hrU2kp0tVs,2885
191
192
  unfold/templates/unfold/helpers/command_history.html,sha256=AB5elGPyY51aHuaWnwXddZmdcSadcqBOzdzjYqR4lfU,2866
192
- unfold/templates/unfold/helpers/command_results.html,sha256=nmdU8QEF68tYIOgt5pPmNPq4pQ_fCtNIOSPeunsadvs,2708
193
+ unfold/templates/unfold/helpers/command_results.html,sha256=3sYaBePPt9o_WDS9UeZojW-QZzbOGd474r2Elfn_33Q,3679
193
194
  unfold/templates/unfold/helpers/delete_submit_line.html,sha256=7SNS0axGytvUih9KxtOzy9ZC5rwG9u99iQxlnUZQfdE,700
194
195
  unfold/templates/unfold/helpers/display_dropdown.html,sha256=IpOy8-hww4arAVjPIRsHrip30J3IEd2p-2BzD3BjIaA,2906
195
196
  unfold/templates/unfold/helpers/display_header.html,sha256=2u-5Vc3mA6rS3ZlOWm3WRNLUX1wmZy4OSmGY2mdPZYs,1714
@@ -203,7 +204,8 @@ unfold/templates/unfold/helpers/edit_inline/tabular_title.html,sha256=2sk1TRzpmw
203
204
  unfold/templates/unfold/helpers/empty_results.html,sha256=zN1XM6_9_Oj-inMZlDxEnicYfBRr4HhwS9ODAnMiRYc,1947
204
205
  unfold/templates/unfold/helpers/field.html,sha256=KEs1SQRxzJ0x11zithlxVyv5zfQ9a_XGoJJ9AiWk1Vc,1038
205
206
  unfold/templates/unfold/helpers/field_readonly.html,sha256=4qtXjWE7SA2qZSTYLOv0lT4pQfD0TE34mvWQJY7jhS8,237
206
- unfold/templates/unfold/helpers/field_readonly_value.html,sha256=FI_CFEsCN7ELQux9gF-IaZ7nT7jyzidqwR2Med-E52g,504
207
+ unfold/templates/unfold/helpers/field_readonly_value.html,sha256=dQHLb3eWeTZnXVzT8vaCbFNb4H9NwwNAv3qOKpOfDtM,531
208
+ unfold/templates/unfold/helpers/field_readonly_value_file.html,sha256=8P-4BO6eH6VfxihDsBj8vFsiMZsloaeoiDYgP3CYUIY,709
207
209
  unfold/templates/unfold/helpers/fieldset_row.html,sha256=LriorPLXU4VmqstjX1oBc3T49H8CbhCrRxwQ7YfUSkI,1219
208
210
  unfold/templates/unfold/helpers/fieldset_row_checkbox.html,sha256=z4aRab--zJS2PC0jfKPez85IptKGRrqgkPpwW6_IKoo,1467
209
211
  unfold/templates/unfold/helpers/fieldset_row_field.html,sha256=cqLT1fHsCCszOHxvYlDFDTAd-6RRoOjdqfdBdhFgHOo,647
@@ -234,7 +236,7 @@ unfold/templates/unfold/helpers/pagination_ellipsis.html,sha256=8g0KUUKtqRkXx_EB
234
236
  unfold/templates/unfold/helpers/pagination_infinite.html,sha256=OPX_UliZvyN2hoCI2EYTFMSTZDVitTJdlSFZyv55i7U,633
235
237
  unfold/templates/unfold/helpers/pagination_inline.html,sha256=mMiqoYTmLjN2DFoen46EWHyP-b61staMG6WiaZn-YxI,1889
236
238
  unfold/templates/unfold/helpers/popup_header.html,sha256=IDR9B0OO6v6TvJggeof-OMGJ4cYK2INStU_2a1AlWfM,1325
237
- unfold/templates/unfold/helpers/search.html,sha256=Fpft3UmIH2EVI3_Jt66-ot42eTGj90cU5kns8XNHe5k,3070
239
+ unfold/templates/unfold/helpers/search.html,sha256=kZlsJxgdFK3otMv4TZCksNKT_dkq5DJc2g9oicKeKPs,3134
238
240
  unfold/templates/unfold/helpers/search_results.html,sha256=9FXx8fiByx0K2o8x2y8n0Wv-9kRUWk0y2PYu9raK1es,833
239
241
  unfold/templates/unfold/helpers/shortcut.html,sha256=K71_vp9qvY5gSsTgDP-9rivneSKAP5uWbfUnIpiwMBM,217
240
242
  unfold/templates/unfold/helpers/site_branding.html,sha256=_hhWQZqUrMYGqRJRpeANjbRJPfCROPZkQLZLV7FiRk8,268
@@ -257,7 +259,7 @@ unfold/templates/unfold/layouts/skeleton.html,sha256=hkCClXksW5IQVN95WPfmOa6h6iA
257
259
  unfold/templates/unfold/layouts/unauthenticated.html,sha256=b1yVJZThOw30SF580BTKzj5TJsIsR3lGBJ5jqtFsXuM,983
258
260
  unfold/templates/unfold/templatetags/preserve_changelist_filters.html,sha256=sx2jUhogNY4emBhY8PqxQ6mXifbXQb_IQI9Hd0boJI0,111
259
261
  unfold/templates/unfold/widgets/clearable_file_input.html,sha256=8dXZnc6z2D2K08hqHsQwR5UJffcIRnmYBT02zmCOr9A,2191
260
- unfold/templates/unfold/widgets/clearable_file_input_small.html,sha256=zhnaDz72mJgMrq-nCPCMuR5tEJaFVfJyOJUAzR6tQWc,2624
262
+ unfold/templates/unfold/widgets/clearable_file_input_small.html,sha256=SngF5SUR2Mk-eQSf0D30Stf-M2gFTY3PQK_EI27icvI,2624
261
263
  unfold/templates/unfold/widgets/date.html,sha256=ih9wj8koTaHb9s1NGUmyN9rHvSAmqCMbti9ONDOIujg,122
262
264
  unfold/templates/unfold/widgets/foreign_key_raw_id.html,sha256=4r7ha4I9vMp9Ls7WIbWxF1xuLGLWli7gIZoZAH3Bcyg,1114
263
265
  unfold/templates/unfold/widgets/radio.html,sha256=7Mgc92mMwJdVLjCxoGj5lo76KNaN5dKKktV3vzmbeqA,652
@@ -297,13 +299,13 @@ unfold/templates/unfold_crispy/uni_form.html,sha256=l9UNCUrIycgJ0p_B6qhtO_YxdARy
297
299
  unfold/templates/unfold_crispy/whole_uni_form.html,sha256=ufXRKCIGqkqKFOsh166rpKUnHpFMyBV4eYUCRhs3iws,475
298
300
  unfold/templates/unfold_crispy/whole_uni_formset.html,sha256=CYksfG2hRUdi5n691KW2ih-FfBxpOU4qVBfbOvcu1zs,873
299
301
  unfold/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
300
- unfold/templatetags/unfold.py,sha256=DNwNoNWIcOztQ7X9M50cV5Pg1gvmsP5miAOnE-EM6V0,22985
302
+ unfold/templatetags/unfold.py,sha256=zzIQyP1O8j1KfzYbCT0TW3gJROj3dZJOq5Y6MbwUe8I,24376
301
303
  unfold/templatetags/unfold_list.py,sha256=WWQSCM4QmQMr8YddA4krFztuuUXo2y28RA7jZfOiewE,14415
302
304
  unfold/typing.py,sha256=i7LM2LiwYTAjT5-OLDUPVn5b9X-DMmHnjlZG2toWwSE,692
303
305
  unfold/utils.py,sha256=UY7ZvkQ6KXkY0OkThppZ1k1HggyUhV_2bZok-HpFBDA,6827
304
306
  unfold/views.py,sha256=SA6mtv3aJD861-aL5VNreQUP7GRZH6hK_60MChjm2MM,1641
305
- unfold/widgets.py,sha256=FebXqilSMcK9vo5xTJdBuUv4B8L288khpWD5y4Srs08,24749
306
- django_unfold-0.65.0.dist-info/LICENSE.md,sha256=Ltk_quRyyvV3J5v3brtOqmibeZSw2Hrb8bY1W3ya0Ik,1077
307
- django_unfold-0.65.0.dist-info/METADATA,sha256=Trhzp9AVy_nZwszVAxS6VDDZwNw2m7f9cP_KbL-TvjY,9998
308
- django_unfold-0.65.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
309
- django_unfold-0.65.0.dist-info/RECORD,,
307
+ unfold/widgets.py,sha256=nu2oqcKhs3lhj9ueYfs3BqDzZT5raAE6jeAEgU4WrHE,24751
308
+ django_unfold-0.67.0.dist-info/METADATA,sha256=HaIyDsX13qf8nRgmZqiECi0mT_zloMaF5dTWpuNkURk,10074
309
+ django_unfold-0.67.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
310
+ django_unfold-0.67.0.dist-info/licenses/LICENSE.md,sha256=Ltk_quRyyvV3J5v3brtOqmibeZSw2Hrb8bY1W3ya0Ik,1077
311
+ django_unfold-0.67.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
unfold/admin.py CHANGED
@@ -192,7 +192,7 @@ class ModelAdmin(BaseModelAdminMixin, ActionModelAdminMixin, BaseModelAdmin):
192
192
  ) -> dict[str, Any]:
193
193
  formset_kwargs = super().get_formset_kwargs(request, obj, inline, prefix)
194
194
 
195
- if hasattr(inline, "per_page"):
195
+ if hasattr(inline, "per_page") and inline.per_page:
196
196
  formset_kwargs["request"] = request
197
197
  formset_kwargs["per_page"] = inline.per_page
198
198
 
@@ -15,6 +15,7 @@ UNFOLD_CONSTANCE_ADDITIONAL_FIELDS = {
15
15
  "django.forms.BooleanField",
16
16
  {
17
17
  "widget": "unfold.widgets.UnfoldBooleanSwitchWidget",
18
+ "required": False,
18
19
  },
19
20
  ],
20
21
  "file_field": [
@@ -179,6 +179,8 @@ class SliderNumericFilter(RangeNumericFilter):
179
179
  ),
180
180
  "form": self.form_class(
181
181
  name=self.parameter_name,
182
+ min=min_value,
183
+ max=max_value,
182
184
  data={
183
185
  self.parameter_name + "_from": self.used_parameters.get(
184
186
  self.parameter_name + "_from", min_value
@@ -59,7 +59,11 @@ class AutocompleteDropdownForm(forms.Form):
59
59
  label=label,
60
60
  required=False,
61
61
  queryset=field.remote_field.model.objects,
62
- widget=self.widget(field, model_admin.admin_site),
62
+ widget=self.widget(
63
+ field,
64
+ model_admin.admin_site,
65
+ attrs={"class": "unfold-filter-autocomplete"},
66
+ ),
63
67
  )
64
68
 
65
69
  class Media:
@@ -175,22 +179,39 @@ class SingleNumericForm(forms.Form):
175
179
 
176
180
 
177
181
  class RangeNumericForm(forms.Form):
178
- def __init__(self, name: str, *args, **kwargs) -> None:
182
+ def __init__(
183
+ self, name: str, min: float = None, max: float = None, *args, **kwargs
184
+ ) -> None:
179
185
  self.name = name
180
186
  super().__init__(*args, **kwargs)
181
187
 
188
+ min_max = {}
189
+
190
+ if min:
191
+ min_max["min"] = min
192
+ if max:
193
+ min_max["max"] = max
194
+
182
195
  self.fields[self.name + "_from"] = forms.FloatField(
183
196
  label="",
184
197
  required=False,
185
198
  widget=forms.NumberInput(
186
- attrs={"placeholder": _("From"), "class": " ".join(INPUT_CLASSES)}
199
+ attrs={
200
+ "placeholder": _("From"),
201
+ "class": " ".join(INPUT_CLASSES),
202
+ **min_max,
203
+ }
187
204
  ),
188
205
  )
189
206
  self.fields[self.name + "_to"] = forms.FloatField(
190
207
  label="",
191
208
  required=False,
192
209
  widget=forms.NumberInput(
193
- attrs={"placeholder": _("To"), "class": " ".join(INPUT_CLASSES)}
210
+ attrs={
211
+ "placeholder": _("To"),
212
+ "class": " ".join(INPUT_CLASSES),
213
+ **min_max,
214
+ }
194
215
  ),
195
216
  )
196
217
 
@@ -1,35 +1,69 @@
1
- document.addEventListener('DOMContentLoaded', function() {
2
- Array.from(document.getElementsByClassName('admin-numeric-filter-slider')).forEach(function(slider) {
3
- if (Array.from(slider.classList).includes("noUi-target")) {
4
- return;
5
- }
1
+ document.addEventListener("DOMContentLoaded", function () {
2
+ Array.from(
3
+ document.getElementsByClassName("admin-numeric-filter-slider")
4
+ ).forEach(function (slider) {
5
+ if (Array.from(slider.classList).includes("noUi-target")) {
6
+ return;
7
+ }
6
8
 
7
- var from = parseFloat(slider.closest('.admin-numeric-filter-wrapper').querySelectorAll('.admin-numeric-filter-wrapper-group input')[0].value);
8
- var to = parseFloat(slider.closest('.admin-numeric-filter-wrapper').querySelectorAll('.admin-numeric-filter-wrapper-group input')[1].value);
9
+ const fromInput = slider
10
+ .closest(".admin-numeric-filter-wrapper")
11
+ .querySelectorAll(".admin-numeric-filter-wrapper-group input")[0];
9
12
 
10
- noUiSlider.create(slider, {
11
- start: [from, to],
12
- step: parseFloat(slider.getAttribute('data-step')),
13
- connect: true,
14
- format: wNumb({
15
- decimals: parseFloat(slider.getAttribute('data-decimals'))
16
- }),
17
- range: {
18
- 'min': parseFloat(slider.getAttribute('data-min')),
19
- 'max': parseFloat(slider.getAttribute('data-max'))
20
- }
21
- });
13
+ const toInput = slider
14
+ .closest(".admin-numeric-filter-wrapper")
15
+ .querySelectorAll(".admin-numeric-filter-wrapper-group input")[1];
22
16
 
23
- slider.noUiSlider.on('update', function(values, handle) {
24
- var parent = this.target.closest('.admin-numeric-filter-wrapper');
25
- var from = parent.querySelectorAll('.admin-numeric-filter-wrapper-group input')[0];
26
- var to = parent.querySelectorAll('.admin-numeric-filter-wrapper-group input')[1];
17
+ noUiSlider.create(slider, {
18
+ start: [parseFloat(fromInput.value), parseFloat(toInput.value)],
19
+ step: parseFloat(slider.getAttribute("data-step")),
20
+ connect: true,
21
+ format: wNumb({
22
+ decimals: parseFloat(slider.getAttribute("data-decimals")),
23
+ }),
24
+ range: {
25
+ min: parseFloat(slider.getAttribute("data-min")),
26
+ max: parseFloat(slider.getAttribute("data-max")),
27
+ },
28
+ });
29
+
30
+ /*************************************************************
31
+ * Update slider when input values change
32
+ *************************************************************/
33
+ fromInput.addEventListener("keyup", function () {
34
+ clearTimeout(this._sliderUpdateTimeout);
35
+ this._sliderUpdateTimeout = setTimeout(() => {
36
+ slider.noUiSlider.set([
37
+ parseFloat(this.value),
38
+ parseFloat(toInput.value),
39
+ ]);
40
+ }, 500);
41
+ });
42
+
43
+ toInput.addEventListener("keyup", function () {
44
+ clearTimeout(this._sliderUpdateTimeout);
45
+ this._sliderUpdateTimeout = setTimeout(() => {
46
+ slider.noUiSlider.set([
47
+ parseFloat(fromInput.value),
48
+ parseFloat(this.value),
49
+ ]);
50
+ }, 500);
51
+ });
27
52
 
28
- parent.querySelectorAll('.admin-numeric-filter-slider-tooltip-from')[0].innerHTML = values[0];
29
- parent.querySelectorAll('.admin-numeric-filter-slider-tooltip-to')[0].innerHTML = values[1];
53
+ /*************************************************************
54
+ * Updated inputs when slider is moved
55
+ *************************************************************/
56
+ slider.noUiSlider.on("update", function (values, handle) {
57
+ const parent = this.target.closest(".admin-numeric-filter-wrapper");
58
+ const from = parent.querySelectorAll(
59
+ ".admin-numeric-filter-wrapper-group input"
60
+ )[0];
61
+ const to = parent.querySelectorAll(
62
+ ".admin-numeric-filter-wrapper-group input"
63
+ )[1];
30
64
 
31
- from.value = values[0];
32
- to.value = values[1];
33
- });
65
+ from.value = values[0];
66
+ to.value = values[1];
34
67
  });
68
+ });
35
69
  });
@@ -8,22 +8,13 @@
8
8
  </h3>
9
9
 
10
10
  {% if choice.min is not None and choice.max is not None and choice.step %}
11
- <div class="admin-numeric-filter-slider-tooltips">
12
- <span class="admin-numeric-filter-slider-tooltip-from border border-base-200 cursor-not-allowed flex grow flex-row items-center mr-auto rounded-default shadow-xs px-3 py-2 w-full dark:bg-base-900 dark:border-base-700 dark:text-font-default-dark">
13
- {{ choice.value_from }}
14
- </span>
15
11
 
16
- <span class="admin-numeric-filter-slider-tooltip-to border border-base-200 cursor-not-allowed flex grow flex-row items-center rounded-default shadow-xs px-3 py-2 w-full dark:bg-base-900 dark:border-base-700 dark:text-font-default-dark">
17
- {{ choice.value_to }}
18
- </span>
12
+ <div class="admin-numeric-filter-wrapper-group flex flex-row gap-4 mb-4">
13
+ {{ choice.form.as_p }}
19
14
  </div>
20
15
 
21
16
  <div class="admin-numeric-filter-slider" data-min="{{ choice.min|unlocalize }}" data-max="{{ choice.max|unlocalize }}" data-decimals="{{ choice.decimals }}" data-step="{{ choice.step|unlocalize }}">
22
17
  </div>
23
-
24
- <div class="admin-numeric-filter-wrapper-group hidden">
25
- {{ choice.form.as_p }}
26
- </div>
27
18
  {% else %}
28
19
  <div class="admin-numeric-filter-slider-error dark:text-font-default-dark">
29
20
  <p class="border border-base-300 border-dashed leading-[36px] h-[38px] rounded-default text-center dark:border-base-700">
@@ -1,5 +1,5 @@
1
1
  <div class="map-widget flex flex-col gap-2">
2
2
  {{ field_input }}
3
3
 
4
- <div id="map_{{ field_name }}" class="border gap-2 h-72 max-w-2xl rounded-default shadow-xs"></div>
4
+ <div id="map_{{ field_name }}" class="border gap-2 h-72 max-w-2xl rounded-default shadow-xs z-10"></div>
5
5
  </div>
unfold/fields.py CHANGED
@@ -1,8 +1,11 @@
1
+ from typing import Union
2
+
1
3
  from django.contrib.admin import helpers
2
4
  from django.contrib.admin.utils import lookup_field, quote
3
5
  from django.core.exceptions import ObjectDoesNotExist
4
6
  from django.db import models
5
7
  from django.db.models import (
8
+ FileField,
6
9
  ForeignObjectRel,
7
10
  ImageField,
8
11
  JSONField,
@@ -36,6 +39,30 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
36
39
 
37
40
  return format_html("<label{}>{}</label>", flatatt(attrs), capfirst(label))
38
41
 
42
+ @property
43
+ def url(self) -> Union[str, bool]:
44
+ field, obj, model_admin = (
45
+ self.field["field"],
46
+ self.form.instance,
47
+ self.model_admin,
48
+ )
49
+
50
+ try:
51
+ f, attr, value = lookup_field(field, obj, model_admin)
52
+ except (AttributeError, ValueError, ObjectDoesNotExist):
53
+ return False
54
+
55
+ if not self.is_file():
56
+ return False
57
+
58
+ if hasattr(obj, field):
59
+ field_value = getattr(obj, field)
60
+
61
+ if field_value and hasattr(field_value, "url"):
62
+ return field_value.url
63
+
64
+ return False
65
+
39
66
  def is_json(self) -> bool:
40
67
  field, obj, model_admin = (
41
68
  self.field["field"],
@@ -73,6 +100,20 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
73
100
 
74
101
  return isinstance(f, ImageField)
75
102
 
103
+ def is_file(self) -> bool:
104
+ field, obj, model_admin = (
105
+ self.field["field"],
106
+ self.form.instance,
107
+ self.model_admin,
108
+ )
109
+
110
+ try:
111
+ f, attr, value = lookup_field(field, obj, model_admin)
112
+ except (AttributeError, ValueError, ObjectDoesNotExist):
113
+ return False
114
+
115
+ return isinstance(f, (ImageField, FileField))
116
+
76
117
  def contents(self) -> str:
77
118
  contents = self._get_contents()
78
119
  contents = self._preprocess_field(contents)
unfold/sites.py CHANGED
@@ -6,6 +6,7 @@ from urllib.parse import parse_qs, urlparse
6
6
 
7
7
  from django.contrib.admin import AdminSite
8
8
  from django.core.cache import cache
9
+ from django.core.paginator import Paginator
9
10
  from django.core.validators import EMPTY_VALUES
10
11
  from django.http import HttpRequest, HttpResponse
11
12
  from django.template.response import TemplateResponse
@@ -209,12 +210,23 @@ class UnfoldAdminSite(AdminSite):
209
210
  return results
210
211
 
211
212
  def _search_models(
212
- self, request: HttpRequest, app_list: list[dict[str, Any]], search_term: str
213
+ self,
214
+ request: HttpRequest,
215
+ app_list: list[dict[str, Any]],
216
+ search_term: str,
217
+ allowed_models: Optional[list[str]] = None,
213
218
  ) -> list[SearchResult]:
214
219
  results = []
215
220
 
216
221
  for app in app_list:
217
222
  for model in app["models"]:
223
+ # Skip models which are not allowed
224
+ if isinstance(allowed_models, (list, tuple)):
225
+ if model["model"]._meta.label.lower() not in [
226
+ m.lower() for m in allowed_models
227
+ ]:
228
+ continue
229
+
218
230
  admin_instance = self._registry.get(model["model"])
219
231
  search_fields = admin_instance.get_search_fields(request)
220
232
 
@@ -255,7 +267,8 @@ class UnfoldAdminSite(AdminSite):
255
267
  ) -> TemplateResponse:
256
268
  start_time = time.time()
257
269
 
258
- CACHE_TIMEOUT = 10
270
+ CACHE_TIMEOUT = 5 * 60
271
+ PER_PAGE = 100
259
272
 
260
273
  search_term = request.GET.get("s")
261
274
  extended_search = "extended" in request.GET
@@ -276,33 +289,50 @@ class UnfoldAdminSite(AdminSite):
276
289
  results = cache_results
277
290
  else:
278
291
  results = self._search_apps(app_list, search_term)
279
- search_models = self._get_config("COMMAND", request).get("search_models")
280
- search_callback = self._get_config("COMMAND", request).get(
281
- "search_callback"
282
- )
283
292
 
284
293
  if extended_search:
285
- if search_callback:
294
+ if search_callback := self._get_config("COMMAND", request).get(
295
+ "search_callback"
296
+ ):
286
297
  results.extend(
287
298
  self._get_value(search_callback, request, search_term)
288
299
  )
289
300
 
290
- if search_models is True:
291
- results.extend(self._search_models(request, app_list, search_term))
301
+ search_models = self._get_value(
302
+ self._get_config("COMMAND", request).get("search_models"), request
303
+ )
304
+
305
+ if search_models is True or isinstance(search_models, (list, tuple)):
306
+ allowed_models = (
307
+ search_models
308
+ if isinstance(search_models, (list, tuple))
309
+ else None
310
+ )
311
+
312
+ results.extend(
313
+ self._search_models(
314
+ request, app_list, search_term, allowed_models
315
+ )
316
+ )
292
317
 
293
318
  cache.set(cache_key, results, timeout=CACHE_TIMEOUT)
294
319
 
295
320
  execution_time = time.time() - start_time
321
+ paginator = Paginator(results, PER_PAGE)
322
+
323
+ show_history = self._get_value(
324
+ self._get_config("COMMAND", request).get("show_history"), request
325
+ )
296
326
 
297
327
  return TemplateResponse(
298
328
  request,
299
329
  template=template_name,
300
330
  context={
301
- "results": results,
331
+ "page_obj": paginator,
332
+ "results": paginator.page(request.GET.get("page", 1)),
333
+ "page_counter": (int(request.GET.get("page", 1)) - 1) * PER_PAGE,
302
334
  "execution_time": execution_time,
303
- "command_show_history": self._get_config("COMMAND", request).get(
304
- "show_history"
305
- ),
335
+ "command_show_history": show_history,
306
336
  },
307
337
  headers={
308
338
  "HX-Trigger": "search",