django-unfold 0.46.0__tar.gz → 0.47.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (237) hide show
  1. {django_unfold-0.46.0 → django_unfold-0.47.0}/PKG-INFO +5 -6
  2. {django_unfold-0.46.0 → django_unfold-0.47.0}/README.md +1 -1
  3. {django_unfold-0.46.0 → django_unfold-0.47.0}/pyproject.toml +2 -4
  4. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/admin.py +15 -16
  5. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/checks.py +4 -4
  6. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/components.py +5 -5
  7. django_unfold-0.47.0/src/unfold/contrib/filters/admin/__init__.py +43 -0
  8. django_unfold-0.47.0/src/unfold/contrib/filters/admin/autocomplete_filters.py +16 -0
  9. django_unfold-0.47.0/src/unfold/contrib/filters/admin/datetime_filters.py +212 -0
  10. django_unfold-0.47.0/src/unfold/contrib/filters/admin/dropdown_filters.py +100 -0
  11. django_unfold-0.47.0/src/unfold/contrib/filters/admin/mixins.py +146 -0
  12. django_unfold-0.47.0/src/unfold/contrib/filters/admin/numeric_filters.py +196 -0
  13. django_unfold-0.47.0/src/unfold/contrib/filters/admin/text_filters.py +65 -0
  14. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/admin.py +32 -32
  15. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/forms.py +68 -17
  16. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/forms/widgets.py +9 -9
  17. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/inlines/checks.py +2 -4
  18. django_unfold-0.47.0/src/unfold/contrib/simple_history/templates/simple_history/object_history.html +35 -0
  19. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/simple_history/templates/simple_history/object_history_list.html +1 -1
  20. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/dataclasses.py +2 -2
  21. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/decorators.py +4 -3
  22. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/settings.py +2 -2
  23. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/sites.py +156 -140
  24. django_unfold-0.47.0/src/unfold/static/unfold/css/styles.css +1 -0
  25. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/js/app.js +2 -2
  26. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/filter.html +1 -1
  27. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/change_list_filter.html +2 -2
  28. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/change_list_filter_actions.html +1 -1
  29. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/header_back_button.html +2 -2
  30. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/tab_list.html +7 -1
  31. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/layouts/skeleton.html +1 -1
  32. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templatetags/unfold.py +55 -22
  33. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templatetags/unfold_list.py +2 -2
  34. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/typing.py +5 -4
  35. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/utils.py +3 -2
  36. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/views.py +2 -2
  37. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/widgets.py +27 -27
  38. django_unfold-0.46.0/src/unfold/contrib/simple_history/templates/simple_history/object_history.html +0 -19
  39. django_unfold-0.46.0/src/unfold/static/unfold/css/styles.css +0 -1
  40. {django_unfold-0.46.0 → django_unfold-0.47.0}/LICENSE.md +0 -0
  41. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/__init__.py +0 -0
  42. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/apps.py +0 -0
  43. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/__init__.py +0 -0
  44. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/__init__.py +0 -0
  45. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/apps.py +0 -0
  46. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/static/unfold/filters/css/nouislider.min.css +0 -0
  47. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/static/unfold/filters/js/DateTimeShortcuts.js +0 -0
  48. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/static/unfold/filters/js/admin-numeric-filter.js +0 -0
  49. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/static/unfold/filters/js/nouislider.min.js +0 -0
  50. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/static/unfold/filters/js/wNumb.min.js +0 -0
  51. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_date_range.html +0 -0
  52. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_datetime_range.html +0 -0
  53. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_field.html +0 -0
  54. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_range.html +0 -0
  55. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_single.html +0 -0
  56. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +0 -0
  57. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/forms/__init__.py +0 -0
  58. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/forms/apps.py +0 -0
  59. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/forms/static/unfold/forms/css/trix.css +0 -0
  60. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/forms/static/unfold/forms/js/trix.config.js +0 -0
  61. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/forms/static/unfold/forms/js/trix.js +0 -0
  62. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/forms/templates/unfold/forms/array.html +0 -0
  63. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html +0 -0
  64. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/forms/templates/unfold/forms/wysiwyg.html +0 -0
  65. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/guardian/__init__.py +0 -0
  66. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/guardian/apps.py +0 -0
  67. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/change_form.html +0 -0
  68. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/field.html +0 -0
  69. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage.html +0 -0
  70. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html +0 -0
  71. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html +0 -0
  72. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/guardian/templates/unfold/guardian/group_form.html +0 -0
  73. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/guardian/templates/unfold/guardian/user_form.html +0 -0
  74. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/__init__.py +0 -0
  75. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/apps.py +0 -0
  76. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/forms.py +0 -0
  77. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/base.html +0 -0
  78. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_form.html +0 -0
  79. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_export.html +0 -0
  80. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_export_item.html +0 -0
  81. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_import_export.html +0 -0
  82. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_import_item.html +0 -0
  83. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/export.html +0 -0
  84. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/import.html +0 -0
  85. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_confirm.html +0 -0
  86. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_errors.html +0 -0
  87. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_form.html +0 -0
  88. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_preview.html +0 -0
  89. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_validation.html +0 -0
  90. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html +0 -0
  91. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/inlines/__init__.py +0 -0
  92. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/inlines/admin.py +0 -0
  93. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/inlines/apps.py +0 -0
  94. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/inlines/forms.py +0 -0
  95. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/simple_history/__init__.py +0 -0
  96. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/simple_history/apps.py +0 -0
  97. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/simple_history/templates/simple_history/object_history_form.html +0 -0
  98. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/contrib/simple_history/templates/simple_history/submit_line.html +0 -0
  99. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/exceptions.py +0 -0
  100. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/fields.py +0 -0
  101. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/forms.py +0 -0
  102. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/admin/js/admin/RelatedObjectLookups.js +0 -0
  103. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/admin/js/inlines.js +0 -0
  104. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/css/simplebar.css +0 -0
  105. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/fonts/inter/Inter-Bold.woff2 +0 -0
  106. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/fonts/inter/Inter-Medium.woff2 +0 -0
  107. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/fonts/inter/Inter-Regular.woff2 +0 -0
  108. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/fonts/inter/Inter-SemiBold.woff2 +0 -0
  109. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/fonts/inter/styles.css +0 -0
  110. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/fonts/material-symbols/Material-Symbols-Outlined.woff2 +0 -0
  111. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/fonts/material-symbols/styles.css +0 -0
  112. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/js/alpine.anchor.js +0 -0
  113. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/js/alpine.js +0 -0
  114. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/js/alpine.persist.js +0 -0
  115. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/js/alpine.resize.js +0 -0
  116. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/js/alpine.sort.js +0 -0
  117. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/js/chart.js +0 -0
  118. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/js/htmx.js +0 -0
  119. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/js/select2.init.js +0 -0
  120. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/static/unfold/js/simplebar.js +0 -0
  121. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/styles.css +0 -0
  122. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/actions.html +0 -0
  123. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/app_index.html +0 -0
  124. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/app_list.html +0 -0
  125. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/auth/user/add_form.html +0 -0
  126. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/auth/user/change_password.html +0 -0
  127. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/base.html +0 -0
  128. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/base_site.html +0 -0
  129. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/change_form.html +0 -0
  130. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/change_form_object_tools.html +0 -0
  131. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/change_list.html +0 -0
  132. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/change_list_object_tools.html +0 -0
  133. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/change_list_results.html +0 -0
  134. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/date_hierarchy.html +0 -0
  135. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/delete_confirmation.html +0 -0
  136. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/delete_selected_confirmation.html +0 -0
  137. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/edit_inline/stacked.html +0 -0
  138. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/edit_inline/tabular.html +0 -0
  139. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/includes/fieldset.html +0 -0
  140. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/includes/object_delete_summary.html +0 -0
  141. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/index.html +0 -0
  142. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/login.html +0 -0
  143. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/nav_sidebar.html +0 -0
  144. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/object_history.html +0 -0
  145. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/pagination.html +0 -0
  146. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/search_form.html +0 -0
  147. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/admin/submit_line.html +0 -0
  148. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/auth/widgets/read_only_password_hash.html +0 -0
  149. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/registration/logged_out.html +0 -0
  150. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/registration/password_change_done.html +0 -0
  151. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/registration/password_change_form.html +0 -0
  152. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/button.html +0 -0
  153. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/card.html +0 -0
  154. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/chart/bar.html +0 -0
  155. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/chart/cohort.html +0 -0
  156. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/chart/line.html +0 -0
  157. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/container.html +0 -0
  158. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/flex.html +0 -0
  159. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/icon.html +0 -0
  160. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/navigation.html +0 -0
  161. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/progress.html +0 -0
  162. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/separator.html +0 -0
  163. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/table.html +0 -0
  164. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/text.html +0 -0
  165. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/title.html +0 -0
  166. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/components/tracker.html +0 -0
  167. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/account_links.html +0 -0
  168. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/actions_row.html +0 -0
  169. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/add_link.html +0 -0
  170. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/app_list.html +0 -0
  171. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/app_list_default.html +0 -0
  172. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/attrs.html +0 -0
  173. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/boolean.html +0 -0
  174. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/breadcrumb_item.html +0 -0
  175. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/change_list_actions.html +0 -0
  176. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/delete_submit_line.html +0 -0
  177. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/display_header.html +0 -0
  178. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/display_label.html +0 -0
  179. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_delete.html +0 -0
  180. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_error.html +0 -0
  181. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_field.html +0 -0
  182. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_heading.html +0 -0
  183. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_row.html +0 -0
  184. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_title.html +0 -0
  185. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/field.html +0 -0
  186. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/field_readonly.html +0 -0
  187. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/field_readonly_value.html +0 -0
  188. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/fieldset_row.html +0 -0
  189. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/fieldset_row_checkbox.html +0 -0
  190. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/fieldset_row_field.html +0 -0
  191. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/fieldsets_tabs.html +0 -0
  192. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/form_errors.html +0 -0
  193. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/form_label.html +0 -0
  194. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/header.html +0 -0
  195. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/help_text.html +0 -0
  196. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/history.html +0 -0
  197. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/label.html +0 -0
  198. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/language_switch.html +0 -0
  199. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/messages/debug.html +0 -0
  200. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/messages/error.html +0 -0
  201. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/messages/errornote.html +0 -0
  202. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/messages/info.html +0 -0
  203. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/messages/success.html +0 -0
  204. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/messages/warning.html +0 -0
  205. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/messages.html +0 -0
  206. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/navigation.html +0 -0
  207. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/navigation_header.html +0 -0
  208. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/pagination_current_item.html +0 -0
  209. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/pagination_ellipsis.html +0 -0
  210. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/search.html +0 -0
  211. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/search_results.html +0 -0
  212. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/site_icon.html +0 -0
  213. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/site_logo.html +0 -0
  214. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/submit.html +0 -0
  215. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/tab_action.html +0 -0
  216. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/theme_switch.html +0 -0
  217. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/userlinks.html +0 -0
  218. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/helpers/welcomemsg.html +0 -0
  219. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/layouts/base.html +0 -0
  220. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/layouts/base_simple.html +0 -0
  221. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/templatetags/preserve_changelist_filters.html +0 -0
  222. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/clearable_file_input.html +0 -0
  223. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/clearable_file_input_small.html +0 -0
  224. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/date.html +0 -0
  225. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/foreign_key_raw_id.html +0 -0
  226. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/radio.html +0 -0
  227. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/radio_option.html +0 -0
  228. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/range.html +0 -0
  229. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/related_widget_wrapper.html +0 -0
  230. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/split_datetime.html +0 -0
  231. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/split_datetime_vertical.html +0 -0
  232. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/split_money.html +0 -0
  233. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/textarea.html +0 -0
  234. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/textarea_expandable.html +0 -0
  235. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/time.html +0 -0
  236. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templates/unfold/widgets/url.html +0 -0
  237. {django_unfold-0.46.0 → django_unfold-0.47.0}/src/unfold/templatetags/__init__.py +0 -0
