django-unfold 0.26.0__tar.gz → 0.28.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (203) hide show
  1. {django_unfold-0.26.0 → django_unfold-0.28.0}/PKG-INFO +54 -5
  2. {django_unfold-0.26.0 → django_unfold-0.28.0}/README.md +53 -4
  3. {django_unfold-0.26.0 → django_unfold-0.28.0}/pyproject.toml +1 -1
  4. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/admin.py +6 -157
  5. django_unfold-0.28.0/src/unfold/contrib/forms/templates/unfold/forms/array.html +31 -0
  6. django_unfold-0.28.0/src/unfold/contrib/forms/widgets.py +97 -0
  7. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/forms.py +21 -7
  8. django_unfold-0.28.0/src/unfold/contrib/import_export/templates/admin/import_export/change_list_export.html +5 -0
  9. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/export.html +1 -1
  10. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/dataclasses.py +2 -0
  11. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/decorators.py +3 -0
  12. django_unfold-0.28.0/src/unfold/fields.py +208 -0
  13. django_unfold-0.28.0/src/unfold/static/unfold/css/styles.css +1 -0
  14. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/change_form.html +0 -2
  15. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/edit_inline/tabular.html +4 -6
  16. django_unfold-0.28.0/src/unfold/templates/admin/includes/fieldset.html +21 -0
  17. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/login.html +4 -0
  18. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/submit_line.html +1 -1
  19. django_unfold-0.28.0/src/unfold/templates/unfold/helpers/attrs.html +1 -0
  20. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/display_header.html +1 -1
  21. django_unfold-0.28.0/src/unfold/templates/unfold/helpers/field_readonly.html +7 -0
  22. django_unfold-0.28.0/src/unfold/templates/unfold/helpers/field_readonly_value.html +1 -0
  23. django_unfold-0.28.0/src/unfold/templates/unfold/helpers/fieldset_row.html +53 -0
  24. django_unfold-0.28.0/src/unfold/templates/unfold/helpers/search_results.html +13 -0
  25. django_unfold-0.28.0/src/unfold/templates/unfold/layouts/base.html +11 -0
  26. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/layouts/base_simple.html +7 -1
  27. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/clearable_file_input.html +1 -1
  28. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/clearable_file_input_small.html +1 -1
  29. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/foreign_key_raw_id.html +7 -13
  30. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/utils.py +21 -1
  31. django_unfold-0.28.0/src/unfold/views.py +34 -0
  32. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/widgets.py +12 -1
  33. django_unfold-0.26.0/src/unfold/contrib/forms/widgets.py +0 -42
  34. django_unfold-0.26.0/src/unfold/static/unfold/css/styles.css +0 -1
  35. django_unfold-0.26.0/src/unfold/templates/admin/includes/fieldset.html +0 -51
  36. django_unfold-0.26.0/src/unfold/templates/unfold/helpers/field_readonly.html +0 -9
  37. django_unfold-0.26.0/src/unfold/templates/unfold/helpers/search_results.html +0 -13
  38. django_unfold-0.26.0/src/unfold/views.py +0 -22
  39. {django_unfold-0.26.0 → django_unfold-0.28.0}/LICENSE.md +0 -0
  40. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/__init__.py +0 -0
  41. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/apps.py +0 -0
  42. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/checks.py +0 -0
  43. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/__init__.py +0 -0
  44. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/__init__.py +0 -0
  45. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/admin.py +0 -0
  46. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/apps.py +0 -0
  47. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/forms.py +0 -0
  48. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/static/unfold/filters/css/nouislider.min.css +0 -0
  49. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/static/unfold/filters/js/DateTimeShortcuts.js +0 -0
  50. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/static/unfold/filters/js/admin-numeric-filter.js +0 -0
  51. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/static/unfold/filters/js/nouislider.min.js +0 -0
  52. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/static/unfold/filters/js/wNumb.min.js +0 -0
  53. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_date_range.html +0 -0
  54. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_datetime_range.html +0 -0
  55. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_field.html +0 -0
  56. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_range.html +0 -0
  57. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_single.html +0 -0
  58. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +0 -0
  59. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/forms/__init__.py +0 -0
  60. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/forms/apps.py +0 -0
  61. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/forms/static/unfold/forms/css/trix.css +0 -0
  62. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/forms/static/unfold/forms/js/trix.config.js +0 -0
  63. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/forms/static/unfold/forms/js/trix.js +0 -0
  64. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html +0 -0
  65. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/forms/templates/unfold/forms/wysiwyg.html +0 -0
  66. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/guardian/__init__.py +0 -0
  67. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/guardian/apps.py +0 -0
  68. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/change_form.html +0 -0
  69. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/field.html +0 -0
  70. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage.html +0 -0
  71. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html +0 -0
  72. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html +0 -0
  73. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/guardian/templates/unfold/guardian/group_form.html +0 -0
  74. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/guardian/templates/unfold/guardian/user_form.html +0 -0
  75. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/__init__.py +0 -0
  76. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/apps.py +0 -0
  77. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/base.html +0 -0
  78. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_form.html +0 -0
  79. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_export_item.html +0 -0
  80. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_import_export.html +0 -0
  81. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_import_item.html +0 -0
  82. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/import.html +0 -0
  83. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_confirm.html +0 -0
  84. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_errors.html +0 -0
  85. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_form.html +0 -0
  86. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_preview.html +0 -0
  87. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_validation.html +0 -0
  88. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html +0 -0
  89. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/inlines/__init__.py +0 -0
  90. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/inlines/admin.py +0 -0
  91. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/inlines/apps.py +0 -0
  92. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/inlines/checks.py +0 -0
  93. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/inlines/forms.py +0 -0
  94. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/simple_history/__init__.py +0 -0
  95. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/simple_history/apps.py +0 -0
  96. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/simple_history/templates/simple_history/_object_history_list.html +0 -0
  97. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/simple_history/templates/simple_history/object_history.html +0 -0
  98. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/simple_history/templates/simple_history/object_history_form.html +0 -0
  99. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/contrib/simple_history/templates/simple_history/submit_line.html +0 -0
  100. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/exceptions.py +0 -0
  101. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/forms.py +0 -0
  102. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/settings.py +0 -0
  103. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/sites.py +0 -0
  104. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/css/simplebar.css +0 -0
  105. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/fonts/inter/Inter-Bold.woff2 +0 -0
  106. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/fonts/inter/Inter-Medium.woff2 +0 -0
  107. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/fonts/inter/Inter-Regular.woff2 +0 -0
  108. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/fonts/inter/Inter-SemiBold.woff2 +0 -0
  109. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/fonts/inter/styles.css +0 -0
  110. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/fonts/material-symbols/Material-Symbols-Outlined.woff2 +0 -0
  111. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/fonts/material-symbols/styles.css +0 -0
  112. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/js/alpine.js +0 -0
  113. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/js/alpine.persist.js +0 -0
  114. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/js/app.js +0 -0
  115. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/js/chart.js +0 -0
  116. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/js/htmx.js +0 -0
  117. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/static/unfold/js/simplebar.js +0 -0
  118. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/styles.css +0 -0
  119. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/actions.html +0 -0
  120. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/app_index.html +0 -0
  121. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/app_list.html +0 -0
  122. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/auth/user/add_form.html +0 -0
  123. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/auth/user/change_password.html +0 -0
  124. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/base.html +0 -0
  125. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/base_site.html +0 -0
  126. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/change_form_object_tools.html +0 -0
  127. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/change_list.html +0 -0
  128. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/change_list_object_tools.html +0 -0
  129. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/change_list_results.html +0 -0
  130. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/date_hierarchy.html +0 -0
  131. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/delete_confirmation.html +0 -0
  132. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/delete_selected_confirmation.html +0 -0
  133. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/edit_inline/stacked.html +0 -0
  134. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/filter.html +0 -0
  135. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/includes/object_delete_summary.html +0 -0
  136. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/index.html +0 -0
  137. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/nav_sidebar.html +0 -0
  138. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/object_history.html +0 -0
  139. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/pagination.html +0 -0
  140. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/admin/search_form.html +0 -0
  141. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/auth/widgets/read_only_password_hash.html +0 -0
  142. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/registration/logged_out.html +0 -0
  143. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/registration/password_change_done.html +0 -0
  144. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/registration/password_change_form.html +0 -0
  145. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/change_list_filter.html +0 -0
  146. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/components/card.html +0 -0
  147. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/components/chart/bar.html +0 -0
  148. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/components/chart/line.html +0 -0
  149. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/components/container.html +0 -0
  150. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/components/flex.html +0 -0
  151. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/components/navigation.html +0 -0
  152. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/components/progress.html +0 -0
  153. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/components/separator.html +0 -0
  154. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/components/text.html +0 -0
  155. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/components/title.html +0 -0
  156. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/account_links.html +0 -0
  157. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/actions_row.html +0 -0
  158. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/add_link.html +0 -0
  159. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/app_list.html +0 -0
  160. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/app_list_default.html +0 -0
  161. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/boolean.html +0 -0
  162. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/breadcrumb_item.html +0 -0
  163. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/display_label.html +0 -0
  164. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/field.html +0 -0
  165. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/fieldsets_tabs.html +0 -0
  166. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/form_errors.html +0 -0
  167. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/form_label.html +0 -0
  168. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/header.html +0 -0
  169. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/help_text.html +0 -0
  170. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/history.html +0 -0
  171. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/label.html +0 -0
  172. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/messages/error.html +0 -0
  173. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/messages/errornote.html +0 -0
  174. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/messages/info.html +0 -0
  175. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/messages.html +0 -0
  176. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/navigation.html +0 -0
  177. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/pagination_current_item.html +0 -0
  178. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/pagination_ellipsis.html +0 -0
  179. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/search.html +0 -0
  180. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/site_icon.html +0 -0
  181. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/site_logo.html +0 -0
  182. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/submit.html +0 -0
  183. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/tab_action.html +0 -0
  184. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/tab_list.html +0 -0
  185. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/theme_switch.html +0 -0
  186. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/userlinks.html +0 -0
  187. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/helpers/welcomemsg.html +0 -0
  188. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/layouts/skeleton.html +0 -0
  189. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/date.html +0 -0
  190. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/radio.html +0 -0
  191. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/radio_option.html +0 -0
  192. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/range.html +0 -0
  193. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/related_widget_wrapper.html +0 -0
  194. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/split_datetime.html +0 -0
  195. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/split_datetime_vertical.html +0 -0
  196. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/split_money.html +0 -0
  197. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/textarea.html +0 -0
  198. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/textarea_expandable.html +0 -0
  199. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templates/unfold/widgets/time.html +0 -0
  200. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templatetags/__init__.py +0 -0
  201. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templatetags/unfold.py +0 -0
  202. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/templatetags/unfold_list.py +0 -0
  203. {django_unfold-0.26.0 → django_unfold-0.28.0}/src/unfold/typing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-unfold
