django-unfold 0.29.1__tar.gz → 0.31.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (207) hide show
  1. {django_unfold-0.29.1 → django_unfold-0.31.0}/PKG-INFO +63 -19
  2. {django_unfold-0.29.1 → django_unfold-0.31.0}/README.md +62 -18
  3. {django_unfold-0.29.1 → django_unfold-0.31.0}/pyproject.toml +1 -1
  4. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/admin.py +32 -11
  5. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +3 -3
  6. django_unfold-0.31.0/src/unfold/contrib/forms/static/unfold/forms/js/trix.js +5 -0
  7. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/forms/templates/unfold/forms/array.html +3 -1
  8. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html +6 -6
  9. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/forms/templates/unfold/forms/wysiwyg.html +1 -1
  10. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/forms/widgets.py +22 -11
  11. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/guardian/templates/unfold/guardian/group_form.html +4 -4
  12. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/guardian/templates/unfold/guardian/user_form.html +4 -4
  13. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_form.html +1 -1
  14. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_errors.html +1 -1
  15. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_preview.html +3 -3
  16. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_validation.html +4 -4
  17. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/inlines/forms.py +1 -2
  18. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/simple_history/templates/simple_history/object_history_list.html +9 -9
  19. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/simple_history/templates/simple_history/submit_line.html +1 -1
  20. django_unfold-0.31.0/src/unfold/dataclasses.py +22 -0
  21. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/fields.py +2 -2
  22. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/forms.py +18 -3
  23. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/settings.py +1 -0
  24. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/sites.py +39 -15
  25. django_unfold-0.31.0/src/unfold/static/unfold/css/styles.css +1 -0
  26. django_unfold-0.31.0/src/unfold/static/unfold/js/alpine.anchor.js +1 -0
  27. django_unfold-0.31.0/src/unfold/static/unfold/js/alpine.js +5 -0
  28. django_unfold-0.31.0/src/unfold/static/unfold/js/alpine.persist.js +1 -0
  29. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/js/app.js +45 -3
  30. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/styles.css +15 -11
  31. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/actions.html +1 -1
  32. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/app_list.html +1 -1
  33. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/base.html +4 -4
  34. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/change_list.html +2 -2
  35. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/change_list_results.html +3 -3
  36. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/delete_confirmation.html +4 -4
  37. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/delete_selected_confirmation.html +4 -4
  38. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/edit_inline/stacked.html +2 -2
  39. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/edit_inline/tabular.html +4 -4
  40. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/filter.html +2 -2
  41. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/includes/fieldset.html +1 -1
  42. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/includes/object_delete_summary.html +1 -1
  43. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/login.html +8 -8
  44. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/object_history.html +4 -4
  45. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/search_form.html +1 -1
  46. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/submit_line.html +7 -5
  47. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/auth/widgets/read_only_password_hash.html +1 -1
  48. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/registration/logged_out.html +1 -1
  49. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/change_list_filter.html +10 -2
  50. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/account_links.html +2 -2
  51. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/actions_row.html +4 -4
  52. django_unfold-0.31.0/src/unfold/templates/unfold/helpers/app_list.html +99 -0
  53. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/app_list_default.html +4 -4
  54. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/breadcrumb_item.html +1 -1
  55. django_unfold-0.31.0/src/unfold/templates/unfold/helpers/field_readonly_value.html +1 -0
  56. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/fieldset_row.html +7 -7
  57. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/fieldsets_tabs.html +2 -2
  58. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/header.html +1 -1
  59. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/help_text.html +1 -1
  60. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/history.html +1 -1
  61. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/label.html +1 -1
  62. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/search.html +7 -4
  63. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/search_results.html +2 -2
  64. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/tab_action.html +1 -1
  65. django_unfold-0.31.0/src/unfold/templates/unfold/helpers/tab_list.html +62 -0
  66. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/theme_switch.html +2 -2
  67. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/layouts/skeleton.html +6 -1
  68. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/clearable_file_input.html +14 -6
  69. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/clearable_file_input_small.html +4 -4
  70. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/split_datetime.html +2 -2
  71. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templatetags/unfold.py +33 -12
  72. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templatetags/unfold_list.py +16 -6
  73. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/widgets.py +11 -4
  74. django_unfold-0.29.1/src/unfold/contrib/forms/static/unfold/forms/js/trix.js +0 -5
  75. django_unfold-0.29.1/src/unfold/dataclasses.py +0 -13
  76. django_unfold-0.29.1/src/unfold/static/unfold/css/styles.css +0 -1
  77. django_unfold-0.29.1/src/unfold/static/unfold/js/alpine.js +0 -5
  78. django_unfold-0.29.1/src/unfold/static/unfold/js/alpine.persist.js +0 -1
  79. django_unfold-0.29.1/src/unfold/templates/unfold/helpers/app_list.html +0 -89
  80. django_unfold-0.29.1/src/unfold/templates/unfold/helpers/field_readonly_value.html +0 -1
  81. django_unfold-0.29.1/src/unfold/templates/unfold/helpers/tab_list.html +0 -40
  82. {django_unfold-0.29.1 → django_unfold-0.31.0}/LICENSE.md +0 -0
  83. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/__init__.py +0 -0
  84. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/apps.py +0 -0
  85. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/checks.py +0 -0
  86. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/__init__.py +0 -0
  87. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/__init__.py +0 -0
  88. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/admin.py +0 -0
  89. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/apps.py +0 -0
  90. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/forms.py +0 -0
  91. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/static/unfold/filters/css/nouislider.min.css +0 -0
  92. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/static/unfold/filters/js/DateTimeShortcuts.js +0 -0
  93. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/static/unfold/filters/js/admin-numeric-filter.js +0 -0
  94. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/static/unfold/filters/js/nouislider.min.js +0 -0
  95. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/static/unfold/filters/js/wNumb.min.js +0 -0
  96. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_date_range.html +0 -0
  97. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_datetime_range.html +0 -0
  98. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_field.html +0 -0
  99. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_range.html +0 -0
  100. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_single.html +0 -0
  101. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/forms/__init__.py +0 -0
  102. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/forms/apps.py +0 -0
  103. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/forms/static/unfold/forms/css/trix.css +0 -0
  104. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/forms/static/unfold/forms/js/trix.config.js +0 -0
  105. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/guardian/__init__.py +0 -0
  106. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/guardian/apps.py +0 -0
  107. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/change_form.html +0 -0
  108. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/field.html +0 -0
  109. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage.html +0 -0
  110. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html +0 -0
  111. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html +0 -0
  112. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/__init__.py +0 -0
  113. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/apps.py +0 -0
  114. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/forms.py +0 -0
  115. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/base.html +0 -0
  116. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_export.html +0 -0
  117. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_export_item.html +0 -0
  118. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_import_export.html +0 -0
  119. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_import_item.html +0 -0
  120. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/export.html +0 -0
  121. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/import.html +0 -0
  122. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_confirm.html +0 -0
  123. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_form.html +0 -0
  124. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html +0 -0
  125. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/inlines/__init__.py +0 -0
  126. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/inlines/admin.py +0 -0
  127. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/inlines/apps.py +0 -0
  128. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/inlines/checks.py +0 -0
  129. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/simple_history/__init__.py +0 -0
  130. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/simple_history/apps.py +0 -0
  131. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/simple_history/templates/simple_history/object_history.html +0 -0
  132. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/contrib/simple_history/templates/simple_history/object_history_form.html +0 -0
  133. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/decorators.py +0 -0
  134. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/exceptions.py +0 -0
  135. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/css/simplebar.css +0 -0
  136. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/fonts/inter/Inter-Bold.woff2 +0 -0
  137. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/fonts/inter/Inter-Medium.woff2 +0 -0
  138. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/fonts/inter/Inter-Regular.woff2 +0 -0
  139. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/fonts/inter/Inter-SemiBold.woff2 +0 -0
  140. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/fonts/inter/styles.css +0 -0
  141. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/fonts/material-symbols/Material-Symbols-Outlined.woff2 +0 -0
  142. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/fonts/material-symbols/styles.css +0 -0
  143. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/js/chart.js +0 -0
  144. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/js/htmx.js +0 -0
  145. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/static/unfold/js/simplebar.js +0 -0
  146. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/app_index.html +0 -0
  147. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/auth/user/add_form.html +0 -0
  148. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/auth/user/change_password.html +0 -0
  149. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/base_site.html +0 -0
  150. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/change_form.html +0 -0
  151. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/change_form_object_tools.html +0 -0
  152. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/change_list_object_tools.html +0 -0
  153. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/date_hierarchy.html +0 -0
  154. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/index.html +0 -0
  155. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/nav_sidebar.html +0 -0
  156. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/admin/pagination.html +0 -0
  157. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/registration/password_change_done.html +0 -0
  158. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/registration/password_change_form.html +0 -0
  159. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/button.html +0 -0
  160. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/card.html +0 -0
  161. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/chart/bar.html +0 -0
  162. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/chart/line.html +0 -0
  163. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/container.html +0 -0
  164. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/flex.html +0 -0
  165. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/navigation.html +0 -0
  166. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/progress.html +0 -0
  167. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/separator.html +0 -0
  168. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/text.html +0 -0
  169. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/components/title.html +0 -0
  170. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/add_link.html +0 -0
  171. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/attrs.html +0 -0
  172. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/boolean.html +0 -0
  173. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/display_header.html +0 -0
  174. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/display_label.html +0 -0
  175. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/field.html +0 -0
  176. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/field_readonly.html +0 -0
  177. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/form_errors.html +0 -0
  178. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/form_label.html +0 -0
  179. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/messages/error.html +0 -0
  180. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/messages/errornote.html +0 -0
  181. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/messages/info.html +0 -0
  182. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/messages.html +0 -0
  183. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/navigation.html +0 -0
  184. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/pagination_current_item.html +0 -0
  185. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/pagination_ellipsis.html +0 -0
  186. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/site_icon.html +0 -0
  187. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/site_logo.html +0 -0
  188. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/submit.html +0 -0
  189. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/userlinks.html +0 -0
  190. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/helpers/welcomemsg.html +0 -0
  191. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/layouts/base.html +0 -0
  192. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/layouts/base_simple.html +0 -0
  193. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/date.html +0 -0
  194. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/foreign_key_raw_id.html +0 -0
  195. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/radio.html +0 -0
  196. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/radio_option.html +0 -0
  197. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/range.html +0 -0
  198. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/related_widget_wrapper.html +0 -0
  199. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/split_datetime_vertical.html +0 -0
  200. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/split_money.html +0 -0
  201. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/textarea.html +0 -0
  202. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/textarea_expandable.html +0 -0
  203. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templates/unfold/widgets/time.html +0 -0
  204. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/templatetags/__init__.py +0 -0
  205. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/typing.py +0 -0
  206. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/utils.py +0 -0
  207. {django_unfold-0.29.1 → django_unfold-0.31.0}/src/unfold/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-unfold