@@ -1,11 +1,10 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: django-unfold
3
- Version: 0.46.0
3
+ Version: 0.47.0
4
4
  Summary: Modern Django admin theme for seamless interface development
5
- Home-page: https://unfoldadmin.com
6
5
  License: MIT
7
6
  Keywords: django,admin,tailwind,theme
8
- Requires-Python: >=3.8
7
+ Requires-Python: >=3.9
9
8
  Classifier: Environment :: Web Environment
10
9
  Classifier: Framework :: Django
11
10
  Classifier: Intended Audience :: Developers
@@ -13,13 +12,13 @@ Classifier: License :: OSI Approved :: MIT License
13
12
  Classifier: Operating System :: OS Independent
14
13
  Classifier: Programming Language :: Python
15
14
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.8
17
15
  Classifier: Programming Language :: Python :: 3.9
18
16
  Classifier: Programming Language :: Python :: 3.10
19
17
  Classifier: Programming Language :: Python :: 3.11
20
18
  Classifier: Programming Language :: Python :: 3.12
21
19
  Classifier: Programming Language :: Python :: 3.13
22
20
  Requires-Dist: django (>=3.2)
21
+ Project-URL: Homepage, https://unfoldadmin.com
23
22
  Project-URL: Repository, https://github.com/unfoldadmin/django-unfold
