django-unfold 0.46.0__tar.gz → 0.48.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {django_unfold-0.46.0 → django_unfold-0.48.0}/PKG-INFO +5 -6
- {django_unfold-0.46.0 → django_unfold-0.48.0}/README.md +1 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/pyproject.toml +2 -4
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/admin.py +15 -16
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/checks.py +4 -4
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/components.py +5 -5
- django_unfold-0.48.0/src/unfold/contrib/filters/admin/__init__.py +43 -0
- django_unfold-0.48.0/src/unfold/contrib/filters/admin/autocomplete_filters.py +16 -0
- django_unfold-0.48.0/src/unfold/contrib/filters/admin/datetime_filters.py +212 -0
- django_unfold-0.48.0/src/unfold/contrib/filters/admin/dropdown_filters.py +100 -0
- django_unfold-0.48.0/src/unfold/contrib/filters/admin/mixins.py +146 -0
- django_unfold-0.48.0/src/unfold/contrib/filters/admin/numeric_filters.py +196 -0
- django_unfold-0.48.0/src/unfold/contrib/filters/admin/text_filters.py +65 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/admin.py +32 -32
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/forms.py +68 -17
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/forms/widgets.py +9 -9
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/inlines/checks.py +2 -4
- django_unfold-0.48.0/src/unfold/contrib/simple_history/templates/simple_history/object_history.html +35 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/simple_history/templates/simple_history/object_history_list.html +1 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/dataclasses.py +9 -2
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/decorators.py +4 -3
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/settings.py +4 -2
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/sites.py +176 -140
- django_unfold-0.48.0/src/unfold/static/unfold/css/styles.css +1 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/js/app.js +2 -2
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/app_index.html +1 -5
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/base_site.html +1 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/filter.html +1 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/index.html +1 -5
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/login.html +1 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/search_form.html +4 -2
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/account_links.html +1 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/actions_row.html +1 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/change_list_filter.html +2 -2
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/change_list_filter_actions.html +1 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/header_back_button.html +2 -2
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/language_switch.html +1 -1
- django_unfold-0.48.0/src/unfold/templates/unfold/helpers/navigation_header.html +23 -0
- django_unfold-0.48.0/src/unfold/templates/unfold/helpers/site_branding.html +9 -0
- django_unfold-0.48.0/src/unfold/templates/unfold/helpers/site_dropdown.html +19 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/site_icon.html +10 -2
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/tab_list.html +7 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/theme_switch.html +1 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/layouts/base.html +1 -5
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/layouts/skeleton.html +1 -1
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templatetags/unfold.py +55 -22
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templatetags/unfold_list.py +2 -2
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/typing.py +5 -4
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/utils.py +3 -2
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/views.py +2 -2
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/widgets.py +27 -27
- django_unfold-0.46.0/src/unfold/contrib/simple_history/templates/simple_history/object_history.html +0 -19
- django_unfold-0.46.0/src/unfold/static/unfold/css/styles.css +0 -1
- django_unfold-0.46.0/src/unfold/templates/unfold/helpers/navigation_header.html +0 -13
- {django_unfold-0.46.0 → django_unfold-0.48.0}/LICENSE.md +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/__init__.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/apps.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/__init__.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/__init__.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/apps.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/static/unfold/filters/css/nouislider.min.css +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/static/unfold/filters/js/DateTimeShortcuts.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/static/unfold/filters/js/admin-numeric-filter.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/static/unfold/filters/js/nouislider.min.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/static/unfold/filters/js/wNumb.min.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_date_range.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_datetime_range.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_field.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_range.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_single.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/forms/__init__.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/forms/apps.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/forms/static/unfold/forms/css/trix.css +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/forms/static/unfold/forms/js/trix.config.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/forms/static/unfold/forms/js/trix.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/forms/templates/unfold/forms/array.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/forms/templates/unfold/forms/wysiwyg.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/guardian/__init__.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/guardian/apps.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/change_form.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/field.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/guardian/templates/unfold/guardian/group_form.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/guardian/templates/unfold/guardian/user_form.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/__init__.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/apps.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/forms.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/base.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_form.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_export.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_export_item.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_import_export.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/change_list_import_item.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/export.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/import.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_confirm.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_errors.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_form.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_preview.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/import_validation.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/inlines/__init__.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/inlines/admin.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/inlines/apps.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/inlines/forms.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/simple_history/__init__.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/simple_history/apps.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/simple_history/templates/simple_history/object_history_form.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/contrib/simple_history/templates/simple_history/submit_line.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/exceptions.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/fields.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/forms.py +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/admin/js/admin/RelatedObjectLookups.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/admin/js/inlines.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/css/simplebar.css +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/fonts/inter/Inter-Bold.woff2 +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/fonts/inter/Inter-Medium.woff2 +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/fonts/inter/Inter-Regular.woff2 +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/fonts/inter/Inter-SemiBold.woff2 +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/fonts/inter/styles.css +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/fonts/material-symbols/Material-Symbols-Outlined.woff2 +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/fonts/material-symbols/styles.css +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/js/alpine.anchor.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/js/alpine.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/js/alpine.persist.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/js/alpine.resize.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/js/alpine.sort.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/js/chart.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/js/htmx.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/js/select2.init.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/static/unfold/js/simplebar.js +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/styles.css +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/actions.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/app_list.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/auth/user/add_form.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/auth/user/change_password.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/base.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/change_form.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/change_form_object_tools.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/change_list.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/change_list_object_tools.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/change_list_results.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/date_hierarchy.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/delete_confirmation.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/delete_selected_confirmation.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/edit_inline/stacked.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/edit_inline/tabular.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/includes/fieldset.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/includes/object_delete_summary.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/nav_sidebar.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/object_history.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/pagination.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/admin/submit_line.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/auth/widgets/read_only_password_hash.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/registration/logged_out.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/registration/password_change_done.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/registration/password_change_form.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/button.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/card.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/chart/bar.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/chart/cohort.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/chart/line.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/container.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/flex.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/icon.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/navigation.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/progress.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/separator.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/table.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/text.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/title.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/components/tracker.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/add_link.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/app_list.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/app_list_default.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/attrs.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/boolean.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/breadcrumb_item.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/change_list_actions.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/delete_submit_line.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/display_header.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/display_label.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_delete.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_error.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_field.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_heading.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_row.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/edit_inline/tabular_title.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/field.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/field_readonly.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/field_readonly_value.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/fieldset_row.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/fieldset_row_checkbox.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/fieldset_row_field.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/fieldsets_tabs.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/form_errors.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/form_label.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/header.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/help_text.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/history.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/label.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/messages/debug.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/messages/error.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/messages/errornote.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/messages/info.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/messages/success.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/messages/warning.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/messages.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/navigation.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/pagination_current_item.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/pagination_ellipsis.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/search.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/search_results.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/site_logo.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/submit.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/tab_action.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/userlinks.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/helpers/welcomemsg.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/layouts/base_simple.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/templatetags/preserve_changelist_filters.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/clearable_file_input.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/clearable_file_input_small.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/date.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/foreign_key_raw_id.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/radio.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/radio_option.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/range.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/related_widget_wrapper.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/split_datetime.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/split_datetime_vertical.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/split_money.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/textarea.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/textarea_expandable.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/time.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templates/unfold/widgets/url.html +0 -0
- {django_unfold-0.46.0 → django_unfold-0.48.0}/src/unfold/templatetags/__init__.py +0 -0
@@ -1,11 +1,10 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: django-unfold
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.48.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.
|
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.
|
3
|
+
version = "0.48.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.
|
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,
|
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:
|
275
|
+
actions: list[UnfoldAction],
|
276
276
|
object_id: Optional[Union[int, str]] = None,
|
277
|
-
) ->
|
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) ->
|
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) ->
|
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
|
-
) ->
|
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) ->
|
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) ->
|
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) ->
|
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
|
-
) ->
|
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) ->
|
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) ->
|
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) ->
|
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[
|
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[
|
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
|
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) ->
|
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) ->
|
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:
|
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,
|
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:
|
7
|
+
_registry: dict[str, type] = {}
|
8
8
|
|
9
9
|
@classmethod
|
10
|
-
def register_class(cls, component_cls:
|
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[
|
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:
|
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
|