3
- Version: 0.29.1
3
+ Version: 0.31.0
4
4
  Summary: Modern Django admin theme for seamless interface development
5
5
  Home-page: https://unfoldadmin.com
6
6
  License: MIT
@@ -33,7 +33,7 @@ Description-Content-Type: text/markdown
33
33
  ![Code Style - Ruff](https://img.shields.io/badge/code%20style-ruff-30173D.svg?style=for-the-badge)
34
34
  ![Pre Commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge)
35
35
 
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.
36
+ Unfold is a theme for Django admin incorporating most common practices for building full-fledged admin areas. It is designed to work on top of default administration provided by Django.
37
37
 
38
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)
@@ -45,7 +45,7 @@ Did you decide to start using Unfold but you don't have time to make the switch
45
45
 
46
46
  ## Features <!-- omit from toc -->
47
47
 
48
- - **Visual**: provides new user interface based on Tailwind CSS framework
48
+ - **Visual**: provides a new user interface based on Tailwind CSS framework
49
49
  - **Sidebar:** simplifies definition of custom sidebar navigation with icons
50
50
  - **Dark mode:** supports both light and dark mode versions
51
51
  - **Configuration:** most of the basic options can be changed in settings.py
@@ -55,14 +55,16 @@ Did you decide to start using Unfold but you don't have time to make the switch
55
55
  - **Array widget:** built-in widget for `django.contrib.postgres.fields.ArrayField`