24
23
  Description-Content-Type: text/markdown
25
24
 
@@ -52,7 +51,7 @@ Have you decided to start using Unfold but don’t have time to make the switch
52
51
  - **Sidebar:** Simplifies the creation of sidebar navigation with icons, collapsibles, and more.
53
52
  - **Dark mode:** Supports both light and dark mode versions.
54
53
  - **Actions:** Offers multiple ways to define actions within different parts of the admin interface.
55
- - **Filters:** Custom dropdowns, numeric, datetime, and text fields.
54
+ - **Filters:** Custom dropdowns, autocomplete, numeric, datetime, and text fields.
56
55
  - **Dashboard:** Includes helpers for creating custom dashboard pages.
57
56
  - **Components:** Reusable UI components such as cards, buttons, and charts.
58
57
  - **WYSIWYG widget:** Built-in support for WYSIWYG (Trix).
@@ -27,7 +27,7 @@ Have you decided to start using Unfold but don’t have time to make the switch
27
27
  - **Sidebar:** Simplifies the creation of sidebar navigation with icons, collapsibles, and more.
28
28
  - **Dark mode:** Supports both light and dark mode versions.
29
29
  - **Actions:** Offers multiple ways to define actions within different parts of the admin interface.
30
- - **Filters:** Custom dropdowns, numeric, datetime, and text fields.
30
+ - **Filters:** Custom dropdowns, autocomplete, numeric, datetime, and text fields.
31
31
  - **Dashboard:** Includes helpers for creating custom dashboard pages.
