django-unfold 0.54.0__py3-none-any.whl → 0.55.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 (39) hide show
  1. {django_unfold-0.54.0.dist-info → django_unfold-0.55.0.dist-info}/METADATA +24 -11
  2. {django_unfold-0.54.0.dist-info → django_unfold-0.55.0.dist-info}/RECORD +39 -17
  3. unfold/contrib/filters/admin/choice_filters.py +71 -32
  4. unfold/contrib/filters/admin/dropdown_filters.py +15 -1
  5. unfold/contrib/filters/admin/mixins.py +25 -0
  6. unfold/layout.py +23 -0
  7. unfold/sites.py +17 -6
  8. unfold/static/unfold/css/styles.css +1 -1
  9. unfold/static/unfold/fonts/material-symbols/LICENSE +202 -0
  10. unfold/static/unfold/fonts/material-symbols/Material-Symbols-Outlined.woff2 +0 -0
  11. unfold/static/unfold/js/alpine.js +2 -2
  12. unfold/static/unfold/js/select2.init.js +4 -0
  13. unfold/templates/unfold/components/button.html +1 -1
  14. unfold/templates/unfold/widgets/clearable_file_input.html +1 -1
  15. unfold/templates/unfold/widgets/clearable_file_input_small.html +1 -1
  16. unfold/templates/unfold_crispy/display_form.html +9 -0
  17. unfold/templates/unfold_crispy/errors.html +3 -0
  18. unfold/templates/unfold_crispy/field.html +23 -0
  19. unfold/templates/unfold_crispy/inputs.html +7 -0
  20. unfold/templates/unfold_crispy/layout/attrs.html +1 -0
  21. unfold/templates/unfold_crispy/layout/baseinput.html +9 -0
  22. unfold/templates/unfold_crispy/layout/button.html +1 -0
  23. unfold/templates/unfold_crispy/layout/buttonholder.html +3 -0
  24. unfold/templates/unfold_crispy/layout/checkbox.html +19 -0
  25. unfold/templates/unfold_crispy/layout/column.html +3 -0
  26. unfold/templates/unfold_crispy/layout/div.html +4 -0
  27. unfold/templates/unfold_crispy/layout/field_errors.html +7 -0
  28. unfold/templates/unfold_crispy/layout/fieldset.html +9 -0
  29. unfold/templates/unfold_crispy/layout/help_text.html +5 -0
  30. unfold/templates/unfold_crispy/layout/help_text_and_errors.html +3 -0
  31. unfold/templates/unfold_crispy/layout/radio_checkbox_select.html +21 -0
  32. unfold/templates/unfold_crispy/layout/row.html +3 -0
  33. unfold/templates/unfold_crispy/layout/table_inline_formset.html +100 -0
  34. unfold/templates/unfold_crispy/uni_form.html +11 -0
  35. unfold/templates/unfold_crispy/whole_uni_form.html +14 -0
  36. unfold/templatetags/unfold.py +15 -9
  37. unfold/widgets.py +86 -1
  38. {django_unfold-0.54.0.dist-info → django_unfold-0.55.0.dist-info}/LICENSE.md +0 -0
  39. {django_unfold-0.54.0.dist-info → django_unfold-0.55.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-unfold
3
- Version: 0.54.0
3
+ Version: 0.55.0
4
4
  Summary: Modern Django admin theme for seamless interface development
5
5
  License: MIT
6
6
  Keywords: django,admin,tailwind,theme
@@ -27,6 +27,7 @@ Description-Content-Type: text/markdown
27
27
  ## Unfold Django Admin Theme
28
28
 
29
29
  [![PyPI - Version](https://img.shields.io/pypi/v/django-unfold.svg?style=for-the-badge)](https://pypi.org/project/django-unfold/)
30
+ [![Discord](https://img.shields.io/discord/1297493955231088650?style=for-the-badge&logo=discord&logoColor=%23ffffff&color=7289da)](https://discord.gg/9sQj9MEbNz)
30
31
  [![Build](https://img.shields.io/github/actions/workflow/status/unfoldadmin/django-unfold/release.yml?style=for-the-badge)](https://github.com/unfoldadmin/django-unfold/actions?query=workflow%3Arelease)
31
32
  ![Pre Commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge)
32
33
  ![Code Style - Ruff](https://img.shields.io/badge/code%20style-ruff-30173D.svg?style=for-the-badge)
@@ -43,26 +44,31 @@ Transform your Django admin into a powerful, modern administrative interface wit
43
44
 
44
45
  Need assistance with implementing Unfold or require custom Django development? Professional consulting and development services are available to help transform your Django admin interface and enhance your web applications. Whether migrating from the native Django admin to Unfold or seeking expert technical guidance, reach out to discuss your project requirements and explore development possibilities. [Get in touch](https://unfoldadmin.com/consulting?utm_medium=github&utm_source=unfold) to learn more about available services.
45
46
 
47
+ ## Unfold Studio
48
+
49
+ Unfold Studio is a theme customizer for Django admin that helps you create a branded interface. You can customize colors, upload your logo, and adjust the style without coding. The tool is currently in development, and [you can join the waitlist](https://unfoldadmin.com/studio/) to be notified when it's available.
50
+
46
51
  ## Fresh Features & Enhancements
47
52
 
48
- - **Autocomplete filters:** Enhance filter functionality with built-in autocomplete support
53
+ - **Conditional fields:** Show or hide fields dynamically based on the values of other fields in the form
54
+ - **Infinite paginator:** Efficiently handle large datasets with seamless pagination that reduces server load
55
+ - **Checkbox & radio filters:** Enhanced filter options with checkbox and radio interfaces for intuitive filtering
49
56
  - **Site dropdown:** Configurable dropdown menu in the header area for managing custom navigation links
50
57
  - **Dropdown actions:** Organize action items in customizable dropdown menus
51
- - **Custom tab navigation:** Create flexible, structured tab navigation with customizable routing
52
-
53
58
 
54
59
  ## Core Features & Capabilities
55
60
 
56
61
  - **Visual Interface**: Provides a modern user interface based on the Tailwind CSS framework.
57
- - **Sidebar Navigation**: Simplifies the creation of sidebar menus with icons, collapsible sections, and more.
58
- - **Dark Mode Support**: Includes both light and dark mode themes.
59
- - **Flexible Actions**: Provides multiple ways to define actions throughout the admin interface.
60
- - **Advanced Filters**: Features custom dropdowns, autocomplete, numeric, datetime, and text field filters.
61
- - **Dashboard Tools**: Includes helpers for building custom dashboard pages.
62
- - **UI Components**: Offers reusable interface components such as cards, buttons, and charts.
63
- - **WYSIWYG Editor**: Built-in support for WYSIWYG editing through Trix.
62
+ - **Sidebar navigation**: Simplifies the creation of sidebar menus with icons, collapsible sections, and more.
63
+ - **Dark mode support**: Includes both light and dark mode themes.
64
+ - **Flexible actions**: Provides multiple ways to define actions throughout the admin interface.
65
+ - **Advanced filters**: Features custom dropdowns, autocomplete, numeric, datetime, and text field filters.
66
+ - **Dashboard tools**: Includes helpers for building custom dashboard pages.
67
+ - **UI components**: Offers reusable interface components such as cards, buttons, and charts.
68
+ - **WYSIWYG editor**: Built-in support for WYSIWYG editing through Trix.
64
69
  - **Array widget:** Built-in widget for `django.contrib.postgres.fields.ArrayField`
65
70
  - **Inline tabs:** Group inlines into tab navigation in the change form
71
+ - **Conditional fields:** Show or hide fields dynamically based on the values of other fields in the form
66
72
  - **Model tabs:** Allow defining custom tab navigation for models
67
73
  - **Fieldset tabs:** Merge multiple fieldsets into tabs in the change form
68
74
  - **Sortable inlines:** Allow sorting inlines by dragging and dropping
@@ -73,9 +79,16 @@ Need assistance with implementing Unfold or require custom Django development? P
73
79
  - **Font colors:** Adjust font colors for better readability
74
80
  - **Changeform modes:** Display fields in compressed mode in the change form
75
81
  - **Language switcher:** Allow changing language directly from the admin area
82
+ - **Infinite paginator:** Efficiently handle large datasets with seamless pagination that reduces server load
76
83
  - **Parallel admin:** Supports [running the default admin](https://unfoldadmin.com/blog/migrating-django-admin-unfold/?utm_medium=github&utm_source=unfold) alongside Unfold.
77
84
  - **Third-party packages:** Provides default support for multiple popular applications.
78
85
  - **Configuration:** Allows basic options to be changed in `settings.py`.
79
86
  - **Dependencies:** Built entirely on `django.contrib.admin`.
80
87
  - **VS Code:** Project configuration and development container included.
81
88
 
89
+ ## Credits
90
+
91
+ - Icons: [Material Symbols](https://github.com/google/material-design-icons)
92
+ - Font: [Inter](https://github.com/rsms/inter)
93
+ - Charts: [Chart.js](https://github.com/chartjs/Chart.js)
94
+
@@ -7,10 +7,10 @@ unfold/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  unfold/contrib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  unfold/contrib/filters/admin/__init__.py,sha256=7pooAoA2Y9MbBL_RTJBZeIAwV5ECrOvMbe0_egoHfew,1663
9
9
  unfold/contrib/filters/admin/autocomplete_filters.py,sha256=Jh3m3iQ8YubZRQDVzHPGfpk9RulOhHkZc0N7L6KV9wc,514
10
- unfold/contrib/filters/admin/choice_filters.py,sha256=pDBGb4uAbAKgZB3ChEVRy6us20JktRtdDfCjquPJKa4,4377
10
+ unfold/contrib/filters/admin/choice_filters.py,sha256=xptxmUM9FL4xZklBOBnFR0yNlzNAZgEt3xLBtMBgQko,5575
11
11
  unfold/contrib/filters/admin/datetime_filters.py,sha256=cVari_7jsfeYxJoRBhcAUOr6v9XdtDtMuMs-S1Irafo,7757
12
- unfold/contrib/filters/admin/dropdown_filters.py,sha256=aZhBTEjtbeaOdYZK4nYRmA38usy9rhVTM4L_08MGU0I,3360
13
- unfold/contrib/filters/admin/mixins.py,sha256=iOO89KpVmKig_4RPJem2TqIwZO-ROUjGTmoDmBkgyLk,4945
12
+ unfold/contrib/filters/admin/dropdown_filters.py,sha256=aBJeutavLRocbrverAUM6mx4_kCyDWKnjcx3JRVqXuc,3837
13
+ unfold/contrib/filters/admin/mixins.py,sha256=4SiJ1Qnp-REUXOhk80zq8uW0nc2i-r8aftvSaFrpPXw,5820
14
14
  unfold/contrib/filters/admin/numeric_filters.py,sha256=8GNmesgMmMIfEZb2IrGbaBoJuvB2FpIc7wPIPBpHat4,6420
15
15
  unfold/contrib/filters/admin/text_filters.py,sha256=19cOEGpSaXafcM8QuGiYaWmNi5UIQnCXEhKG-vCTfe8,2088
16
16
  unfold/contrib/filters/apps.py,sha256=wEySJy0gMLzFLb9XNKE-RexiO05X7NaQ5QmxZyziJ_k,136
@@ -78,6 +78,7 @@ unfold/enums.py,sha256=9sTfyEycMWeoGPBa-ft6ZhQGnW1vkFg108RMBVf9nko,187
78
78
  unfold/exceptions.py,sha256=gcCj1ox61E137bk_0Cqy4YC3SttdPgB-fiJUqpmyHSE,43
79
79
  unfold/fields.py,sha256=J3goIMlP_XOmQ85Xgh2v5MylXSP1o5I7o9_So2FQn2k,7461
80
80
  unfold/forms.py,sha256=3MrVG9jDdDsrJHph-cimZXRycfdoAw-KhT0uJJInG7E,4552
81
+ unfold/layout.py,sha256=idG4v0AumFPyy16KUlKtWrs9kyBQIKY_Ln26Lx9DOPg,501
81
82
  unfold/mixins/__init__.py,sha256=YlhoDs9Y_DXQ8Ejr3enxuHMS1-Ij9fwukxNwpeOS4Co,190
82
83
  unfold/mixins/action_model_admin.py,sha256=BPajuTGf7zAaZOqIq4shX8VQ3NTxWQpedvG58fmCTjk,12036
83
84
  unfold/mixins/base_model_admin.py,sha256=PR4eqU48yB4CNy9Bjd7fjxbOFiFpZM4etYmBOSaRX_M,4140
@@ -85,27 +86,28 @@ unfold/overrides.py,sha256=ERVY9boZiXgjDYcTEZcIp7KWoeBZ45VM_zN31yAOjyQ,2922
85
86
  unfold/paginator.py,sha256=vWCIfpuWbV3nvIkQOPyD7MLTg2hZplHz3rze42d-qiA,268
86
87
  unfold/sections.py,sha256=Lcg3xCZ76fqjm4vD4x7YdE5VsbTKluL-sX5QTEBlHxA,2379
87
88
  unfold/settings.py,sha256=3ICha2I0oSaHhajG8159nHdNTYJInBtRQq9VziWKYKs,2709
88
- unfold/sites.py,sha256=fx3U0kIfN3My4UNiXO_ggrudmFvYNglMAIARaMa2gqY,16321
89
+ unfold/sites.py,sha256=n1IY7xS4316ZfjcwKWQlnkPtzdVCifbltBT-yZoUu3k,16657
89
90
  unfold/static/admin/js/admin/RelatedObjectLookups.js,sha256=alI0-yq7YPDJJJn-yg1ce79-Cv88yQDUrfaGqFZnsaY,9048
90
91
  unfold/static/admin/js/inlines.js,sha256=zcYwLGI_6RHg-gucDbbhp43RRoZd3so8bPk841KFaIE,15573
91
92
  unfold/static/unfold/css/simplebar.css,sha256=5LLaEM11pKi6JFCOLt4XKuZxTpT9rpdq_tNlaQytFlU,4647
92
- unfold/static/unfold/css/styles.css,sha256=-tB48trW_4YUmPdnHYP31ulsXOPiwsC6s6hHffmDW_w,161933
93
+ unfold/static/unfold/css/styles.css,sha256=LolJmXEJUWR42aC7fNQLhfoQZBSzTE9vD-1bynfFeGo,162977
93
94
  unfold/static/unfold/fonts/inter/Inter-Bold.woff2,sha256=-oiBJ7baAVtlVp8DUfO1w5GtkokElR8cIOn4RiqNleo,114840
94
95
  unfold/static/unfold/fonts/inter/Inter-Medium.woff2,sha256=D_PpRhThST61VjFP0keubEqFp3g7TMhr5TmUDPg_Kkg,114348
95
96
  unfold/static/unfold/fonts/inter/Inter-Regular.woff2,sha256=4G9rG8VTqupORmgCPtCrChRxKcMQf1Ebx9A9NhsK4IU,111268
96
97
  unfold/static/unfold/fonts/inter/Inter-SemiBold.woff2,sha256=XLcQPk5gWYmv68A9mJx5IB5UshtRg9szmB9w25F4owE,114812
97
98
  unfold/static/unfold/fonts/inter/styles.css,sha256=QqdgevXVfcD6vZGo8ciSkaEG_62_YcZg0hvHTNgJ56E,608
98
- unfold/static/unfold/fonts/material-symbols/Material-Symbols-Outlined.woff2,sha256=V6mmjk69Ee-coQvMsGV0tcNgV3E1nb7N9IKW8HVUBzk,256424
99
+ unfold/static/unfold/fonts/material-symbols/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
100
+ unfold/static/unfold/fonts/material-symbols/Material-Symbols-Outlined.woff2,sha256=sMDmadC_mJOI200LuWcwAZRwAqDWLgdj85JA0x42qu8,274228
99
101
  unfold/static/unfold/fonts/material-symbols/styles.css,sha256=ZX1icA_BXCVL0eoY2H6XR8VT_Fxl_KnV6vFBm_OImHA,535
100
102
  unfold/static/unfold/js/alpine.anchor.js,sha256=2Jg9aUq749pjFynzr_H1NK3lf-nXrbHMtO9wvlWJyIQ,15524
101
- unfold/static/unfold/js/alpine.js,sha256=NY2a-7GrW--i9IBhowd25bzXcH9BCmBrqYX5i8OxwDQ,44659
103
+ unfold/static/unfold/js/alpine.js,sha256=PtHu0lJIiSHfZeNj1nFd6wTX-Squ255SGZ_fc8seCtM,44758
102
104
  unfold/static/unfold/js/alpine.persist.js,sha256=jFBwr6faTqqhp3sVi4_VTxJ0FpaF9YGZN1ZGLl_5QYM,837
103
105
  unfold/static/unfold/js/alpine.resize.js,sha256=mXR2L6neT584cfNLY5_imDq5DKjW1noXys7fhb1yxY4,703
104
106
  unfold/static/unfold/js/alpine.sort.js,sha256=q6LxgConwu8M8R03mZSBcsKwJv6fF7oomP8sqLxVQ0w,37891
105
107
  unfold/static/unfold/js/app.js,sha256=mxCSV8gZvRLHab-_Xm_229NtQ9IX-59vS_GKWTp9liM,7748
106
108
  unfold/static/unfold/js/chart.js,sha256=22W6cFERR-CElMOKRgMMicueMVP0Vf7FBEBYH8Z8tCk,200633
107
109
  unfold/static/unfold/js/htmx.js,sha256=acrv0NqSJpBm5yXX_hdeJrnVDJYuMFZFnAxHcVTNudM,50918
108
- unfold/static/unfold/js/select2.init.js,sha256=gXZngZh-l5ID2-VmndpGseNi6A0zKB18UgPyCor68CU,1040
110
+ unfold/static/unfold/js/select2.init.js,sha256=l0O8ggw_CkAw-QtH3qEzC8xWk0mR9QHjy4VzAjMwiQQ,1109
109
111
  unfold/static/unfold/js/simplebar.js,sha256=t-uG1FAD6ZoiMeN--wac0XRS7SxoDVG6zvRnGuEp7X8,27176
110
112
  unfold/styles.css,sha256=Mbe0mO2608aH3bCVllxTKb6OcsMIy-ZAugcwaWBwM0E,19142
111
113
  unfold/templates/admin/actions.html,sha256=aroIid16mORcx-0p8O2dOoWupEoTGX66pt-PAXjSYZ0,3063
@@ -139,7 +141,7 @@ unfold/templates/auth/widgets/read_only_password_hash.html,sha256=bQiYKDKJkyVP0_
139
141
  unfold/templates/registration/logged_out.html,sha256=GgLzqmOGpiw5FcA-Aw_T6YvhYHhGFWhyES40T28waLQ,1002
140
142
  unfold/templates/registration/password_change_done.html,sha256=9HDpSraz3Kxdy0Fghbn-Lv0GAtpPgG6cuO7fvfMiYg8,1077
141
143
  unfold/templates/registration/password_change_form.html,sha256=EgakVWzpQUEJKQS0AOfk8O_CY1bKbyWXSUuBaxtQEdU,2232
142
- unfold/templates/unfold/components/button.html,sha256=TABo79ppuGlm2ENvRs6L3Vne8B5_9he_OhZTnPMiitQ,473
144
+ unfold/templates/unfold/components/button.html,sha256=db7bT5aR4WXE3aJzHpKkRPGCD2UHrNgVRvZXLSHmIC4,603
143
145
  unfold/templates/unfold/components/card.html,sha256=FVLN7kQZqMd_uhEi1LpiN9FUxZCa4RgGHYGmofCtQro,1141
144
146
  unfold/templates/unfold/components/chart/bar.html,sha256=nDdDCUXvFd793OpNgNPQo0vBmvYngMsQyPHVTf1vfOw,297
145
147
  unfold/templates/unfold/components/chart/cohort.html,sha256=wDjRb2IT1XEJ--Y1BrSaMOeM7XXy9iohJR_4RZ2ilSs,2362
@@ -222,8 +224,8 @@ unfold/templates/unfold/layouts/base.html,sha256=e9T-vuPvrySHf23LLwRuTQXUg0Q4x-6
222
224
  unfold/templates/unfold/layouts/base_simple.html,sha256=VigjNWnWOt_XtbahvqsroPf8mdwl7cDGbg5fEaLmG9s,1211
223
225
  unfold/templates/unfold/layouts/skeleton.html,sha256=TmZ0hX5j7McwSiYcdGGuhAjDunPDCWtPC7lDF6hbtDk,4641
224
226
  unfold/templates/unfold/templatetags/preserve_changelist_filters.html,sha256=sx2jUhogNY4emBhY8PqxQ6mXifbXQb_IQI9Hd0boJI0,111
225
- unfold/templates/unfold/widgets/clearable_file_input.html,sha256=4vsLRjqOmGqdU4aPSq81rc-mdRvpBr-H_bC5ScyUkEY,2231
226
- unfold/templates/unfold/widgets/clearable_file_input_small.html,sha256=NNlosUwhzv-3SZH9LdliL5c6fdoVNRb1JP0uHwFV-N8,2849
227
+ unfold/templates/unfold/widgets/clearable_file_input.html,sha256=3C4N7OZaiQILNahs12NotNxlQ5QInaXVQtMR4bekHw8,2149
228
+ unfold/templates/unfold/widgets/clearable_file_input_small.html,sha256=FkjdsrkrBa0GxmY3RNZoU08Xs5aDrOD4Q8fU8VbY_vU,2574
227
229
  unfold/templates/unfold/widgets/date.html,sha256=ih9wj8koTaHb9s1NGUmyN9rHvSAmqCMbti9ONDOIujg,122
228
230
  unfold/templates/unfold/widgets/foreign_key_raw_id.html,sha256=7-JPapXS2Ty0MLXNdZ1jCuydqc6JyDs-8-w9b8TcmVU,1075
229
231
  unfold/templates/unfold/widgets/radio.html,sha256=7Mgc92mMwJdVLjCxoGj5lo76KNaN5dKKktV3vzmbeqA,652
@@ -236,14 +238,34 @@ unfold/templates/unfold/widgets/split_money.html,sha256=AFLUBmzGbY-RXgsfz7gaDxVR
236
238
  unfold/templates/unfold/widgets/textarea.html,sha256=-ZLDGrtASero7L-J3VvTNq_KjPAZh_kLVw0Ow3awqXM,144
237
239
  unfold/templates/unfold/widgets/time.html,sha256=ih9wj8koTaHb9s1NGUmyN9rHvSAmqCMbti9ONDOIujg,122
238
240
  unfold/templates/unfold/widgets/url.html,sha256=IRLgW44VTKN7UrSWeywJwaxQhfG5jhhXuT-KTadKX58,304
241
+ unfold/templates/unfold_crispy/display_form.html,sha256=tGyr8hvMlnKkm2ILbVj0enj6IrjtqiKo6nO9Z-zHE2Q,270
242
+ unfold/templates/unfold_crispy/errors.html,sha256=bzTXuZh_UkQLb4sPaa0En5Uopveoe573TaEOM9OQVcw,132
243
+ unfold/templates/unfold_crispy/field.html,sha256=7Q55yPp0-lkIF2v916GNJbjWjaRlQSvlLJ6KvG9TabI,1610
244
+ unfold/templates/unfold_crispy/inputs.html,sha256=GFOh6U3XzZJvUAZ4_V9Jrbm3FusI5flWzT_sEWLn8tI,294
245
+ unfold/templates/unfold_crispy/layout/attrs.html,sha256=LsLlfKenzHKl6n25m4Ers2NepLSA_RBy9AqezKZ7rH8,173
246
+ unfold/templates/unfold_crispy/layout/baseinput.html,sha256=GjMxXoqpnxCPkPC1E3v_crzlxDb_hHMf_UgJYfqSsYE,440
247
+ unfold/templates/unfold_crispy/layout/button.html,sha256=evlO80en9FkbW3UayLPTNUZXAYvgr3slSAsGPKXs96M,67
248
+ unfold/templates/unfold_crispy/layout/buttonholder.html,sha256=ruHqViROk7nh4luckA8-mcfmnYBqsVM85e8j0rOi3cU,415
249
+ unfold/templates/unfold_crispy/layout/checkbox.html,sha256=DT3zJIUCDuGJ-gxaaNgHUSWfU7oWyk5lvJPREGv830w,732
250
+ unfold/templates/unfold_crispy/layout/column.html,sha256=Bob4xRcbJOHScDjSjLMRIMNf4lonNmm8F_JOm2azTik,172
251
+ unfold/templates/unfold_crispy/layout/div.html,sha256=RE9qQ2w2I2zpi7Z7xzVmVO03UXq0yZvMw-W7rVJkEdw,174
252
+ unfold/templates/unfold_crispy/layout/field_errors.html,sha256=pEn-c6Bfdg54vRm2FxmM8BGbvN0O2R92bnj_gL81X_Q,274
253
+ unfold/templates/unfold_crispy/layout/fieldset.html,sha256=nfIp48NjAJUyNYHkrY7LLEvdgm9rkbytupqzLWTgkFc,581
254
+ unfold/templates/unfold_crispy/layout/help_text.html,sha256=FY0UQv5FsoOFhrqvbILEfZE78Hj30r73YHCtg_MWNeY,207
255
+ unfold/templates/unfold_crispy/layout/help_text_and_errors.html,sha256=Um14wIg0FHthNM0mKE9VdptTrC-lTTcrDUO-UbbXxhY,108
256
+ unfold/templates/unfold_crispy/layout/radio_checkbox_select.html,sha256=pUlHC6j-fH62fAMiAZW-4BWXGD0RocHOKCmLtHN9pP8,997
257
+ unfold/templates/unfold_crispy/layout/row.html,sha256=MgGCI9s2mEn8C6xc78fb2Y4BsW6sbsKvgEd6aVRlFsg,184
258
+ unfold/templates/unfold_crispy/layout/table_inline_formset.html,sha256=QkKQU54uPTtFH58waipszk4SsQQw6mnBEqrzqgNsbls,4557
259
+ unfold/templates/unfold_crispy/uni_form.html,sha256=l9UNCUrIycgJ0p_B6qhtO_YxdARy3aq8qYY5h5BheSo,312
260
+ unfold/templates/unfold_crispy/whole_uni_form.html,sha256=ufXRKCIGqkqKFOsh166rpKUnHpFMyBV4eYUCRhs3iws,475
239
261
  unfold/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
240
- unfold/templatetags/unfold.py,sha256=yPSNtaBDiiJh6VNhl3VPDJ-jsX69pbnIf4JolEnw290,16239
262
+ unfold/templatetags/unfold.py,sha256=FcP69QNQ2_CPeqESvbxNax_VRw3WAE_R8Bjdkn4ZJuQ,16581
241
263
  unfold/templatetags/unfold_list.py,sha256=ayShOYEXcV3o7wKt3t2aFTWvg62Mb-WGlxjYVGgk3a0,14344
242
264
  unfold/typing.py,sha256=i7LM2LiwYTAjT5-OLDUPVn5b9X-DMmHnjlZG2toWwSE,692
243
265
  unfold/utils.py,sha256=SJikRuAuJN0bE9rfzUujZwycTRrzQm14Hk0zB0EwivU,6164
244
266
  unfold/views.py,sha256=FuYnMrlg6SegLUDDN6An__yZj8rchD_iBK8myieYhkA,1492
245
- unfold/widgets.py,sha256=1uyGsw-xA0qspf7Uzic47GVN5xv4L_sVfL-elqlxgm0,20293
246
- django_unfold-0.54.0.dist-info/LICENSE.md,sha256=Ltk_quRyyvV3J5v3brtOqmibeZSw2Hrb8bY1W3ya0Ik,1077
247
- django_unfold-0.54.0.dist-info/METADATA,sha256=jKgXXRccydhPXLehmUQkHA_xcBzY2XulaY2fB04a_NA,5929
248
- django_unfold-0.54.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
249
- django_unfold-0.54.0.dist-info/RECORD,,
267
+ unfold/widgets.py,sha256=-l0eRljJheVWhTtD0nTvNuY1dvNmwtfd-aqXdSVr-q0,22344
268
+ django_unfold-0.55.0.dist-info/LICENSE.md,sha256=Ltk_quRyyvV3J5v3brtOqmibeZSw2Hrb8bY1W3ya0Ik,1077
269
+ django_unfold-0.55.0.dist-info/METADATA,sha256=6Wl1ju636FxUQ16ZKM9WFgvPOq5fdhX4S-I--r7XTqg,6977
270
+ django_unfold-0.55.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
271
+ django_unfold-0.55.0.dist-info/RECORD,,
@@ -8,7 +8,11 @@ from django.db.models import QuerySet
8
8
  from django.http import HttpRequest
9
9
  from django.utils.translation import gettext_lazy as _
10
10
 
11
- from unfold.contrib.filters.admin.mixins import MultiValueMixin, ValueMixin
11
+ from unfold.contrib.filters.admin.mixins import (
12
+ ChoicesMixin,
13
+ MultiValueMixin,
14
+ ValueMixin,
15
+ )
12
16
  from unfold.contrib.filters.forms import CheckboxForm, HorizontalRadioForm, RadioForm
13
17
 
14
18
 
@@ -18,10 +22,23 @@ class RadioFilter(admin.SimpleListFilter):
18
22
  all_option = ["", _("All")]
19
23
 
20
24
  def choices(self, changelist: ChangeList) -> tuple[dict[str, Any], ...]:
25
+ add_facets = changelist.add_facets
26
+ facet_counts = self.get_facet_queryset(changelist) if add_facets else None
27
+ choices = []
28
+
21
29
  if self.all_option:
22
- choices = [self.all_option, *self.lookup_choices]
30
+ choices = [self.all_option]
31
+
32
+ if add_facets:
33
+ for i, (lookup, title) in enumerate(self.lookup_choices):
34
+ if (count := facet_counts.get(f"{i}__c", -1)) != -1:
35
+ title = f"{title} ({count})"
36
+ else:
37
+ title = f"{title} (-)"
38
+
39
+ choices.append((lookup, title))
23
40
  else:
24
- choices = self.lookup_choices
41
+ choices.extend(self.lookup_choices)
25
42
 
26
43
  return (
27
44
  {
@@ -39,24 +56,8 @@ class CheckboxFilter(RadioFilter):
39
56
  form_class = CheckboxForm
40
57
  all_option = None
41
58
 
42
-
43
- class ChoicesMixin:
44
- template = "unfold/filters/filters_field.html"
45
-
46
- def choices(self, changelist: ChangeList) -> Generator[dict[str, Any], None, None]:
47
- if self.all_option:
48
- choices = [self.all_option, *self.field.flatchoices]
49
- else:
50
- choices = self.field.flatchoices
51
-
52
- yield {
53
- "form": self.form_class(
54
- label=_(" By %(filter_title)s ") % {"filter_title": self.title},
55
- name=self.lookup_kwarg,
56
- choices=choices,
57
- data={self.lookup_kwarg: self.value()},
58
- ),
59
- }
59
+ def value(self) -> list[Any]:
60
+ return self.request.GET.getlist(self.parameter_name)
60
61
 
61
62
 
62
63
  class ChoicesRadioFilter(ValueMixin, ChoicesMixin, admin.ChoicesFieldListFilter):
@@ -64,7 +65,9 @@ class ChoicesRadioFilter(ValueMixin, ChoicesMixin, admin.ChoicesFieldListFilter)
64
65
  all_option = ["", _("All")]
65
66
 
66
67
 
67
- class ChoicesCheckboxFilter(ValueMixin, ChoicesMixin, admin.ChoicesFieldListFilter):
68
+ class ChoicesCheckboxFilter(
69
+ MultiValueMixin, ChoicesMixin, admin.ChoicesFieldListFilter
70
+ ):
68
71
  form_class = CheckboxForm
69
72
  all_option = None
70
73
 
@@ -75,13 +78,25 @@ class BooleanRadioFilter(ValueMixin, admin.BooleanFieldListFilter):
75
78
  all_option = ["", _("All")]
76
79
 
77
80
  def choices(self, changelist: ChangeList) -> Generator[dict[str, Any], None, None]:
78
- choices = [
79
- self.all_option,
80
- *[
81
- ("1", _("Yes")),
82
- ("0", _("No")),
83
- ],
84
- ]
81
+ add_facets = changelist.add_facets
82
+ facet_counts = self.get_facet_queryset(changelist) if add_facets else None
83
+
84
+ if add_facets:
85
+ choices = [
86
+ self.all_option,
87
+ *[
88
+ ("1", f"{_('Yes')} ({facet_counts['true__c']})"),
89
+ ("0", f"{_('No')} ({facet_counts['false__c']})"),
90
+ ],
91
+ ]
92
+ else:
93
+ choices = [
94
+ self.all_option,
95
+ *[
96
+ ("1", _("Yes")),
97
+ ("0", _("No")),
98
+ ],
99
+ ]
85
100
 
86
101
  yield {
87
102
  "form": self.form_class(
@@ -104,11 +119,24 @@ class RelatedCheckboxFilter(MultiValueMixin, admin.RelatedFieldListFilter):
104
119
  return queryset
105
120
 
106
121
  def choices(self, changelist: ChangeList) -> Generator[dict[str, Any], None, None]:
122
+ add_facets = changelist.add_facets
123
+ facet_counts = self.get_facet_queryset(changelist) if add_facets else None
124
+
125
+ if add_facets:
126
+ choices = []
127
+
128
+ for pk_val, val in self.lookup_choices:
129
+ count = facet_counts[f"{pk_val}__c"]
130
+ choice = (pk_val, f"{val} ({count})")
131
+ choices.append(choice)
132
+ else:
133
+ choices = self.lookup_choices
134
+
107
135
  yield {
108
136
  "form": self.form_class(
109
137
  label=_(" By %(filter_title)s ") % {"filter_title": self.title},
110
138
  name=self.lookup_kwarg,
111
- choices=self.lookup_choices,
139
+ choices=choices,
112
140
  data={self.lookup_kwarg: self.value()},
113
141
  ),
114
142
  }
@@ -119,7 +147,18 @@ class AllValuesCheckboxFilter(MultiValueMixin, admin.AllValuesFieldListFilter):
119
147
  form_class = CheckboxForm
120
148
 
121
149
  def choices(self, changelist: ChangeList) -> Generator[dict[str, Any], None, None]:
122
- choices = [[i, val] for i, val in enumerate(self.lookup_choices)]
150
+ add_facets = changelist.add_facets
151
+ facet_counts = self.get_facet_queryset(changelist) if add_facets else None
152
+
153
+ if add_facets:
154
+ choices = []
155
+
156
+ for i, val in enumerate(self.lookup_choices):
157
+ count = facet_counts[f"{i}__c"]
158
+ choice = (val, f"{val} ({count})")
159
+ choices.append(choice)
160
+ else:
161
+ choices = [[val, val] for _i, val in enumerate(self.lookup_choices)]
123
162
 
124
163
  if len(choices) == 0:
125
164
  return
@@ -128,7 +167,7 @@ class AllValuesCheckboxFilter(MultiValueMixin, admin.AllValuesFieldListFilter):
128
167
  "form": self.form_class(
129
168
  label=_(" By %(filter_title)s ") % {"filter_title": self.title},
130
169
  name=self.lookup_kwarg,
131
- choices=[[i, val] for i, val in enumerate(self.lookup_choices)],
170
+ choices=choices,
132
171
  data={self.lookup_kwarg: self.value()},
133
172
  ),
134
173
  }
@@ -86,11 +86,25 @@ class RelatedDropdownFilter(ValueMixin, DropdownMixin, admin.RelatedFieldListFil
86
86
  self.request = request
87
87
 
88
88
  def choices(self, changelist: ChangeList) -> Generator[dict[str, Any], None, None]:
89
+ add_facets = changelist.add_facets
90
+ facet_counts = self.get_facet_queryset(changelist) if add_facets else None
91
+
92
+ if add_facets:
93
+ choices = [self.all_option]
94
+
95
+ for pk_val, val in self.lookup_choices:
96
+ if add_facets:
97
+ count = facet_counts[f"{pk_val}__c"]
98
+ choice = (pk_val, f"{val} ({count})")
99
+ choices.append(choice)
100
+ else:
101
+ choices = [self.all_option, *self.lookup_choices]
102
+
89
103
  yield {
90
104
  "form": self.form_class(
91
105
  label=_(" By %(filter_title)s ") % {"filter_title": self.title},
92
106
  name=self.lookup_kwarg,
93
- choices=[self.all_option, *self.lookup_choices],
107
+ choices=choices,
94
108
  data={self.lookup_kwarg: self.value()},
95
109
  multiple=self.multiple if hasattr(self, "multiple") else False,
96
110
  ),
@@ -51,6 +51,31 @@ class DropdownMixin:
51
51
  return queryset
52
52
 
53
53
 
54
+ class ChoicesMixin:
55
+ template = "unfold/filters/filters_field.html"
56
+
57
+ def choices(self, changelist: ChangeList) -> Generator[dict[str, Any], None, None]:
58
+ add_facets = changelist.add_facets
59
+ facet_counts = self.get_facet_queryset(changelist) if add_facets else None
60
+ choices = [self.all_option] if self.all_option else []
61
+
62
+ for i, choice in enumerate(self.field.flatchoices):
63
+ if add_facets:
64
+ count = facet_counts[f"{i}__c"]
65
+ choice = (choice[0], f"{choice[1]} ({count})")
66
+
67
+ choices.append(choice)
68
+
69
+ yield {
70
+ "form": self.form_class(
71
+ label=_(" By %(filter_title)s ") % {"filter_title": self.title},
72
+ name=self.lookup_kwarg,
73
+ choices=choices,
74
+ data={self.lookup_kwarg: self.value()},
75
+ ),
76
+ }
77
+
78
+
54
79
  class RangeNumericMixin:
55
80
  request = None
56
81
  parameter_name = None
unfold/layout.py ADDED
@@ -0,0 +1,23 @@
1
+ from crispy_forms.layout import BaseInput
2
+
3
+ from unfold.widgets import BUTTON_CLASSES
4
+
5
+
6
+ class ButtonClassesMixin:
7
+ def __init__(self, *args, css_class=None, **kwargs):
8
+ classes = BUTTON_CLASSES
9
+
10
+ if css_class:
11
+ classes.append(css_class)
12
+
13
+ self.field_classes = " ".join(classes)
14
+
15
+ super().__init__(*args, **kwargs)
16
+
17
+
18
+ class Submit(ButtonClassesMixin, BaseInput):
19
+ input_type = "submit"
20
+
21
+
22
+ class Button(ButtonClassesMixin, BaseInput):
23
+ input_type = "button"
unfold/sites.py CHANGED
@@ -24,9 +24,16 @@ except ImportError:
24
24
  return func
25
25
 
26
26
 
27
- from .settings import get_config
28
- from .utils import hex_to_rgb
29
- from .widgets import CHECKBOX_CLASSES, INPUT_CLASSES
27
+ from unfold.settings import get_config
28
+ from unfold.utils import hex_to_rgb
29
+ from unfold.widgets import (
30
+ BUTTON_CLASSES,
31
+ CHECKBOX_CLASSES,
32
+ FILE_CLASSES,
33
+ INPUT_CLASSES,
34
+ RADIO_CLASSES,
35
+ SWITCH_CLASSES,
36
+ )
30
37
 
31
38
 
32
39
  class UnfoldAdminSite(AdminSite):
@@ -34,7 +41,7 @@ class UnfoldAdminSite(AdminSite):
34
41
  settings_name = "UNFOLD"
35
42
 
36
43
  def __init__(self, name: str = "admin") -> None:
37
- from .forms import AuthenticationForm
44
+ from unfold.forms import AuthenticationForm
38
45
 
39
46
  super().__init__(name)
40
47
 
@@ -68,8 +75,12 @@ class UnfoldAdminSite(AdminSite):
68
75
  sidebar_config = self._get_config("SIDEBAR", request)
69
76
  data = {
70
77
  "form_classes": {
71
- "text_input": INPUT_CLASSES,
72
- "checkbox": CHECKBOX_CLASSES,
78
+ "text_input": " ".join(INPUT_CLASSES),
79
+ "checkbox": " ".join(CHECKBOX_CLASSES),
80
+ "button": " ".join(BUTTON_CLASSES),
81
+ "radio": " ".join(RADIO_CLASSES),
82
+ "switch": " ".join(SWITCH_CLASSES),
83
+ "file": " ".join(FILE_CLASSES),
73
84
  },
74
85
  "site_title": self._get_config("SITE_TITLE", request) or self.site_title,
75
86
  "site_header": self._get_config("SITE_HEADER", request) or self.site_header,