56
56
  - **Filters:** custom dropdown, numeric, datetime, and text fields
57
57
  - **Dashboard:** custom components for rapid dashboard development
58
+ - **Inline tabs:** group inlines into tab navigation in the change form
58
59
  - **Model tabs:** define custom tab navigations for models
59
- - **Fieldset tabs:** merge several fielsets into tabs in change form
60
- - **Colors:** possibility to override default color scheme
61
- - **Changeform modes:** display fields in changeform in compressed mode
60
+ - **Fieldset tabs:** merge several fieldsets into tabs in the change form
61
+ - **Colors:** possibility to override the default color scheme
62
+ - **Changeform modes:** display fields in the change form in compressed mode
62
63
  - **Third party packages:** default support for multiple popular applications
63
64
  - **Environment label**: distinguish between environments by displaying a label
64
65
  - **Nonrelated inlines**: displays nonrelated model as inline in changeform
65
66
  - **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)
67
+ - **Favicons**: built-in support for configuring various site favicons
66
68
  - **VS Code**: project configuration and development container is included
67
69
 
68
70
  ## Table of contents <!-- omit from toc -->
@@ -111,7 +113,7 @@ Did you decide to start using Unfold but you don't have time to make the switch
111
113
 
112
114
  ## Installation
113
115
 
114
- The installation process is minimal. Everything what is needed after installation is to put new application at the beginning of **INSTALLED_APPS**. Default admin configuration in urls.py can stay as it is and there are no changes required.
116
+ The installation process is minimal. Everything that is needed after installation is to put new application at the beginning of **INSTALLED_APPS**. The default admin configuration in urls.py can stay as it is, and no changes are required.
115
117
 
116
118
  ```python
117
119
  # settings.py
@@ -170,17 +172,24 @@ class CustomAdminClass(ModelAdmin):
170
172
 
171
173
  from django.contrib import admin
172
174
  from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
175
+ from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
173
176
  from django.contrib.auth.models import User
174
177
 
175
178
  from unfold.admin import ModelAdmin
176
179
 
177
180
 
178
181
  admin.site.unregister(User)
182
+ admin.site.unregister(Group)
179
183
 
180
184
 
181
185
  @admin.register(User)
182
186
  class UserAdmin(BaseUserAdmin, ModelAdmin):
183
187
  pass
188
+
189
+
190
+ @admin.register(Group)
191
+ class GroupAdmin(BaseGroupAdmin, ModelAdmin):
192
+ pass
184
193
  ```
185
194
 
186
195
  ## Configuration
@@ -209,6 +218,14 @@ UNFOLD = {
209
218
  "dark": lambda request: static("logo-dark.svg"), # dark mode
210
219
  },
211
220
  "SITE_SYMBOL": "speed", # symbol from icon set
221
+ "SITE_FAVICONS": [
222
+ {
223
+ "rel": "icon",
224
+ "sizes": "32x32",
225
+ "type": "image/svg+xml",
226
+ "href": lambda request: static("favicon.svg"),
227
+ },
228
+ ],
212
229
  "SHOW_HISTORY": True, # show/hide "History" button, default: True
213
230
  "SHOW_VIEW_ON_SITE": True, # show/hide "View on site" button, default: True
214
231
  "ENVIRONMENT": "sample_app.environment_callback",