32
32
  - **Components:** Reusable UI components such as cards, buttons, and charts.
33
33
  - **WYSIWYG widget:** Built-in support for WYSIWYG (Trix).
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "django-unfold"
3
- version = "0.46.0"
3
+ version = "0.47.0"
4
4
  description = "Modern Django admin theme for seamless interface development"
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -11,7 +11,6 @@ packages = [{ include = "unfold", from = "src" }]
11
11
  keywords = ["django", "admin", "tailwind", "theme"]
12
12
  classifiers = [
13
13
  "Programming Language :: Python",
14
- "Programming Language :: Python :: 3.8",
15
14
  "Programming Language :: Python :: 3.9",
16
15
  "Programming Language :: Python :: 3.10",
17
16
  "Programming Language :: Python :: 3.11",
@@ -24,7 +23,7 @@ classifiers = [
24
23
  ]
25
24
 
26
25
  [tool.poetry.dependencies]
27
- python = ">=3.8"
26
+ python = ">=3.9"
28
27
  django = ">=3.2"
29
28
 
30
29
  [tool.poetry.group.test.dependencies]
@@ -39,7 +38,6 @@ python-semantic-release = "^8.7"
39
38
  [tool.ruff]
40
39
  fix = true
41
40
  line-length = 88
42
- target-version = "py38"
43
41
 
44
42
  [tool.ruff.lint]
45
43
  select = [
@@ -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, Union
3
+ from typing import Any, Callable, Optional, Union
4
4
 
5
5
  from django import forms
6
6
  from django.contrib.admin import ModelAdmin as BaseModelAdmin
@@ -272,9 +272,9 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
272
272
  def _filter_unfold_actions_by_permissions(
273
273
  self,
274
274
  request: HttpRequest,
275
- actions: List[UnfoldAction],
275
+ actions: list[UnfoldAction],
276
276
  object_id: Optional[Union[int, str]] = None,
277
- ) -> List[UnfoldAction]:
277
+ ) -> list[UnfoldAction]:
278
278
  """Filter out any Unfold actions that the user doesn't have access to."""
279
279
  filtered_actions = []
280
280
  for action in actions:
@@ -299,12 +299,12 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
299
299
 
300
300
  return filtered_actions
301
301
 
302
- def get_actions_list(self, request: HttpRequest) -> List[UnfoldAction]:
302
+ def get_actions_list(self, request: HttpRequest) -> list[UnfoldAction]:
303
303
  return self._filter_unfold_actions_by_permissions(
304
304
  request, self._get_base_actions_list()
305
305
  )
306
306
 
307
- def _get_base_actions_list(self) -> List[UnfoldAction]:
307
+ def _get_base_actions_list(self) -> list[UnfoldAction]:
308
308
  """
309
309
  Returns all available list global actions, prior to any filtering
310
310
  """
@@ -312,23 +312,23 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
312
312
 
313
313
  def get_actions_detail(
314
314
  self, request: HttpRequest, object_id: int
315
- ) -> List[UnfoldAction]:
315
+ ) -> list[UnfoldAction]:
316
316
  return self._filter_unfold_actions_by_permissions(
317
317
  request, self._get_base_actions_detail(), object_id
318
318
  )
319
319
 
320
- def _get_base_actions_detail(self) -> List[UnfoldAction]:
320
+ def _get_base_actions_detail(self) -> list[UnfoldAction]:
321
321
  """
322
322
  Returns all available detail actions, prior to any filtering
323
323
  """
324
324
  return [self.get_unfold_action(action) for action in self.actions_detail or []]
325
325
 
326
- def get_actions_row(self, request: HttpRequest) -> List[UnfoldAction]:
326
+ def get_actions_row(self, request: HttpRequest) -> list[UnfoldAction]:
327
327
  return self._filter_unfold_actions_by_permissions(
328
328
  request, self._get_base_actions_row()
329
329
  )
330
330
 
331
- def _get_base_actions_row(self) -> List[UnfoldAction]:
331
+ def _get_base_actions_row(self) -> list[UnfoldAction]:
332
332
  """
333
333
  Returns all available row actions, prior to any filtering
334
334
  """
@@ -336,12 +336,12 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
336
336
 
337
337
  def get_actions_submit_line(
338
338
  self, request: HttpRequest, object_id: int
339
- ) -> List[UnfoldAction]:
339
+ ) -> list[UnfoldAction]:
340
340
  return self._filter_unfold_actions_by_permissions(
341
341
  request, self._get_base_actions_submit_line(), object_id
342
342
  )
343
343
 
344
- def _get_base_actions_submit_line(self) -> List[UnfoldAction]:
344
+ def _get_base_actions_submit_line(self) -> list[UnfoldAction]:
345
345
  """
346
346
  Returns all available submit row actions, prior to any filtering
347
347
  """