3
- Version: 0.26.0
3
+ Version: 0.28.0
4
4
  Summary: Modern Django admin theme for seamless interface development
5
5
  Home-page: https://unfoldadmin.com
6
6
  License: MIT
@@ -35,13 +35,13 @@ Description-Content-Type: text/markdown
35
35
 
36
36
  Unfold is theme for Django admin incorporating most common practises for building full-fledged admin areas. It is designed to work at the top of default administration provided by Django.
37
37
 
38
- - **Unfold:** demo site is available at [unfoldadmin.com](https://unfoldadmin.com)
38
+ - **Unfold:** demo site is available at [unfoldadmin.com](https://unfoldadmin.com?utm_medium=github&utm_source=unfold)
39
39
  - **Formula:** repository with demo implementation at [github.com/unfoldadmin/formula](https://github.com/unfoldadmin/formula)
40
40
  - **Turbo:** Django & Next.js boilerplate implementing Unfold at [github.com/unfoldadmin/turbo](https://github.com/unfoldadmin/turbo)
41
41
 
42
42
  ## Are you using Unfold and need a help?<!-- omit from toc -->
43
43
 
44
- Did you decide to start using Unfold but you don't have time to make the switch from native Django admin? [Get in touch with us](https://unfoldadmin.com/) and let's supercharge development by using our know-how.
44
+ Did you decide to start using Unfold but you don't have time to make the switch from native Django admin? [Get in touch with us](https://unfoldadmin.com/?utm_medium=github&utm_source=unfold) and let's supercharge development by using our know-how.
45
45
 
46
46
  ## Features <!-- omit from toc -->
47
47
 
@@ -52,15 +52,17 @@ Did you decide to start using Unfold but you don't have time to make the switch
52
52
  - **Dependencies:** completely based only on `django.contrib.admin`
53
53
  - **Actions:** multiple ways how to define actions within different parts of admin
54
54
  - **WYSIWYG:** built-in support for WYSIWYG (Trix)
55
+ - **Array widget:** built-in widget for `django.contrib.postgres.fields.ArrayField`
55
56
  - **Filters:** custom dropdown, numeric, datetime, and text fields
56
57
  - **Dashboard:** custom components for rapid dashboard development
57
58
  - **Model tabs:** define custom tab navigations for models
58
59
  - **Fieldset tabs:** merge several fielsets into tabs in change form
59
60
  - **Colors:** possibility to override default color scheme
61
+ - **Changeform modes:** display fields in changeform in compressed mode
60
62
  - **Third party packages:** default support for multiple popular applications
61
63
  - **Environment label**: distinguish between environments by displaying a label
62
64
  - **Nonrelated inlines**: displays nonrelated model as inline in changeform
63
- - **Parallel admin**: support for default admin in parallel with Unfold. [Admin migration guide](https://unfoldadmin.com/blog/migrating-django-admin-unfold/)
65
+ - **Parallel admin**: support for default admin in parallel with Unfold. [Admin migration guide](https://unfoldadmin.com/blog/migrating-django-admin-unfold/?utm_medium=github&utm_source=unfold)
64
66
  - **VS Code**: project configuration and development container is included
65
67
 
66
68
  ## Table of contents <!-- omit from toc -->
@@ -79,6 +81,7 @@ Did you decide to start using Unfold but you don't have time to make the switch
79
81
  - [Dropdown filters](#dropdown-filters)
80
82
  - [Numeric filters](#numeric-filters)
81
83
  - [Date/time filters](#datetime-filters)
84
+ - [Custom admin pages](#custom-admin-pages)
82
85
  - [Nonrelated inlines](#nonrelated-inlines)
83
86
  - [Display decorator](#display-decorator)
84
87
  - [Change form tabs](#change-form-tabs)
@@ -321,13 +324,17 @@ def permission_callback(request):
321
324
 
322
325
  from django import models
323
326
  from django.contrib import admin
327
+ from django.contrib.postgres.fields import ArrayField
324
328
  from django.db import models
325
329
  from unfold.admin import ModelAdmin
326
- from unfold.contrib.forms.widgets import WysiwygWidget
330
+ from unfold.contrib.forms.widgets import ArrayWidget, WysiwygWidget
327
331
 
328
332
 
329
333
  @admin.register(MyModel)
330
334
  class CustomAdminClass(ModelAdmin):
335
+ # Display fields in changeform in compressed mode
336
+ compressed_fields = True # Default: False
337
+
331
338
  # Preprocess content of readonly fields before render
332
339
  readonly_preprocess_fields = {
333
340
  "model_field_name": "html.unescape",
@@ -346,6 +353,9 @@ class CustomAdminClass(ModelAdmin):
346
353
  formfield_overrides = {
347
354
  models.TextField: {
348
355
  "widget": WysiwygWidget,
356
+ },
357
+ ArrayField: {
358
+ "widget": ArrayWidget,
349
359
  }
350
360
  }
351
361
  ```
@@ -636,6 +646,45 @@ class YourModelAdmin(ModelAdmin):
636
646
  )
637
647
  ```
638
648
 
649
+ ## Custom admin pages
650
+
651
+ By default, Unfold provides a basic view mixin which helps with creation of basic views which are part of Unfold UI. The implementation requires creation of class based view inheriting from `unfold.views.UnfoldModelAdminViewMixin`. It is important to add `title` and `permissions_required` properties.
652
+
653
+ ```python
654
+ # admin.py
655
+
656
+ from django.views.generic import TemplateView
657
+ from unfold.admin import ModelAdmin
658
+ from unfold.views import UnfoldModelAdminViewMixin
659
+
660
+
661
+ class MyClassBasedView(UnfoldModelAdminViewMixin, TemplateView):
662
+ title = "Custom Title" # required: custom page header title
663
+ permissions_required = () # required: tuple of permissions
664
+ template_name = "some/template/path.html"
665
+
666
+
667
+ class CustomAdmin(ModelAdmin):
668
+ def get_urls(self):
669
+ return super().get_urls() + [
670
+ path(
671
+ "custom-url-path",
672
+ MyClassBasedView.as_view(model_admin=self), # IMPORTANT: model_admin is required
673
+ name="custom_name"
674
+ ),
675
+ ]
676
+ ```
677
+
678
+ The template is straightforward, extend from `unfold/layouts/base.html` and the UI will display all Unfold components like header or sidebar with all menu items. Then all content needs to be located in `content` block.
679
+
680
+ ```django-html
681
+ {% extends "unfold/layouts/base.html" %}
682
+
683
+ {% block content %}
684
+ Content here
685
+ {% endblock %}
686
+ ```
687
+
639
688
  ## Nonrelated inlines
640
689
 
641
690
  To display inlines which are not related (no foreign key pointing at the main model) to the model instance in changeform, you can use nonrelated inlines which are included in `unfold.contrib.inlines` module. Make sure this module is included in `INSTALLED_APPS` in settings.py.
@@ -11,13 +11,13 @@
11
11
 
12
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.
13
13
 
14
- - **Unfold:** demo site is available at [unfoldadmin.com](https://unfoldadmin.com)
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)
16
16
  - **Turbo:** Django & Next.js boilerplate implementing Unfold at [github.com/unfoldadmin/turbo](https://github.com/unfoldadmin/turbo)
17
17
 
18
18
  ## Are you using Unfold and need a help?<!-- omit from toc -->
19
19
 
20
- Did you decide to start using Unfold but you don't have time to make the switch from native Django admin? [Get in touch with us](https://unfoldadmin.com/) and let's supercharge development by using our know-how.
20
+ Did you decide to start using Unfold but you don't have time to make the switch from native Django admin? [Get in touch with us](https://unfoldadmin.com/?utm_medium=github&utm_source=unfold) and let's supercharge development by using our know-how.
21
21
 
22
22
  ## Features <!-- omit from toc -->
23
23
 
@@ -28,15 +28,17 @@ Did you decide to start using Unfold but you don't have time to make the switch
28
28
  - **Dependencies:** completely based only on `django.contrib.admin`
29
29
  - **Actions:** multiple ways how to define actions within different parts of admin
30
30
  - **WYSIWYG:** built-in support for WYSIWYG (Trix)
31
+ - **Array widget:** built-in widget for `django.contrib.postgres.fields.ArrayField`
31
32
  - **Filters:** custom dropdown, numeric, datetime, and text fields
32
33
  - **Dashboard:** custom components for rapid dashboard development
33
34
  - **Model tabs:** define custom tab navigations for models
34
35
  - **Fieldset tabs:** merge several fielsets into tabs in change form
35
36
  - **Colors:** possibility to override default color scheme
37
+ - **Changeform modes:** display fields in changeform in compressed mode
36
38
  - **Third party packages:** default support for multiple popular applications
37
39
  - **Environment label**: distinguish between environments by displaying a label
38
40
  - **Nonrelated inlines**: displays nonrelated model as inline in changeform
39
- - **Parallel admin**: support for default admin in parallel with Unfold. [Admin migration guide](https://unfoldadmin.com/blog/migrating-django-admin-unfold/)
41
+ - **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)
40
42
  - **VS Code**: project configuration and development container is included
41
43
 
42
44
  ## Table of contents <!-- omit from toc -->
@@ -55,6 +57,7 @@ Did you decide to start using Unfold but you don't have time to make the switch
55
57
  - [Dropdown filters](#dropdown-filters)
56
58
  - [Numeric filters](#numeric-filters)
57
59
  - [Date/time filters](#datetime-filters)
60
+ - [Custom admin pages](#custom-admin-pages)
58
61
  - [Nonrelated inlines](#nonrelated-inlines)
59
62
  - [Display decorator](#display-decorator)
60
63
  - [Change form tabs](#change-form-tabs)
@@ -297,13 +300,17 @@ def permission_callback(request):
297
300
 
298
301
  from django import models
299
302
  from django.contrib import admin
303
+ from django.contrib.postgres.fields import ArrayField
300
304
  from django.db import models
301
305
  from unfold.admin import ModelAdmin
302
- from unfold.contrib.forms.widgets import WysiwygWidget
306
+ from unfold.contrib.forms.widgets import ArrayWidget, WysiwygWidget
303
307
 
304
308
 
305
309
  @admin.register(MyModel)
306
310
  class CustomAdminClass(ModelAdmin):
311
+ # Display fields in changeform in compressed mode
312
+ compressed_fields = True # Default: False
313
+
307
314
  # Preprocess content of readonly fields before render
308
315
  readonly_preprocess_fields = {
309
316
  "model_field_name": "html.unescape",
@@ -322,6 +329,9 @@ class CustomAdminClass(ModelAdmin):
322
329
  formfield_overrides = {
323
330
  models.TextField: {
324
331
  "widget": WysiwygWidget,
332
+ },
333
+ ArrayField: {
334
+ "widget": ArrayWidget,
325
335
  }
326
336
  }
327
337
  ```
@@ -612,6 +622,45 @@ class YourModelAdmin(ModelAdmin):
612
622
  )
613
623
  ```
614
624
 
625
+ ## Custom admin pages
626
+
627
+ By default, Unfold provides a basic view mixin which helps with creation of basic views which are part of Unfold UI. The implementation requires creation of class based view inheriting from `unfold.views.UnfoldModelAdminViewMixin`. It is important to add `title` and `permissions_required` properties.
628
+
629
+ ```python
630
+ # admin.py
631
+
632
+ from django.views.generic import TemplateView
633
+ from unfold.admin import ModelAdmin
634
+ from unfold.views import UnfoldModelAdminViewMixin
635
+
636
+
637
+ class MyClassBasedView(UnfoldModelAdminViewMixin, TemplateView):
638
+ title = "Custom Title" # required: custom page header title
639
+ permissions_required = () # required: tuple of permissions
640
+ template_name = "some/template/path.html"
641
+
642
+
643
+ class CustomAdmin(ModelAdmin):
644
+ def get_urls(self):
645
+ return super().get_urls() + [
646
+ path(
647
+ "custom-url-path",
648
+ MyClassBasedView.as_view(model_admin=self), # IMPORTANT: model_admin is required
649
+ name="custom_name"
650
+ ),
651
+ ]
652
+ ```
653
+
654
+ The template is straightforward, extend from `unfold/layouts/base.html` and the UI will display all Unfold components like header or sidebar with all menu items. Then all content needs to be located in `content` block.
655
+
656
+ ```django-html
657
+ {% extends "unfold/layouts/base.html" %}
658
+
659
+ {% block content %}
660
+ Content here
661
+ {% endblock %}
662
+ ```
663
+
615
664
  ## Nonrelated inlines
616
665
 
617
666
  To display inlines which are not related (no foreign key pointing at the main model) to the model instance in changeform, you can use nonrelated inlines which are included in `unfold.contrib.inlines` module. Make sure this module is included in `INSTALLED_APPS` in settings.py.
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "django-unfold"
3
- version = "0.26.0"
3
+ version = "0.28.0"
4
4
  description = "Modern Django admin theme for seamless interface development"
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -7,50 +7,30 @@ from django.contrib.admin import ModelAdmin as BaseModelAdmin
7
7
  from django.contrib.admin import StackedInline as BaseStackedInline
8
8
  from django.contrib.admin import TabularInline as BaseTabularInline
9
9
  from django.contrib.admin import display, helpers
10
- from django.contrib.admin.utils import lookup_field
11
10
  from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
12
- from django.core.exceptions import ObjectDoesNotExist
13
11
  from django.db import models
14
- from django.db.models import (
15
- BLANK_CHOICE_DASH,
16
- ForeignObjectRel,
17
- JSONField,
18
- ManyToManyRel,
19
- Model,
20
- OneToOneField,
21
- )
12
+ from django.db.models import BLANK_CHOICE_DASH, Model
22
13
  from django.db.models.fields import Field
23
14
  from django.db.models.fields.related import ForeignKey, ManyToManyField
24
15
  from django.forms import Form
25
16
  from django.forms.fields import TypedChoiceField
26
- from django.forms.models import (
27
- ModelChoiceField,
28
- ModelMultipleChoiceField,
29
- )
30
- from django.forms.utils import flatatt
17
+ from django.forms.models import ModelChoiceField, ModelMultipleChoiceField
31
18
  from django.forms.widgets import SelectMultiple
32
19
  from django.http import HttpRequest, HttpResponse
33
20
  from django.shortcuts import redirect
34
- from django.template.defaultfilters import linebreaksbr
35
21
  from django.template.response import TemplateResponse
36
22
  from django.urls import URLPattern, path, reverse
37
- from django.utils.html import conditional_escape, format_html
38
- from django.utils.module_loading import import_string
39
- from django.utils.safestring import SafeText, mark_safe
40
- from django.utils.text import capfirst
23
+ from django.utils.safestring import mark_safe
41
24
  from django.utils.translation import gettext_lazy as _
42
25
  from django.views import View
43
26
 
44
27
  from .checks import UnfoldModelAdminChecks
45
28
  from .dataclasses import UnfoldAction
46
29
  from .exceptions import UnfoldException
30
+ from .fields import UnfoldAdminField, UnfoldAdminReadonlyField
47
31
  from .forms import ActionForm
48
- from .settings import get_config
49
32
  from .typing import FieldsetsType
50
- from .utils import display_for_field
51
33
  from .widgets import (
52
- CHECKBOX_LABEL_CLASSES,
53
- LABEL_CLASSES,
54
34
  SELECT_CLASSES,
55
35
  UnfoldAdminBigIntegerFieldWidget,
56
36
  UnfoldAdminDecimalFieldWidget,
@@ -90,8 +70,6 @@ try:
90
70
  except ImportError:
91
71
  HAS_MONEY = False
92
72
 
93
- checkbox = UnfoldBooleanWidget({"class": "action-select"}, lambda value: False)
94
-
95
73
  FORMFIELD_OVERRIDES = {
96
74
  models.DateTimeField: {
97
75
  "form_class": forms.SplitDateTimeField,
@@ -141,140 +119,10 @@ FORMFIELD_OVERRIDES_INLINE.update(
141
119
  }
142
120
  )
143
121
 
144
-
145
- class UnfoldAdminField(helpers.AdminField):
146
- def label_tag(self) -> SafeText:
147
- classes = []
148
- if not self.field.field.widget.__class__.__name__.startswith(
149
- "Unfold"
150
- ) and not self.field.field.widget.template_name.startswith("unfold"):
151
- return super().label_tag()
152
-
153
- # TODO load config from current AdminSite (override Fieldline.__iter__ method)
154
- for lang, flag in get_config()["EXTENSIONS"]["modeltranslation"][
155
- "flags"
156
- ].items():
157
- if f"[{lang}]" in self.field.label:
158
- self.field.label = self.field.label.replace(f"[{lang}]", flag)
159
- break
160
-
161
- contents = conditional_escape(self.field.label)
162
-
163
- if self.is_checkbox:
164
- classes.append(" ".join(CHECKBOX_LABEL_CLASSES))
165
- else:
166
- classes.append(" ".join(LABEL_CLASSES))
167
-
168
- if self.field.field.required:
169
- classes.append("required")
170
-
171
- attrs = {"class": " ".join(classes)} if classes else {}
172
- required = mark_safe(' <span class="text-red-600">*</span>')
173
-
174
- return self.field.label_tag(
175
- contents=mark_safe(contents),
176
- attrs=attrs,
177
- label_suffix=required if self.field.field.required else "",
178
- )
179
-
122
+ checkbox = UnfoldBooleanWidget({"class": "action-select"}, lambda value: False)
180
123
 
181
124
  helpers.AdminField = UnfoldAdminField
182
125
 
183
-
184
- class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
185
- def label_tag(self) -> SafeText:
186
- if not isinstance(self.model_admin, ModelAdmin) and not isinstance(
187
- self.model_admin, ModelAdminMixin
188
- ):
189
- return super().label_tag()
190
-
191
- attrs = {
192
- "class": " ".join(LABEL_CLASSES + ["mb-2"]),
193
- }
194
-
195
- label = self.field["label"]
196
-
197
- return format_html(
198
- "<label{}>{}{}</label>",
199
- flatatt(attrs),
200
- capfirst(label),
201
- self.form.label_suffix,
202
- )
203
-
204
- def is_json(self) -> bool:
205
- field, obj, model_admin = (
206
- self.field["field"],
207
- self.form.instance,
208
- self.model_admin,
209
- )
210
-
211
- try:
212
- f, attr, value = lookup_field(field, obj, model_admin)
213
- except (AttributeError, ValueError, ObjectDoesNotExist):
214
- return False
215
-
216
- return isinstance(f, JSONField)
217
-
218
- def contents(self) -> str:
219
- contents = self._get_contents()
220
- contents = self._preprocess_field(contents)
221
- return contents
222
-
223
- def _get_contents(self) -> str:
224
- from django.contrib.admin.templatetags.admin_list import _boolean_icon
225
-
226
- field, obj, model_admin = (
227
- self.field["field"],
228
- self.form.instance,
229
- self.model_admin,
230
- )
231
- try:
232
- f, attr, value = lookup_field(field, obj, model_admin)
233
- except (AttributeError, ValueError, ObjectDoesNotExist):
234
- result_repr = self.empty_value_display
235
- else:
236
- if field in self.form.fields:
237
- widget = self.form[field].field.widget
238
- # This isn't elegant but suffices for contrib.auth's
239
- # ReadOnlyPasswordHashWidget.
240
- if getattr(widget, "read_only", False):
241
- return widget.render(field, value)
242
- if f is None:
243
- if getattr(attr, "boolean", False):
244
- result_repr = _boolean_icon(value)
245
- else:
246
- if hasattr(value, "__html__"):
247
- result_repr = value
248
- else:
249
- result_repr = linebreaksbr(value)
250
- else:
251
- if isinstance(f.remote_field, ManyToManyRel) and value is not None:
252
- result_repr = ", ".join(map(str, value.all()))
253
- elif (
254
- isinstance(f.remote_field, (ForeignObjectRel, OneToOneField))
255
- and value is not None
256
- ):
257
- result_repr = self.get_admin_url(f.remote_field, value)
258
- else:
259
- result_repr = display_for_field(value, f, self.empty_value_display)
260
- return conditional_escape(result_repr)
261
- result_repr = linebreaksbr(result_repr)
262
- return conditional_escape(result_repr)
263
-
264
- def _preprocess_field(self, contents: str) -> str:
265
- if (
266
- hasattr(self.model_admin, "readonly_preprocess_fields")
267
- and self.field["field"] in self.model_admin.readonly_preprocess_fields
268
- ):
269
- func = self.model_admin.readonly_preprocess_fields[self.field["field"]]
270
- if isinstance(func, str):
271
- contents = import_string(func)(contents)
272
- elif callable(func):
273
- contents = func(contents)
274
-
275
- return contents
276
-
277
-
278
126
  helpers.AdminReadonlyField = UnfoldAdminReadonlyField
279
127
 
280
128
 
@@ -616,6 +464,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
616
464
  method=method,
617
465
  description=self._get_action_description(method, action),
618
466
  path=self._get_action_url(method, action),
467
+ attrs=method.attrs if hasattr(method, "attrs") else None,
619
468
  )
620
469
 
621
470
  @staticmethod
@@ -0,0 +1,31 @@
1
+ {% load i18n %}
2
+
3
+ <div class="flex flex-col gap-4" x-data="{items: []}">
4
+ {% for subwidget in widget.subwidgets %}
5
+ <div class="flex flex-row">
6
+ {% with widget=subwidget %}
7
+ {% include widget.template_name %}
8
+ {% endwith %}
9
+
10
+ <a x-on:click="$el.parentElement.remove()" class="bg-white border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-red-600 text-sm w-9.5 dark:bg-gray-900 dark:border-gray-700 dark:text-red-500">
11
+ <span class="material-symbols-outlined text-sm">delete</span>
12
+ </a>
13
+ </div>
14
+ {% endfor %}
15
+
16
+ <template x-for="(item, index) in items" :key="item.key">
17
+ <div class="flex flex-row">
18
+ {% include template.template_name with widget=template %}
19
+
20
+ <a x-on:click="items.splice(index, 1)" class="bg-white border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-red-600 text-sm w-9.5 dark:bg-gray-900 dark:border-gray-700 dark:text-red-500">
21
+ <span class="material-symbols-outlined text-sm">delete</span>
22
+ </a>
23
+ </div>
24
+ </template>
25
+
26
+ <div class="flex flex-row">
27
+ <div x-on:click="items.push({ key: new Date().getTime()})" class="bg-primary-600 border border-transparent cursor-pointer font-medium inline-block px-3 py-2 rounded-md text-sm text-white w-full lg:w-auto">
28
+ {% trans "Add new item" %}
29
+ </div>
30
+ </div>
31
+ </div>
@@ -0,0 +1,97 @@
1
+ from typing import Any, Dict, List, Optional, Union
2
+
3
+ from django.core.validators import EMPTY_VALUES
4
+ from django.forms import MultiWidget, Widget
5
+ from django.http import QueryDict
6
+ from django.utils.datastructures import MultiValueDict
7
+ from unfold.widgets import PROSE_CLASSES, UnfoldAdminTextInputWidget
8
+
9
+ WYSIWYG_CLASSES = [
10
+ *PROSE_CLASSES,
11
+ "border",
12
+ "border-gray-200",
13
+ "border-t-0",
14
+ "group-[.errors]:border-red-600",
15
+ "max-w-none",
16
+ "p-4",
17
+ "rounded-b-md",
18
+ "rounded-t-none",
19
+ "text-gray-500",
20
+ "w-full",
21
+ "focus:outline-none",
22
+ "dark:border-gray-700",
23
+ "dark:text-gray-400",
24
+ "dark:group-[.errors]:border-red-500",
25
+ ]
26
+
27
+
28
+ class ArrayWidget(MultiWidget):
29
+ template_name = "unfold/forms/array.html"
30
+ widget_class = UnfoldAdminTextInputWidget
31
+
32
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
33
+ widgets = [self.widget_class]
34
+ super().__init__(widgets)
35
+
36
+ def get_context(self, name: str, value: str, attrs: Dict) -> Dict:
37
+ self._resolve_widgets(value)
38
+ context = super().get_context(name, value, attrs)
39
+ template_widget = UnfoldAdminTextInputWidget()
40
+ template_widget.name = name
41
+
42
+ context.update({"template": template_widget})
43
+ return context
44
+
45
+ def value_from_datadict(
46
+ self, data: QueryDict, files: MultiValueDict, name: str
47
+ ) -> List:
48
+ values = []
49
+
50
+ for item in data.getlist(name):
51
+ if item not in EMPTY_VALUES:
52
+ values.append(item)
53
+
54
+ return values
55
+
56
+ def value_omitted_from_data(
57
+ self, data: QueryDict, files: MultiValueDict, name: str
58
+ ) -> List:
59
+ return data.getlist(name) not in [[""], *EMPTY_VALUES]
60
+
61
+ def decompress(self, value: Union[str, List]) -> List:
62
+ if isinstance(value, List):
63
+ return value.split(",")
64
+
65
+ return []
66
+
67
+ def _resolve_widgets(self, value: Optional[Union[List, str]]) -> None:
68
+ if value is None:
69
+ value = []
70
+
71
+ elif isinstance(value, List):
72
+ self.widgets = [self.widget_class for item in value]
73
+ else:
74
+ self.widgets = [self.widget_class for item in value.split(",")]
75
+
76
+ self.widgets_names = ["" for i in range(len(self.widgets))]
77
+ self.widgets = [w() if isinstance(w, type) else w for w in self.widgets]
78
+
79
+
80
+ class WysiwygWidget(Widget):
81
+ template_name = "unfold/forms/wysiwyg.html"
82
+
83
+ class Media:
84
+ css = {"all": ("unfold/forms/css/trix.css",)}
85
+ js = (
86
+ "unfold/forms/js/trix.js",
87
+ "unfold/forms/js/trix.config.js",
88
+ )
89
+
90
+ def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
91
+ super().__init__(attrs)
92
+
93
+ self.attrs.update(
94
+ {
95
+ "class": " ".join(WYSIWYG_CLASSES),
96
+ }
97
+ )
@@ -15,23 +15,37 @@ class ImportForm(BaseImportForm):
15
15
  def __init__(self, *args, **kwargs):
16
16
  super().__init__(*args, **kwargs)
17
17
 
18
- self.fields["resource"].widget.attrs["class"] = " ".join(SELECT_CLASSES)
19
- self.fields["import_file"].widget = UnfoldAdminFileFieldWidget()
20
- self.fields["format"].widget.attrs["class"] = " ".join(SELECT_CLASSES)
18
+ self.fields["resource"].widget.attrs["class"] = " ".join(
19
+ [self.fields["resource"].widget.attrs.get("class", ""), *SELECT_CLASSES]
20
+ )
21
+ self.fields["import_file"].widget = UnfoldAdminFileFieldWidget(
22
+ attrs=self.fields["import_file"].widget.attrs
23
+ )
24
+ self.fields["format"].widget.attrs["class"] = " ".join(
25
+ [self.fields["format"].widget.attrs.get("class", ""), *SELECT_CLASSES]
26
+ )
21
27
 
22
28
 
23
29
  class ExportForm(BaseExportForm):
24
30
  def __init__(self, *args, **kwargs):
25
31
  super().__init__(*args, **kwargs)
26
- self.fields["resource"].widget.attrs["class"] = " ".join(SELECT_CLASSES)
27
- self.fields["format"].widget.attrs["class"] = " ".join(SELECT_CLASSES)
32
+ self.fields["resource"].widget.attrs["class"] = " ".join(
33
+ [self.fields["resource"].widget.attrs.get("class", ""), *SELECT_CLASSES]
34
+ )
35
+ self.fields["format"].widget.attrs["class"] = " ".join(
36
+ [self.fields["format"].widget.attrs.get("class", ""), *SELECT_CLASSES]
37
+ )
28
38
 
29
39
 
30
40
  class SelectableFieldsExportForm(BaseSelectableFieldsExportForm):
31
41
  def __init__(self, formats, resources, **kwargs):
32
42
  super().__init__(formats, resources, **kwargs)
33
- self.fields["resource"].widget.attrs["class"] = " ".join(SELECT_CLASSES)
34
- self.fields["format"].widget.attrs["class"] = " ".join(SELECT_CLASSES)
43
+ self.fields["resource"].widget.attrs["class"] = " ".join(
44
+ [self.fields["resource"].widget.attrs.get("class", ""), *SELECT_CLASSES]
45
+ )
46
+ self.fields["format"].widget.attrs["class"] = " ".join(
47
+ [self.fields["format"].widget.attrs.get("class", ""), *SELECT_CLASSES]
48
+ )
35
49
 
36
50
  for _key, field in self.fields.items():
37
51
  if isinstance(field, BooleanField):
@@ -0,0 +1,5 @@
1
+ {% extends "admin/import_export/change_list.html" %}
2
+
3
+ {% block actions-items %}
4
+ {% include "admin/import_export/change_list_export_item.html" %}
5
+ {% endblock %}
@@ -54,7 +54,7 @@
54
54
 
55
55
  <fieldset class="border border-gray-200 mb-4 rounded-md pt-3 px-3 shadow-sm dark:border-gray-800">
56
56
  {% for field in form.visible_fields %}
57
- <div {% if field.field.is_selectable_field %}class="selectable-field-export-row" resource-index="{{ field.field.resource_index }}"{% else %}class="form-row aligned"{% endif %}>
57
+ <div {% if field.field.is_selectable_field %}class="selectable-field-export-row" resource-index="{{ field.field.resource_index }}"{% endif %}>
58
58
  {% if field.field.initial_field %}
59
59
  <p class="block font-medium mb-2 text-gray-900 text-sm dark:text-gray-200">
60
60
  {% trans "This exporter will export the following fields" %}
@@ -1,4 +1,5 @@
1
1
  from dataclasses import dataclass
2
+ from typing import Dict, Optional
2
3
 
3
4
  from .typing import ActionFunction
4
5
 
@@ -9,3 +10,4 @@ class UnfoldAction:
9
10
  method: ActionFunction
10
11
  description: str
11
12
  path: str
13
+ attrs: Optional[Dict] = None