@@ -255,6 +272,7 @@ UNFOLD = {
255
272
  {
256
273
  "title": _("Navigation"),
257
274
  "separator": True, # Top border
275
+ "collapsible": True, # Collapsible group of links
258
276
  "items": [
259
277
  {
260
278
  "title": _("Dashboard"),
@@ -345,6 +363,12 @@ class CustomAdminClass(ModelAdmin):
345
363
  # Display submit button in filters
346
364
  list_filter_submit = False
347
365
 
366
+ # Display changelist in fullwidth
367
+ list_fullwidth = False
368
+
369
+ # Position horizontal scrollbar in changelist at the top
370
+ list_horizontal_scrollbar_top = False
371
+
348
372
  # Custom actions
349
373
  actions_list = [] # Displayed above the results list
350
374
  actions_row = [] # Displayed in a table row in results list
@@ -438,7 +462,7 @@ class UserAdmin(ModelAdmin):
438
462
  actions_detail = ["change_detail_action_block"]
439
463
  actions_submit_line = ["submit_line_action_activate"]
440
464
 
441
- @action(description=_("Save & Activate"))
465
+ @action(description=_("Save & Activate"), permissions=["submit_line_action_activate"])
442
466
  def submit_line_action_activate(self, request: HttpRequest, obj: User):
443
467
  """
444
468
  If instance is modified in any way, it also needs to be saved,
@@ -450,6 +474,9 @@ class UserAdmin(ModelAdmin):
450
474
  obj.is_active = True
451
475
  obj.save()
452
476
 
477
+ def has_submit_line_action_activate_permission(self, request: HttpRequest, object_id: Union[str, int]):
478
+ pass
479
+
453
480
  @action(description=_("Import"), url_path="import")
454
481
  def changelist_global_action_import(self, request: HttpRequest):
455
482
  """
@@ -474,7 +501,7 @@ class UserAdmin(ModelAdmin):
474
501
  """
475
502
  return redirect(f"https://example.com/{object_id}")
476
503
 
477
- @action(description=_("Detail"), url_path="detail-action", attrs={"target": "_blank"})
504
+ @action(description=_("Detail"), url_path="detail-action", attrs={"target": "_blank"}, permissions=["change_detail_action_block"])
478
505
  def change_detail_action_block(self, request: HttpRequest, object_id: int):
479
506
  """
480
507
  Handler for detail action.
@@ -489,6 +516,10 @@ class UserAdmin(ModelAdmin):
489
516
  return redirect(
490
517
  reverse_lazy("admin:users_user_change", args=(object_id,))
491
518
  )
519
+
520
+
521
+ def has_change_detail_action_block_permission(self, request: HttpRequest, object_id: Union[str, int]):
522
+ pass
492
523
  ```
493
524
 
494
525
  ### Action with form example
@@ -511,10 +542,10 @@ class SomeForm(forms.Form):
511
542
  @register(User)
512
543
  class UserAdmin(ModelAdmin):
513
544
  actions_detail = ["change_detail_action_block"]
514
- form = SomeForm(request.POST or None)
515
545
 
516
546
  @action(description=_("Detail"))
517
547
  def change_detail_action_block(self, request: HttpRequest, object_id: int) -> str:
548
+ form = SomeForm(request.POST or None)
518
549
  user = User.objects.get(pk=object_id)
519
550
 
520
551
  if request.method == "POST" and form.is_valid():
@@ -560,7 +591,7 @@ Text input field which allows filtering by the free string submitted by the user
560
591
 
561
592
  `FieldTextFilter` requires just a model field name and the filter will make `__icontains` search on this field. There are no other things to configure so the integration in `list_filter` will be just one new row looking like `("model_field_name", FieldTextFilter)`.
562
593
 
563
- In the case of the `TextFilter`, it is needed the write a whole new class inheriting from `TextFilter` with a custom implementation of the `queryset` method and the `parameter_name` attribute. This attribute will be a representation of the search query parameter name in URI. The benefit of the `TextFilter` is the possibility of writing complex queries.
594
+ In the case of the `TextFilter`, it is needed to write a whole new class inheriting from `TextFilter` with a custom implementation of the `queryset` method and the `parameter_name` attribute. This attribute will be a representation of the search query parameter name in URI. The benefit of the `TextFilter` is the possibility of writing complex queries.
564
595
 
565
596
  ```python
566
597
  from django.contrib import admin
@@ -595,7 +626,7 @@ class MyAdmin(ModelAdmin):
595
626
 
596
627
  Dropdown filters will display a select field with a list of options. Unfold contains two types of dropdowns: `ChoicesDropdownFilter` and `RelatedDropdownFilter`.
597
628
 
598
- The difference between them is that `ChoicesDropdownFilter` will collect a list of options based on the `choices` attribute of the model field so most commonly it will be used in combination with `CharField` with specified `choices`. On the other side, `RelatedDropdownFilter` needs a one-to-many or many-to-many foreign key to display options.
629
+ The difference between them is that `ChoicesDropdownFilter` will collect a list of options based on the `choices` attribute of the model field so most commonly it will be used in combination with `CharField` with specified `choices`. On the other hand, `RelatedDropdownFilter` needs a one-to-many or many-to-many foreign key to display options.
599
630
 
600
631
  **Note:** At the moment Unfold does not implement a dropdown with an autocomplete functionality, so it is important not to use dropdowns displaying large datasets.
601
632
 
@@ -904,6 +935,19 @@ class MyModelAdmin(ModelAdmin):
904
935
  )
905
936
  ```
906
937
 
938
+ Inlines can be grouped into tab navigation by specifying `tab` attribute in the inline class.
939
+
940
+ ```python
941
+ # admin.py
942
+
943
+ from unfold.admin import TabularInline
944
+
945
+
946
+ class MyInline(TabularInline):
947
+ model = User
948
+ tab = True
949
+ ```
950
+
907
951
  ## Third party packages
908
952
 
909
953
  ### django-celery-beat
@@ -973,11 +1017,11 @@ class ClockedScheduleAdmin(BaseClockedScheduleAdmin, ModelAdmin):
973
1017
 
974
1018
  ### django-guardian
975
1019
 
976
- Adding support for django-guardian is quote straightforward in Unfold, just add `unfold.contrib.guardian` to `INSTALLED_APPS` at the beggining of the file. This action will override all templates coming from the django-guardian. Please note that **Object permissions** link is available in top right dropdown navigation.
1020
+ Adding support for django-guardian is quite straightforward in Unfold, just add `unfold.contrib.guardian` to `INSTALLED_APPS` at the beginning of the file. This action will override all templates coming from the django-guardian. Please note that **Object permissions** link is available in top right dropdown navigation.
977
1021
 
978
1022
  ### django-import-export
979
1023
 
980
- 1. Add `unfold.contrib.import_export` to `INSTALLED_APPS` at the beggining of the file. This action will override all templates coming from the application.
1024
+ 1. Add `unfold.contrib.import_export` to `INSTALLED_APPS` at the beginning of the file. This action will override all templates coming from the application.
981
1025
  2. Change `import_form_class` and `export_form_class` in ModelAdmin which is inheriting from `ImportExportModelAdmin`. This chunk of code is responsible for adding proper styling to form elements.
982
1026
 
983
1027
  ```python
@@ -1049,7 +1093,7 @@ This application is supported in Unfold by default. It is not needed to add any
1049
1093
 
1050
1094
  ### django-simple-history
1051
1095
 
1052
- To make this application work, add `unfold.contrib.simple_history` into `settings.py` in `INSTALLED_APPS` variable before right after `unfold`. This app should ensure that templates coming from django-simple-history are overriden by Unfold.
1096
+ To make this application work, add `unfold.contrib.simple_history` into `settings.py` in `INSTALLED_APPS` variable before right after `unfold`. This app should ensure that templates coming from django-simple-history are overridden by Unfold.
1053
1097
 
1054
1098
  ## User Admin Form
1055
1099
 
@@ -1130,7 +1174,7 @@ npx tailwindcss -o your_project/static/css/styles.css --watch --minify
1130
1174
 
1131
1175
  ### Overriding template
1132
1176
 
1133
- Create `templates/admin/index.html` in your project and paste the base template below into it. By default, all your custom styles here are not compiled because CSS classes are located in your specific project. Here it is needed to set up the Tailwind for your project and all requried instructions are located in [Project Level Tailwind Stylesheet](#project-level-tailwind-stylesheet) chapter.
1177
+ Create `templates/admin/index.html` in your project and paste the base template below into it. By default, all your custom styles here are not compiled because CSS classes are located in your specific project. Here it is needed to set up the Tailwind for your project and all required instructions are located in [Project Level Tailwind Stylesheet](#project-level-tailwind-stylesheet) chapter.
1134
1178
 
1135
1179
  ```html+django
1136
1180
  {% extends 'unfold/layouts/base_simple.html' %}
@@ -1252,7 +1296,7 @@ Below you can find a more complex example which is using multiple components and
1252
1296
 
1253
1297
  ### Pre-commit
1254
1298
 
1255
- Before adding any source code, it is recommended to have pre-commit installed on your local computer to check for all potential issues when comitting the code.
1299
+ Before adding any source code, it is recommended to have pre-commit installed on your local computer to check for all potential issues when committing the code.
1256
1300
 
1257
1301
  ```bash
1258
1302
  pip install pre-commit
@@ -1272,7 +1316,7 @@ To add a new feature or fix the easiest approach is to use django-unfold in comb
1272
1316
 
1273
1317
  ### Compiling Tailwind
1274
1318
 
1275
- At the moment project contains package.json with all dependencies required to compile new CSS file. Tailwind configuration file is set to check all html, js and py files for Tailwind's classeses occurrences.
1319
+ At the moment project contains package.json with all dependencies required to compile new CSS file. Tailwind configuration file is set to check all html, js and py files for Tailwind's classes occurrences.
1276
1320
 
1277
1321
  ```bash
1278
1322
  npm install
@@ -1282,7 +1326,7 @@ npm run tailwind:watch # run after each change in code
1282
1326
  npm run tailwind:build # run once
1283
1327
  ```
1284
1328
 
1285
- Some components like datepickers, calendars or selectors in admin was not possible to style by overriding html templates so their default styles are overriden in **styles.css**.
1329
+ Some components like datepickers, calendars or selectors in admin was not possible to style by overriding html templates so their default styles are overridden in **styles.css**.
1286
1330
 
1287
1331
  **Note:** most of the custom styles located in style.css are created via `@apply some-tailwind-class;` as is not possible to manually add CSS class to element which are for example created via jQuery.
1288
1332
 
@@ -9,7 +9,7 @@
9
9
  ![Code Style - Ruff](https://img.shields.io/badge/code%20style-ruff-30173D.svg?style=for-the-badge)
10
10
  ![Pre Commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge)
11
11
 
12
- 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.
12
+ Unfold is a theme for Django admin incorporating most common practices for building full-fledged admin areas. It is designed to work on top of default administration provided by Django.
13
13
 
14
14
  - **Unfold:** demo site is available at [unfoldadmin.com](https://unfoldadmin.com?utm_medium=github&utm_source=unfold)
15
15
  - **Formula:** repository with demo implementation at [github.com/unfoldadmin/formula](https://github.com/unfoldadmin/formula)
@@ -21,7 +21,7 @@ Did you decide to start using Unfold but you don't have time to make the switch
21
21
 
22
22
  ## Features <!-- omit from toc -->
23
23
 
24
- - **Visual**: provides new user interface based on Tailwind CSS framework
24
+ - **Visual**: provides a new user interface based on Tailwind CSS framework
25
25
  - **Sidebar:** simplifies definition of custom sidebar navigation with icons
26
26
  - **Dark mode:** supports both light and dark mode versions
27
27
  - **Configuration:** most of the basic options can be changed in settings.py
@@ -31,14 +31,16 @@ Did you decide to start using Unfold but you don't have time to make the switch
31
31
  - **Array widget:** built-in widget for `django.contrib.postgres.fields.ArrayField`
32
32
  - **Filters:** custom dropdown, numeric, datetime, and text fields
33
33
  - **Dashboard:** custom components for rapid dashboard development
34
+ - **Inline tabs:** group inlines into tab navigation in the change form
34
35
  - **Model tabs:** define custom tab navigations for models
35
- - **Fieldset tabs:** merge several fielsets into tabs in change form
36
- - **Colors:** possibility to override default color scheme
37
- - **Changeform modes:** display fields in changeform in compressed mode
36
+ - **Fieldset tabs:** merge several fieldsets into tabs in the change form
37
+ - **Colors:** possibility to override the default color scheme
38
+ - **Changeform modes:** display fields in the change form in compressed mode
38
39
  - **Third party packages:** default support for multiple popular applications
39
40
  - **Environment label**: distinguish between environments by displaying a label
40
41
  - **Nonrelated inlines**: displays nonrelated model as inline in changeform
41
42
  - **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)
43
+ - **Favicons**: built-in support for configuring various site favicons
42
44
  - **VS Code**: project configuration and development container is included
43
45
 
44
46
  ## Table of contents <!-- omit from toc -->
@@ -87,7 +89,7 @@ Did you decide to start using Unfold but you don't have time to make the switch
87
89
 
88
90
  ## Installation
89
91
 
90
- The installation process is minimal. Everything what is needed after installation is to put new application at the beginning of **INSTALLED_APPS**. Default admin configuration in urls.py can stay as it is and there are no changes required.
92
+ The installation process is minimal. Everything that is needed after installation is to put new application at the beginning of **INSTALLED_APPS**. The default admin configuration in urls.py can stay as it is, and no changes are required.
91
93
 
92
94
  ```python
93
95
  # settings.py
@@ -146,17 +148,24 @@ class CustomAdminClass(ModelAdmin):
146
148
 
147
149
  from django.contrib import admin
148
150
  from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
151
+ from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
149
152
  from django.contrib.auth.models import User
150
153
 
151
154
  from unfold.admin import ModelAdmin
152
155
 
153
156
 
154
157
  admin.site.unregister(User)
158
+ admin.site.unregister(Group)
155
159
 
156
160
 
157
161
  @admin.register(User)
158
162
  class UserAdmin(BaseUserAdmin, ModelAdmin):
159
163
  pass
164
+
165
+
166
+ @admin.register(Group)
167
+ class GroupAdmin(BaseGroupAdmin, ModelAdmin):
168
+ pass
160
169
  ```
161
170
 
162
171
  ## Configuration
@@ -185,6 +194,14 @@ UNFOLD = {
185
194
  "dark": lambda request: static("logo-dark.svg"), # dark mode
186
195
  },
187
196
  "SITE_SYMBOL": "speed", # symbol from icon set
197
+ "SITE_FAVICONS": [
198
+ {
199
+ "rel": "icon",
200
+ "sizes": "32x32",
201
+ "type": "image/svg+xml",
202
+ "href": lambda request: static("favicon.svg"),
203
+ },
204
+ ],
188
205
  "SHOW_HISTORY": True, # show/hide "History" button, default: True
189
206
  "SHOW_VIEW_ON_SITE": True, # show/hide "View on site" button, default: True
190
207
  "ENVIRONMENT": "sample_app.environment_callback",
@@ -231,6 +248,7 @@ UNFOLD = {
231
248
  {
232
249
  "title": _("Navigation"),
233
250
  "separator": True, # Top border
251
+ "collapsible": True, # Collapsible group of links
234
252
  "items": [
235
253
  {
236
254
  "title": _("Dashboard"),
@@ -321,6 +339,12 @@ class CustomAdminClass(ModelAdmin):
321
339
  # Display submit button in filters
322
340
  list_filter_submit = False
323
341
 
342
+ # Display changelist in fullwidth
343
+ list_fullwidth = False
344
+
345
+ # Position horizontal scrollbar in changelist at the top
346
+ list_horizontal_scrollbar_top = False
347
+
324
348
  # Custom actions
325
349
  actions_list = [] # Displayed above the results list
326
350
  actions_row = [] # Displayed in a table row in results list
@@ -414,7 +438,7 @@ class UserAdmin(ModelAdmin):
414
438
  actions_detail = ["change_detail_action_block"]
415
439
  actions_submit_line = ["submit_line_action_activate"]
416
440
 
417
- @action(description=_("Save & Activate"))
441
+ @action(description=_("Save & Activate"), permissions=["submit_line_action_activate"])
418
442
  def submit_line_action_activate(self, request: HttpRequest, obj: User):
419
443
  """
420
444
  If instance is modified in any way, it also needs to be saved,
@@ -426,6 +450,9 @@ class UserAdmin(ModelAdmin):
426
450
  obj.is_active = True
427
451
  obj.save()
428
452
 
453
+ def has_submit_line_action_activate_permission(self, request: HttpRequest, object_id: Union[str, int]):
454
+ pass
455
+
429
456
  @action(description=_("Import"), url_path="import")
430
457
  def changelist_global_action_import(self, request: HttpRequest):
431
458
  """
@@ -450,7 +477,7 @@ class UserAdmin(ModelAdmin):
450
477
  """
451
478
  return redirect(f"https://example.com/{object_id}")
452
479
 
453
- @action(description=_("Detail"), url_path="detail-action", attrs={"target": "_blank"})
480
+ @action(description=_("Detail"), url_path="detail-action", attrs={"target": "_blank"}, permissions=["change_detail_action_block"])
454
481
  def change_detail_action_block(self, request: HttpRequest, object_id: int):
455
482
  """
456
483
  Handler for detail action.
@@ -465,6 +492,10 @@ class UserAdmin(ModelAdmin):
465
492
  return redirect(
466
493
  reverse_lazy("admin:users_user_change", args=(object_id,))
467
494
  )
495
+
496
+
497
+ def has_change_detail_action_block_permission(self, request: HttpRequest, object_id: Union[str, int]):
498
+ pass
468
499
  ```
469
500
 
470
501
  ### Action with form example
@@ -487,10 +518,10 @@ class SomeForm(forms.Form):
487
518
  @register(User)
488
519
  class UserAdmin(ModelAdmin):
489
520
  actions_detail = ["change_detail_action_block"]
490
- form = SomeForm(request.POST or None)
491
521
 
492
522
  @action(description=_("Detail"))
493
523
  def change_detail_action_block(self, request: HttpRequest, object_id: int) -> str:
524
+ form = SomeForm(request.POST or None)
494
525
  user = User.objects.get(pk=object_id)
495
526
 
496
527
  if request.method == "POST" and form.is_valid():
@@ -536,7 +567,7 @@ Text input field which allows filtering by the free string submitted by the user
536
567
 
537
568
  `FieldTextFilter` requires just a model field name and the filter will make `__icontains` search on this field. There are no other things to configure so the integration in `list_filter` will be just one new row looking like `("model_field_name", FieldTextFilter)`.
538
569
 
539
- In the case of the `TextFilter`, it is needed the write a whole new class inheriting from `TextFilter` with a custom implementation of the `queryset` method and the `parameter_name` attribute. This attribute will be a representation of the search query parameter name in URI. The benefit of the `TextFilter` is the possibility of writing complex queries.
570
+ In the case of the `TextFilter`, it is needed to write a whole new class inheriting from `TextFilter` with a custom implementation of the `queryset` method and the `parameter_name` attribute. This attribute will be a representation of the search query parameter name in URI. The benefit of the `TextFilter` is the possibility of writing complex queries.
540
571
 
541
572
  ```python
542
573
  from django.contrib import admin
@@ -571,7 +602,7 @@ class MyAdmin(ModelAdmin):
571
602
 
572
603
  Dropdown filters will display a select field with a list of options. Unfold contains two types of dropdowns: `ChoicesDropdownFilter` and `RelatedDropdownFilter`.
573
604
 
574
- The difference between them is that `ChoicesDropdownFilter` will collect a list of options based on the `choices` attribute of the model field so most commonly it will be used in combination with `CharField` with specified `choices`. On the other side, `RelatedDropdownFilter` needs a one-to-many or many-to-many foreign key to display options.
605
+ The difference between them is that `ChoicesDropdownFilter` will collect a list of options based on the `choices` attribute of the model field so most commonly it will be used in combination with `CharField` with specified `choices`. On the other hand, `RelatedDropdownFilter` needs a one-to-many or many-to-many foreign key to display options.
575
606
 
576
607
  **Note:** At the moment Unfold does not implement a dropdown with an autocomplete functionality, so it is important not to use dropdowns displaying large datasets.
577
608
 
@@ -880,6 +911,19 @@ class MyModelAdmin(ModelAdmin):
880
911
  )
881
912
  ```
882
913
 
914
+ Inlines can be grouped into tab navigation by specifying `tab` attribute in the inline class.
915
+
916
+ ```python
917
+ # admin.py
918
+
919
+ from unfold.admin import TabularInline
920
+
921
+
922
+ class MyInline(TabularInline):
923
+ model = User
924
+ tab = True
925
+ ```
926
+
883
927
  ## Third party packages
884
928
 
885
929
  ### django-celery-beat
@@ -949,11 +993,11 @@ class ClockedScheduleAdmin(BaseClockedScheduleAdmin, ModelAdmin):
949
993
 
950
994
  ### django-guardian
951
995
 
952
- Adding support for django-guardian is quote straightforward in Unfold, just add `unfold.contrib.guardian` to `INSTALLED_APPS` at the beggining of the file. This action will override all templates coming from the django-guardian. Please note that **Object permissions** link is available in top right dropdown navigation.
996
+ Adding support for django-guardian is quite straightforward in Unfold, just add `unfold.contrib.guardian` to `INSTALLED_APPS` at the beginning of the file. This action will override all templates coming from the django-guardian. Please note that **Object permissions** link is available in top right dropdown navigation.
953
997
 
954
998
  ### django-import-export
955
999
 
956
- 1. Add `unfold.contrib.import_export` to `INSTALLED_APPS` at the beggining of the file. This action will override all templates coming from the application.
1000
+ 1. Add `unfold.contrib.import_export` to `INSTALLED_APPS` at the beginning of the file. This action will override all templates coming from the application.
957
1001
  2. Change `import_form_class` and `export_form_class` in ModelAdmin which is inheriting from `ImportExportModelAdmin`. This chunk of code is responsible for adding proper styling to form elements.
958
1002
 
959
1003
  ```python
@@ -1025,7 +1069,7 @@ This application is supported in Unfold by default. It is not needed to add any
1025
1069
 
1026
1070
  ### django-simple-history
1027
1071
 
1028
- To make this application work, add `unfold.contrib.simple_history` into `settings.py` in `INSTALLED_APPS` variable before right after `unfold`. This app should ensure that templates coming from django-simple-history are overriden by Unfold.
1072
+ To make this application work, add `unfold.contrib.simple_history` into `settings.py` in `INSTALLED_APPS` variable before right after `unfold`. This app should ensure that templates coming from django-simple-history are overridden by Unfold.
1029
1073
 
1030
1074
  ## User Admin Form
1031
1075
 
@@ -1106,7 +1150,7 @@ npx tailwindcss -o your_project/static/css/styles.css --watch --minify
1106
1150
 
1107
1151
  ### Overriding template
1108
1152
 
1109
- Create `templates/admin/index.html` in your project and paste the base template below into it. By default, all your custom styles here are not compiled because CSS classes are located in your specific project. Here it is needed to set up the Tailwind for your project and all requried instructions are located in [Project Level Tailwind Stylesheet](#project-level-tailwind-stylesheet) chapter.
1153
+ Create `templates/admin/index.html` in your project and paste the base template below into it. By default, all your custom styles here are not compiled because CSS classes are located in your specific project. Here it is needed to set up the Tailwind for your project and all required instructions are located in [Project Level Tailwind Stylesheet](#project-level-tailwind-stylesheet) chapter.
1110
1154
 
1111
1155
  ```html+django
1112
1156
  {% extends 'unfold/layouts/base_simple.html' %}
@@ -1228,7 +1272,7 @@ Below you can find a more complex example which is using multiple components and
1228
1272
 
1229
1273
  ### Pre-commit
1230
1274
 
1231
- Before adding any source code, it is recommended to have pre-commit installed on your local computer to check for all potential issues when comitting the code.
1275
+ Before adding any source code, it is recommended to have pre-commit installed on your local computer to check for all potential issues when committing the code.
1232
1276
 
1233
1277
  ```bash
1234
1278
  pip install pre-commit
@@ -1248,7 +1292,7 @@ To add a new feature or fix the easiest approach is to use django-unfold in comb
1248
1292
 
1249
1293
  ### Compiling Tailwind
1250
1294
 
1251
- At the moment project contains package.json with all dependencies required to compile new CSS file. Tailwind configuration file is set to check all html, js and py files for Tailwind's classeses occurrences.
1295
+ At the moment project contains package.json with all dependencies required to compile new CSS file. Tailwind configuration file is set to check all html, js and py files for Tailwind's classes occurrences.
1252
1296
 
1253
1297
  ```bash
1254
1298
  npm install
@@ -1258,7 +1302,7 @@ npm run tailwind:watch # run after each change in code
1258
1302
  npm run tailwind:build # run once
1259
1303
  ```
1260
1304
 
1261
- Some components like datepickers, calendars or selectors in admin was not possible to style by overriding html templates so their default styles are overriden in **styles.css**.
1305
+ Some components like datepickers, calendars or selectors in admin was not possible to style by overriding html templates so their default styles are overridden in **styles.css**.
1262
1306
 
1263
1307
  **Note:** most of the custom styles located in style.css are created via `@apply some-tailwind-class;` as is not possible to manually add CSS class to element which are for example created via jQuery.
1264
1308
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "django-unfold"
3
- version = "0.29.1"
3
+ version = "0.31.0"
4
4
  description = "Modern Django admin theme for seamless interface development"
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  import copy
2
2
  from functools import update_wrapper
3
- from typing import Any, Callable, Dict, List, Optional, Tuple
3
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
4
4
 
5
5
  from django import forms
6
6
  from django.contrib.admin import ModelAdmin as BaseModelAdmin
@@ -148,6 +148,7 @@ class ModelAdminMixin:
148
148
  else:
149
149
  kwargs["widget"] = UnfoldAdminSelectWidget()
150
150
 
151
+ if "choices" not in kwargs:
151
152
  kwargs["choices"] = db_field.get_choices(
152
153
  include_blank=db_field.blank, blank_choice=[("", _("Select value"))]
153
154
  )
@@ -227,7 +228,10 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
227
228
  actions_submit_line = ()
228
229
  custom_urls = ()
229
230
  add_fieldsets = ()
231
+ list_horizontal_scrollbar_top = False
230
232
  list_filter_submit = False
233
+ list_fullwidth = False
234
+ compressed_fields = False
231
235
  readonly_preprocess_fields = {}
232
236
  checks_class = UnfoldModelAdminChecks
233
237
 
@@ -253,7 +257,10 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
253
257
  return super().get_fieldsets(request, obj)
254
258
 
255
259
  def _filter_unfold_actions_by_permissions(
256
- self, request: HttpRequest, actions: List[UnfoldAction]
260
+ self,
261
+ request: HttpRequest,
262
+ actions: List[UnfoldAction],
263
+ object_id: Optional[Union[int, str]] = None,
257
264
  ) -> List[UnfoldAction]:
258
265
  """Filter out any Unfold actions that the user doesn't have access to."""
259
266
  filtered_actions = []
@@ -261,12 +268,22 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
261
268
  if not hasattr(action.method, "allowed_permissions"):
262
269
  filtered_actions.append(action)
263
270
  continue
271
+
264
272
  permission_checks = (
265
273
  getattr(self, f"has_{permission}_permission")
266
274
  for permission in action.method.allowed_permissions
267
275
  )
268
- if any(has_permission(request) for has_permission in permission_checks):
269
- filtered_actions.append(action)
276
+
277
+ if object_id:
278
+ if any(
279
+ has_permission(request, object_id)
280
+ for has_permission in permission_checks
281
+ ):
282
+ filtered_actions.append(action)
283
+ else:
284
+ if any(has_permission(request) for has_permission in permission_checks):
285
+ filtered_actions.append(action)
286
+
270
287
  return filtered_actions
271
288
 
272
289
  def get_actions_list(self, request: HttpRequest) -> List[UnfoldAction]:
@@ -280,9 +297,11 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
280
297
  """
281
298
  return [self.get_unfold_action(action) for action in self.actions_list or []]
282
299
 
283
- def get_actions_detail(self, request: HttpRequest) -> List[UnfoldAction]:
300
+ def get_actions_detail(
301
+ self, request: HttpRequest, object_id: int
302
+ ) -> List[UnfoldAction]:
284
303
  return self._filter_unfold_actions_by_permissions(
285
- request, self._get_base_actions_detail()
304
+ request, self._get_base_actions_detail(), object_id
286
305
  )
287
306
 
288
307
  def _get_base_actions_detail(self) -> List[UnfoldAction]:
@@ -302,9 +321,11 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
302
321
  """
303
322
  return [self.get_unfold_action(action) for action in self.actions_row or []]
304
323
 
305
- def get_actions_submit_line(self, request: HttpRequest) -> List[UnfoldAction]:
324
+ def get_actions_submit_line(
325
+ self, request: HttpRequest, object_id: int
326
+ ) -> List[UnfoldAction]:
306
327
  return self._filter_unfold_actions_by_permissions(
307
- request, self._get_base_actions_submit_line()
328
+ request, self._get_base_actions_submit_line(), object_id
308
329
  )
309
330
 
310
331
  def _get_base_actions_submit_line(self) -> List[UnfoldAction]:
@@ -402,7 +423,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
402
423
 
403
424
  actions = []
404
425
  if object_id:
405
- for action in self.get_actions_detail(request):
426
+ for action in self.get_actions_detail(request, object_id):
406
427
  actions.append(
407
428
  {
408
429
  "title": action.description,
@@ -416,7 +437,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
416
437
 
417
438
  extra_context.update(
418
439
  {
419
- "actions_submit_line": self.get_actions_submit_line(request),
440
+ "actions_submit_line": self.get_actions_submit_line(request, object_id),
420
441
  "actions_detail": actions,
421
442
  }
422
443
  )
@@ -483,7 +504,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
483
504
  ) -> None:
484
505
  super().save_model(request, obj, form, change)
485
506
 
486
- for action in self.get_actions_submit_line(request):
507
+ for action in self.get_actions_submit_line(request, obj.pk):
487
508
  if action.action_name not in request.POST:
488
509
  continue
489
510
 
@@ -9,11 +9,11 @@
9
9
 
10
10
  {% if choice.min is not None and choice.max is not None and choice.step %}
11
11
  <div class="admin-numeric-filter-slider-tooltips">
12
- <span class="admin-numeric-filter-slider-tooltip-from border cursor-not-allowed flex flex-grow flex-row items-center mr-auto rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-400">
12
+ <span class="admin-numeric-filter-slider-tooltip-from border cursor-not-allowed flex flex-grow flex-row items-center mr-auto rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
13
13
  {{ choice.value_from }}
14
14
  </span>
15
15
 
16
- <span class="admin-numeric-filter-slider-tooltip-to border cursor-not-allowed flex flex-grow flex-row items-center rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-400">
16
+ <span class="admin-numeric-filter-slider-tooltip-to border cursor-not-allowed flex flex-grow flex-row items-center rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
17
17
  {{ choice.value_to }}
18
18
  </span>
19
19
  </div>
@@ -25,7 +25,7 @@
25
25
  {{ choice.form.as_p }}
26
26
  </div>
27
27
  {% else %}
28
- <div class="admin-numeric-filter-slider-error dark:text-gray-400">
28
+ <div class="admin-numeric-filter-slider-error dark:text-gray-300">
29
29
  <p>
30
30
  {% trans 'Not enough data.' %}
31
31
  </p>