@@ -349,7 +349,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
349
349
  self.get_unfold_action(action) for action in self.actions_submit_line or []
350
350
  ]
351
351
 
352
- def get_custom_urls(self) -> Tuple[Tuple[str, str, View], ...]:
352
+ def get_custom_urls(self) -> tuple[tuple[str, str, View], ...]:
353
353
  """
354
354
  Method to get custom views for ModelAdmin with their urls
355
355
 
@@ -358,7 +358,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
358
358
  """
359
359
  return () if self.custom_urls is None else self.custom_urls
360
360
 
361
- def get_urls(self) -> List[URLPattern]:
361
+ def get_urls(self) -> list[URLPattern]:
362
362
  urls = super().get_urls()
363
363
 
364
364
  def wrap(view):
@@ -409,7 +409,6 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
409
409
  )
410
410
 
411
411
  def _path_from_custom_url(self, custom_url) -> URLPattern:
412
- # TODO: wrap()
413
412
  return path(
414
413
  custom_url[0],
415
414
  self.admin_site.admin_view(custom_url[2]),
@@ -422,7 +421,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
422
421
  request: HttpRequest,
423
422
  object_id: Optional[str] = None,
424
423
  form_url: str = "",
425
- extra_context: Optional[Dict[str, bool]] = None,
424
+ extra_context: Optional[dict[str, bool]] = None,
426
425
  ) -> Any:
427
426
  if extra_context is None:
428
427
  extra_context = {}
@@ -451,7 +450,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
451
450
  return super().changeform_view(request, object_id, form_url, extra_context)
452
451
 
453
452
  def changelist_view(
454
- self, request: HttpRequest, extra_context: Optional[Dict[str, str]] = None
453
+ self, request: HttpRequest, extra_context: Optional[dict[str, str]] = None
455
454
  ) -> TemplateResponse:
456
455
  if extra_context is None:
457
456
  extra_context = {}
@@ -1,4 +1,4 @@
1
- from typing import Any, List
1
+ from typing import Any
2
2
 
3
3
  from django.contrib.admin.checks import ModelAdminChecks
4
4
  from django.contrib.admin.options import BaseModelAdmin
@@ -8,18 +8,18 @@ from .dataclasses import UnfoldAction
8
8
 
9
9
 
10
10
  class UnfoldModelAdminChecks(ModelAdminChecks):
11
- def check(self, admin_obj: BaseModelAdmin, **kwargs) -> List[checks.Error]:
11
+ def check(self, admin_obj: BaseModelAdmin, **kwargs) -> list[checks.Error]:
12
12
  return [
13
13
  *super().check(admin_obj, **kwargs),
14
14
  *self._check_unfold_action_permission_methods(admin_obj),
15
15
  ]
16
16
 
17
- def _check_unfold_action_permission_methods(self, obj: Any) -> List[checks.Error]:
17
+ def _check_unfold_action_permission_methods(self, obj: Any) -> list[checks.Error]:
18
18
  """
19
19
  Actions with an allowed_permission attribute require the ModelAdmin to
20
20
  implement a has_<perm>_permission() method for each permission.
21
21
  """
22
- actions: List[UnfoldAction] = [
22
+ actions: list[UnfoldAction] = [
23
23
  *obj._get_base_actions_list(),
24
24
  *obj._get_base_actions_detail(),
25
25
  *obj._get_base_actions_row(),
@@ -1,13 +1,13 @@
1
- from typing import Any, Dict, Optional, Type
1
+ from typing import Any, Optional
2
2
 
3
3
  from django.http import HttpRequest
4
4
 
5
5
 
6
6
  class ComponentRegistry:
7
- _registry: Dict[str, Type] = {}
7
+ _registry: dict[str, type] = {}
8
8
 
9
9
  @classmethod
10
- def register_class(cls, component_cls: Type) -> None:
10
+ def register_class(cls, component_cls: type) -> None:
11
11
  if not issubclass(component_cls, BaseComponent):
12
12
  raise ValueError(
13
13
  f"Class '{component_cls.__name__}' must inherit from BaseComponent."
@@ -21,7 +21,7 @@ class ComponentRegistry:
21
21
  cls._registry[class_name] = component_cls
22
22
 
23
23
  @classmethod
24
- def get_class(cls, class_name: str) -> Optional[Type]:
24
+ def get_class(cls, class_name: str) -> Optional[type]:
25
25
  return cls._registry.get(class_name)
26
26
 
27
27
  @classmethod
@@ -34,7 +34,7 @@ class ComponentRegistry:
34
34
  return component_cls(**kwargs)
35
35
 
36
36
 
37
- def register_component(cls: Type) -> Type:
37
+ def register_component(cls: type) -> type:
38
38
  ComponentRegistry.register_class(cls)
39
39
  return cls
40
40
 
@@ -0,0 +1,43 @@
1
+ from unfold.contrib.filters.admin.autocomplete_filters import (
2
+ AutocompleteSelectFilter,
3
+ AutocompleteSelectMultipleFilter,
4
+ )
5
+ from unfold.contrib.filters.admin.datetime_filters import (
6
+ RangeDateFilter,
7
+ RangeDateTimeFilter,
8
+ )
9
+ from unfold.contrib.filters.admin.dropdown_filters import (
10
+ ChoicesDropdownFilter,
11
+ DropdownFilter,
12
+ MultipleChoicesDropdownFilter,
13
+ MultipleDropdownFilter,
14
+ MultipleRelatedDropdownFilter,
15
+ RelatedDropdownFilter,
16
+ )
17
+ from unfold.contrib.filters.admin.numeric_filters import (
18
+ RangeNumericFilter,
19
+ RangeNumericListFilter,
20
+ SingleNumericFilter,
21
+ SliderNumericFilter,
22
+ )
23
+ from unfold.contrib.filters.admin.text_filters import FieldTextFilter, TextFilter
24
+
25
+ __all__ = [
26
+ "ChoicesDropdownFilter",
27
+ "MultipleChoicesDropdownFilter",
28
+ "DropdownFilter",
29
+ "RelatedDropdownFilter",
30
+ "MultipleDropdownFilter",
31
+ "MultipleRelatedDropdownFilter",
32
+ "FieldTextFilter",
33
+ "TextFilter",
34
+ "RangeDateFilter",
35
+ "RangeDateFilter",
36
+ "RangeDateTimeFilter",
37
+ "SingleNumericFilter",
38
+ "RangeNumericFilter",
39
+ "RangeNumericListFilter",
40
+ "SliderNumericFilter",
41
+ "AutocompleteSelectFilter",
42
+ "AutocompleteSelectMultipleFilter",
43
+ ]
@@ -0,0 +1,16 @@
1
+ from unfold.contrib.filters.admin.dropdown_filters import (
2
+ MultipleRelatedDropdownFilter,
3
+ RelatedDropdownFilter,
4
+ )
5
+ from unfold.contrib.filters.admin.mixins import AutocompleteMixin
6
+ from unfold.contrib.filters.forms import AutocompleteDropdownForm
7
+
8
+
9
+ class AutocompleteSelectFilter(AutocompleteMixin, RelatedDropdownFilter):
10
+ form_class = AutocompleteDropdownForm
11
+
12
+
13
+ class AutocompleteSelectMultipleFilter(
14
+ AutocompleteMixin, MultipleRelatedDropdownFilter
15
+ ):
16
+ form_class = AutocompleteDropdownForm
@@ -0,0 +1,212 @@
1
+ from typing import Any
2
+
3
+ from django.contrib import admin
4
+ from django.contrib.admin.options import ModelAdmin
5
+ from django.contrib.admin.views.main import ChangeList
6
+ from django.core.validators import EMPTY_VALUES
7
+ from django.db.models import Model, QuerySet
8
+ from django.db.models.fields import (
9
+ DateField,
10
+ DateTimeField,
11
+ Field,
12
+ )
13
+ from django.forms import ValidationError
14
+ from django.http import HttpRequest
15
+
16
+ from unfold.contrib.filters.forms import (
17
+ RangeDateForm,
18
+ RangeDateTimeForm,
19
+ )
20
+ from unfold.utils import parse_date_str, parse_datetime_str
21
+
22
+
23
+ class RangeDateFilter(admin.FieldListFilter):
24
+ request = None
25
+ parameter_name = None
26
+ form_class = RangeDateForm
27
+ template = "unfold/filters/filters_date_range.html"
28
+
29
+ def __init__(
30
+ self,
31
+ field: Field,
32
+ request: HttpRequest,
33
+ params: dict[str, str],
34
+ model: type[Model],
35
+ model_admin: ModelAdmin,
36
+ field_path: str,
37
+ ) -> None:
38
+ super().__init__(field, request, params, model, model_admin, field_path)
39
+ if not isinstance(field, DateField):
40
+ raise TypeError(
41
+ f"Class {type(self.field)} is not supported for {self.__class__.__name__}."
42
+ )
43
+
44
+ self.request = request
45
+ if self.parameter_name is None:
46
+ self.parameter_name = self.field_path
47
+
48
+ if self.parameter_name + "_from" in params:
49
+ value = params.pop(self.field_path + "_from")
50
+ value = value[0] if isinstance(value, list) else value
51
+
52
+ if value not in EMPTY_VALUES:
53
+ self.used_parameters[self.field_path + "_from"] = value
54
+
55
+ if self.parameter_name + "_to" in params:
56
+ value = params.pop(self.field_path + "_to")
57
+ value = value[0] if isinstance(value, list) else value
58
+
59
+ if value not in EMPTY_VALUES:
60
+ self.used_parameters[self.field_path + "_to"] = value
61
+
62
+ def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet:
63
+ filters = {}
64
+
65
+ value_from = self.used_parameters.get(self.parameter_name + "_from")
66
+ if value_from not in EMPTY_VALUES:
67
+ filters.update({self.parameter_name + "__gte": parse_date_str(value_from)})
68
+
69
+ value_to = self.used_parameters.get(self.parameter_name + "_to")
70
+ if value_to not in EMPTY_VALUES:
71
+ filters.update({self.parameter_name + "__lte": parse_date_str(value_to)})
72
+
73
+ try:
74
+ return queryset.filter(**filters)
75
+ except (ValueError, ValidationError):
76
+ return None
77
+
78
+ def expected_parameters(self) -> list[str]:
79
+ return [
80
+ f"{self.parameter_name}_from",
81
+ f"{self.parameter_name}_to",
82
+ ]
83
+
84
+ def choices(self, changelist: ChangeList) -> tuple[dict[str, Any], ...]:
85
+ return (
86
+ {
87
+ "request": self.request,
88
+ "parameter_name": self.parameter_name,
89
+ "form": self.form_class(
90
+ name=self.parameter_name,
91
+ data={
92
+ self.parameter_name + "_from": self.used_parameters.get(
93
+ self.parameter_name + "_from", None
94
+ ),
95
+ self.parameter_name + "_to": self.used_parameters.get(
96
+ self.parameter_name + "_to", None
97
+ ),
98
+ },
99
+ ),
100
+ },
101
+ )
102
+
103
+
104
+ class RangeDateTimeFilter(admin.FieldListFilter):
105
+ request = None
106
+ parameter_name = None
107
+ template = "unfold/filters/filters_datetime_range.html"
108
+ form_class = RangeDateTimeForm
109
+
110
+ def __init__(
111
+ self,
112
+ field: Field,
113
+ request: HttpRequest,
114
+ params: dict[str, str],
115
+ model: type[Model],
116
+ model_admin: ModelAdmin,
117
+ field_path: str,
118
+ ) -> None:
119
+ super().__init__(field, request, params, model, model_admin, field_path)
120
+ if not isinstance(field, DateTimeField):
121
+ raise TypeError(
122
+ f"Class {type(self.field)} is not supported for {self.__class__.__name__}."
123
+ )
124
+
125
+ self.request = request
126
+ if self.parameter_name is None:
127
+ self.parameter_name = self.field_path
128
+
129
+ if self.parameter_name + "_from_0" in params:
130
+ value = params.pop(self.field_path + "_from_0")
131
+ value = value[0] if isinstance(value, list) else value
132
+ self.used_parameters[self.field_path + "_from_0"] = value
133
+
134
+ if self.parameter_name + "_from_1" in params:
135
+ value = params.pop(self.field_path + "_from_1")
136
+ value = value[0] if isinstance(value, list) else value
137
+ self.used_parameters[self.field_path + "_from_1"] = value
138
+
139
+ if self.parameter_name + "_to_0" in params:
140
+ value = params.pop(self.field_path + "_to_0")
141
+ value = value[0] if isinstance(value, list) else value
142
+ self.used_parameters[self.field_path + "_to_0"] = value
143
+
144
+ if self.parameter_name + "_to_1" in params:
145
+ value = params.pop(self.field_path + "_to_1")
146
+ value = value[0] if isinstance(value, list) else value
147
+ self.used_parameters[self.field_path + "_to_1"] = value
148
+
149
+ def expected_parameters(self) -> list[str]:
150
+ return [
151
+ f"{self.parameter_name}_from_0",
152
+ f"{self.parameter_name}_from_1",
153
+ f"{self.parameter_name}_to_0",
154
+ f"{self.parameter_name}_to_1",
155
+ ]
156
+
157
+ def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet:
158
+ filters = {}
159
+
160
+ date_value_from = self.used_parameters.get(self.parameter_name + "_from_0")
161
+ time_value_from = self.used_parameters.get(self.parameter_name + "_from_1")
162
+
163
+ date_value_to = self.used_parameters.get(self.parameter_name + "_to_0")
164
+ time_value_to = self.used_parameters.get(self.parameter_name + "_to_1")
165
+
166
+ if date_value_from not in EMPTY_VALUES and time_value_from not in EMPTY_VALUES:
167
+ filters.update(
168
+ {
169
+ f"{self.parameter_name}__gte": parse_datetime_str(
170
+ f"{date_value_from} {time_value_from}"
171
+ ),
172
+ }
173
+ )
174
+
175
+ if date_value_to not in EMPTY_VALUES and time_value_to not in EMPTY_VALUES:
176
+ filters.update(
177
+ {
178
+ f"{self.parameter_name}__lte": parse_datetime_str(
179
+ f"{date_value_to} {time_value_to}"
180
+ ),
181
+ }
182
+ )
183
+
184
+ try:
185
+ return queryset.filter(**filters)
186
+ except (ValueError, ValidationError):
187
+ return None
188
+
189
+ def choices(self, changelist: ChangeList) -> tuple[dict[str, Any], ...]:
190
+ return (
191
+ {
192
+ "request": self.request,
193
+ "parameter_name": self.parameter_name,
194
+ "form": self.form_class(
195
+ name=self.parameter_name,
196
+ data={
197
+ self.parameter_name + "_from_0": self.used_parameters.get(
198
+ self.parameter_name + "_from_0"
199
+ ),
200
+ self.parameter_name + "_from_1": self.used_parameters.get(
201
+ self.parameter_name + "_from_1"
202
+ ),
203
+ self.parameter_name + "_to_0": self.used_parameters.get(
204
+ self.parameter_name + "_to_0"
205
+ ),
206
+ self.parameter_name + "_to_1": self.used_parameters.get(
207
+ self.parameter_name + "_to_1"
208
+ ),
209
+ },
210
+ ),
211
+ },
212
+ )
@@ -0,0 +1,100 @@
1
+ from collections.abc import Generator
2
+ from typing import Any
3
+
4
+ from django.contrib import admin
5
+ from django.contrib.admin.views.main import ChangeList
6
+ from django.db.models import Field, Model
7
+ from django.http import HttpRequest
8
+ from django.utils.translation import gettext_lazy as _
9
+
10
+ from unfold.admin import ModelAdmin
11
+ from unfold.contrib.filters.admin.mixins import (
12
+ DropdownMixin,
13
+ MultiValueMixin,
14
+ ValueMixin,
15
+ )
16
+ from unfold.contrib.filters.forms import DropdownForm
17
+
18
+
19
+ class DropdownFilter(admin.SimpleListFilter):
20
+ template = "unfold/filters/filters_field.html"
21
+ form_class = DropdownForm
22
+ all_option = ["", _("All")]
23
+
24
+ def choices(self, changelist: ChangeList) -> tuple[dict[str, Any], ...]:
25
+ return (
26
+ {
27
+ "form": self.form_class(
28
+ label=_("By %(filter_title)s") % {"filter_title": self.title},
29
+ name=self.parameter_name,
30
+ choices=[self.all_option, *self.lookup_choices],
31
+ data={self.parameter_name: self.value()},
32
+ multiple=self.multiple if hasattr(self, "multiple") else False,
33
+ ),
34
+ },
35
+ )
36
+
37
+
38
+ class MultipleDropdownFilter(DropdownFilter):
39
+ multiple = True
40
+
41
+ def __init__(
42
+ self,
43
+ request: HttpRequest,
44
+ params: dict[str, Any],
45
+ model: type[Model],
46
+ model_admin: ModelAdmin,
47
+ ) -> None:
48
+ self.request = request
49
+ super().__init__(request, params, model, model_admin)
50
+
51
+ def value(self) -> list[Any]:
52
+ return self.request.GET.getlist(self.parameter_name)
53
+
54
+
55
+ class ChoicesDropdownFilter(ValueMixin, DropdownMixin, admin.ChoicesFieldListFilter):
56
+ def choices(self, changelist: ChangeList) -> Generator[dict[str, Any], None, None]:
57
+ choices = [self.all_option, *self.field.flatchoices]
58
+
59
+ yield {
60
+ "form": self.form_class(
61
+ label=_("By %(filter_title)s") % {"filter_title": self.title},
62
+ name=self.lookup_kwarg,
63
+ choices=choices,
64
+ data={self.lookup_kwarg: self.value()},
65
+ multiple=self.multiple if hasattr(self, "multiple") else False,
66
+ ),
67
+ }
68
+
69
+
70
+ class MultipleChoicesDropdownFilter(MultiValueMixin, ChoicesDropdownFilter):
71
+ multiple = True
72
+
73
+
74
+ class RelatedDropdownFilter(ValueMixin, DropdownMixin, admin.RelatedFieldListFilter):
75
+ def __init__(
76
+ self,
77
+ field: Field,
78
+ request: HttpRequest,
79
+ params: dict[str, str],
80
+ model: type[Model],
81
+ model_admin: ModelAdmin,
82
+ field_path: str,
83
+ ) -> None:
84
+ super().__init__(field, request, params, model, model_admin, field_path)
85
+ self.model_admin = model_admin
86
+
87
+ def choices(self, changelist: ChangeList) -> Generator[dict[str, Any], None, None]:
88
+ yield {
89
+ "form": self.form_class(
90
+ label=_("By %(filter_title)s") % {"filter_title": self.title},
91
+ name=self.lookup_kwarg,
92
+ choices=[self.all_option, *self.lookup_choices],
93
+ data={self.lookup_kwarg: self.value()},
94
+ multiple=self.multiple if hasattr(self, "multiple") else False,
95
+ ),
96
+ }
97
+
98
+
99
+ class MultipleRelatedDropdownFilter(MultiValueMixin, RelatedDropdownFilter):
100
+ multiple = True