django-smartbase-admin 0.2.47__py3-none-any.whl → 1.0.38__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_smartbase_admin/actions/admin_action_list.py +80 -51
- django_smartbase_admin/actions/advanced_filters.py +55 -20
- django_smartbase_admin/admin/admin_base.py +477 -89
- django_smartbase_admin/admin/site.py +104 -34
- django_smartbase_admin/admin/widgets.py +598 -26
- django_smartbase_admin/apps.py +2 -0
- django_smartbase_admin/engine/actions.py +34 -16
- django_smartbase_admin/engine/admin_base_view.py +253 -115
- django_smartbase_admin/engine/configuration.py +186 -4
- django_smartbase_admin/engine/const.py +7 -0
- django_smartbase_admin/engine/dashboard.py +44 -23
- django_smartbase_admin/engine/fake_inline.py +44 -7
- django_smartbase_admin/engine/field.py +54 -10
- django_smartbase_admin/engine/field_formatter.py +32 -9
- django_smartbase_admin/engine/filter_widgets.py +356 -21
- django_smartbase_admin/engine/menu_item.py +8 -5
- django_smartbase_admin/engine/modal_view.py +12 -7
- django_smartbase_admin/engine/request.py +2 -0
- django_smartbase_admin/integration/__init__.py +0 -0
- django_smartbase_admin/integration/django_cms.py +43 -0
- django_smartbase_admin/locale/sk/LC_MESSAGES/django.mo +0 -0
- django_smartbase_admin/locale/sk/LC_MESSAGES/django.po +268 -37
- django_smartbase_admin/migrations/0005_sbadminuserconfiguration.py +26 -0
- django_smartbase_admin/migrations/0006_alter_sbadminuserconfiguration_color_scheme.py +18 -0
- django_smartbase_admin/models.py +22 -0
- django_smartbase_admin/monkeypatch/admin_readonly_field_monkeypatch.py +96 -0
- django_smartbase_admin/monkeypatch/fake_inline_monkeypatch.py +1 -1
- django_smartbase_admin/querysets.py +3 -0
- django_smartbase_admin/services/configuration.py +30 -0
- django_smartbase_admin/services/thread_local.py +6 -19
- django_smartbase_admin/services/views.py +82 -27
- django_smartbase_admin/services/xlsx_export.py +6 -0
- django_smartbase_admin/static/sb_admin/build/tailwind.config.js +1 -0
- django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/colors.js +4 -0
- django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/spacing.js +1 -0
- django_smartbase_admin/static/sb_admin/build/webpack.common.js +11 -8
- django_smartbase_admin/static/sb_admin/css/ckeditor/ckeditor_content_dark.css +208 -0
- django_smartbase_admin/static/sb_admin/css/coloris/coloris.min.css +1 -0
- django_smartbase_admin/static/sb_admin/dist/calendar.js +1 -0
- django_smartbase_admin/static/sb_admin/dist/calendar_style.css +1 -0
- django_smartbase_admin/static/sb_admin/dist/calendar_style.js +0 -0
- django_smartbase_admin/static/sb_admin/dist/chart.js +1 -1
- django_smartbase_admin/static/sb_admin/dist/main.js +1 -1
- django_smartbase_admin/static/sb_admin/dist/main_style.css +1 -1
- django_smartbase_admin/static/sb_admin/dist/table.js +1 -1
- django_smartbase_admin/static/sb_admin/dist/table.js.LICENSE.txt +9 -0
- django_smartbase_admin/static/sb_admin/dist/tree_widget.js +1 -0
- django_smartbase_admin/static/sb_admin/dist/tree_widget_style.css +1 -0
- django_smartbase_admin/static/sb_admin/dist/tree_widget_style.js +0 -0
- django_smartbase_admin/static/sb_admin/fancytree/jquery.fancytree-all-deps.min.js +1 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-csv.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-doc.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-docx.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-other.svg +13 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-pdf.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-ppt.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-xls.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-xlsx.svg +11 -0
- django_smartbase_admin/static/sb_admin/images/file_types/file-zip.svg +18 -0
- django_smartbase_admin/static/sb_admin/images/flags/de-at.png +0 -0
- django_smartbase_admin/static/sb_admin/images/flags/de-ch.png +0 -0
- django_smartbase_admin/static/sb_admin/images/logo_light.svg +21 -0
- django_smartbase_admin/static/sb_admin/js/coloris/coloris.min.js +6 -0
- django_smartbase_admin/static/sb_admin/js/fullcalendar.min.js +14804 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Bolt-one.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Calendar.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Caution.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Electric-drill.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Fire-extinguisher.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Gas.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Lightning-fill.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Moon.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Phone-telephone.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Printer.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Pull.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Sun-one.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Time.svg +3 -0
- django_smartbase_admin/static/sb_admin/src/css/_base.css +5 -1
- django_smartbase_admin/static/sb_admin/src/css/_colors.css +257 -82
- django_smartbase_admin/static/sb_admin/src/css/_components.css +66 -13
- django_smartbase_admin/static/sb_admin/src/css/_datepicker.css +8 -1
- django_smartbase_admin/static/sb_admin/src/css/_filer.css +60 -0
- django_smartbase_admin/static/sb_admin/src/css/_inlines.css +51 -10
- django_smartbase_admin/static/sb_admin/src/css/_tabulator.css +8 -2
- django_smartbase_admin/static/sb_admin/src/css/calendar.css +162 -0
- django_smartbase_admin/static/sb_admin/src/css/components/_button.css +41 -1
- django_smartbase_admin/static/sb_admin/src/css/components/_dropdown.css +26 -8
- django_smartbase_admin/static/sb_admin/src/css/components/_input.css +62 -20
- django_smartbase_admin/static/sb_admin/src/css/components/_modal.css +1 -1
- django_smartbase_admin/static/sb_admin/src/css/components/_query-builder.css +21 -2
- django_smartbase_admin/static/sb_admin/src/css/components/_toggle.css +12 -1
- django_smartbase_admin/static/sb_admin/src/css/components/_tooltip.css +8 -22
- django_smartbase_admin/static/sb_admin/src/css/style.css +17 -0
- django_smartbase_admin/static/sb_admin/src/css/tree_widget.css +411 -0
- django_smartbase_admin/static/sb_admin/src/js/autocomplete.js +63 -5
- django_smartbase_admin/static/sb_admin/src/js/calendar.js +56 -0
- django_smartbase_admin/static/sb_admin/src/js/chart.js +8 -22
- django_smartbase_admin/static/sb_admin/src/js/choices.js +18 -8
- django_smartbase_admin/static/sb_admin/src/js/datepicker.js +97 -336
- django_smartbase_admin/static/sb_admin/src/js/datepicker_plugins.js +357 -0
- django_smartbase_admin/static/sb_admin/src/js/main.js +307 -26
- django_smartbase_admin/static/sb_admin/src/js/multiselect.js +50 -41
- django_smartbase_admin/static/sb_admin/src/js/range.js +3 -2
- django_smartbase_admin/static/sb_admin/src/js/sb_ajax_params_tabulator_modifier.js +21 -0
- django_smartbase_admin/static/sb_admin/src/js/table.js +38 -13
- django_smartbase_admin/static/sb_admin/src/js/table_modules/advanced_filter_module.js +43 -20
- django_smartbase_admin/static/sb_admin/src/js/table_modules/data_edit_module.js +8 -10
- django_smartbase_admin/static/sb_admin/src/js/table_modules/filter_module.js +3 -3
- django_smartbase_admin/static/sb_admin/src/js/table_modules/header_tabs_module.js +11 -11
- django_smartbase_admin/static/sb_admin/src/js/table_modules/selection_module.js +28 -8
- django_smartbase_admin/static/sb_admin/src/js/table_modules/table_params_module.js +6 -0
- django_smartbase_admin/static/sb_admin/src/js/table_modules/views_module.js +19 -3
- django_smartbase_admin/static/sb_admin/src/js/tree_widget.js +406 -0
- django_smartbase_admin/static/sb_admin/src/js/utils.js +56 -21
- django_smartbase_admin/templates/sb_admin/actions/change_form.html +169 -117
- django_smartbase_admin/templates/sb_admin/actions/dashboard.html +2 -2
- django_smartbase_admin/templates/sb_admin/actions/delete_selected_confirmation.html +56 -32
- django_smartbase_admin/templates/sb_admin/actions/list.html +79 -42
- django_smartbase_admin/templates/sb_admin/actions/object_history.html +2 -2
- django_smartbase_admin/templates/sb_admin/actions/partials/action_link.html +14 -0
- django_smartbase_admin/templates/sb_admin/actions/partials/selected_rows_actions.html +2 -2
- django_smartbase_admin/templates/sb_admin/actions/partials/tabulator_header_v2.html +2 -2
- django_smartbase_admin/templates/sb_admin/actions/tree_list.html +63 -0
- django_smartbase_admin/templates/sb_admin/authentification/login_base.html +5 -1
- django_smartbase_admin/templates/sb_admin/components/columns.html +1 -1
- django_smartbase_admin/templates/sb_admin/components/filters_v2.html +99 -85
- django_smartbase_admin/templates/sb_admin/config/view.html +0 -1
- django_smartbase_admin/templates/sb_admin/dashboard/calendar_widget.html +69 -0
- django_smartbase_admin/templates/sb_admin/dashboard/chart_widget.html +21 -2
- django_smartbase_admin/templates/sb_admin/dashboard/list_widget.html +6 -0
- django_smartbase_admin/templates/sb_admin/dashboard/widget_base.html +1 -1
- django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/date_field.html +18 -8
- django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/multiple_choice_field.html +1 -1
- django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/tree_select_filter.html +2 -0
- django_smartbase_admin/templates/sb_admin/filter_widgets/date_field.html +18 -4
- django_smartbase_admin/templates/sb_admin/filter_widgets/multiple_choice_field.html +14 -0
- django_smartbase_admin/templates/sb_admin/filter_widgets/partials/clear.html +10 -5
- django_smartbase_admin/templates/sb_admin/filter_widgets/radio_choice_field.html +2 -2
- django_smartbase_admin/templates/sb_admin/filter_widgets/tree_select_filter.html +16 -0
- django_smartbase_admin/templates/sb_admin/includes/change_form_title.html +3 -1
- django_smartbase_admin/templates/sb_admin/includes/components.html +5 -1
- django_smartbase_admin/templates/sb_admin/includes/inline_fieldset.html +48 -39
- django_smartbase_admin/templates/sb_admin/includes/notifications.html +2 -1
- django_smartbase_admin/templates/sb_admin/includes/readonly_boolean_field.html +9 -0
- django_smartbase_admin/templates/sb_admin/includes/readonly_field.html +12 -0
- django_smartbase_admin/templates/sb_admin/includes/table_inline_delete_button.html +4 -5
- django_smartbase_admin/templates/sb_admin/inlines/stacked_inline.html +68 -40
- django_smartbase_admin/templates/sb_admin/inlines/table_inline.html +78 -36
- django_smartbase_admin/templates/sb_admin/integrations/filer/folder_list.html +18 -0
- django_smartbase_admin/templates/sb_admin/navigation.html +166 -158
- django_smartbase_admin/templates/sb_admin/partials/modal/modal_content.html +2 -6
- django_smartbase_admin/templates/sb_admin/sb_admin_base.html +49 -4
- django_smartbase_admin/templates/sb_admin/sb_admin_base_no_sidebar.html +35 -11
- django_smartbase_admin/templates/sb_admin/sb_admin_js_trans.html +3 -0
- django_smartbase_admin/templates/sb_admin/sprites/sb_admin.svg +1 -1
- django_smartbase_admin/templates/sb_admin/tailwind_whitelist.html +6 -3
- django_smartbase_admin/templates/sb_admin/widgets/array.html +0 -1
- django_smartbase_admin/templates/sb_admin/widgets/attributes.html +68 -0
- django_smartbase_admin/templates/sb_admin/widgets/autocomplete.html +13 -2
- django_smartbase_admin/templates/sb_admin/widgets/{checkbox_select.html → checkbox_dropdown.html} +2 -2
- django_smartbase_admin/templates/sb_admin/widgets/checkbox_group.html +15 -0
- django_smartbase_admin/templates/sb_admin/widgets/clearable_file_input.html +2 -2
- django_smartbase_admin/templates/sb_admin/widgets/color_field.html +30 -0
- django_smartbase_admin/templates/sb_admin/widgets/date.html +8 -1
- django_smartbase_admin/templates/sb_admin/widgets/filer_file.html +84 -0
- django_smartbase_admin/templates/sb_admin/widgets/html_read_only.html +1 -0
- django_smartbase_admin/templates/sb_admin/widgets/includes/related_item_buttons.html +38 -0
- django_smartbase_admin/templates/sb_admin/widgets/multiwidget.html +1 -1
- django_smartbase_admin/templates/sb_admin/widgets/radio.html +3 -2
- django_smartbase_admin/templates/sb_admin/widgets/radio_dropdown.html +30 -0
- django_smartbase_admin/templates/sb_admin/widgets/read_only_password_hash.html +3 -0
- django_smartbase_admin/templates/sb_admin/widgets/time.html +8 -1
- django_smartbase_admin/templates/sb_admin/widgets/toggle.html +1 -1
- django_smartbase_admin/templates/sb_admin/widgets/tree_base.html +59 -0
- django_smartbase_admin/templates/sb_admin/widgets/tree_select.html +24 -0
- django_smartbase_admin/templates/sb_admin/widgets/tree_select_inline.html +12 -0
- django_smartbase_admin/templatetags/sb_admin_tags.py +163 -4
- django_smartbase_admin/utils.py +22 -3
- django_smartbase_admin/views/dashboard_view.py +6 -0
- django_smartbase_admin/views/global_filter_view.py +8 -2
- django_smartbase_admin/views/translations_view.py +12 -5
- django_smartbase_admin/views/user_config_view.py +52 -0
- django_smartbase_admin-1.0.38.dist-info/METADATA +166 -0
- {django_smartbase_admin-0.2.47.dist-info → django_smartbase_admin-1.0.38.dist-info}/RECORD +186 -121
- {django_smartbase_admin-0.2.47.dist-info → django_smartbase_admin-1.0.38.dist-info}/WHEEL +1 -1
- django_smartbase_admin/templates/sb_admin/integrations/sorting/change_list.html +0 -401
- django_smartbase_admin-0.2.47.dist-info/METADATA +0 -25
- {django_smartbase_admin-0.2.47.dist-info → django_smartbase_admin-1.0.38.dist-info}/LICENSE.md +0 -0
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
1
3
|
from django.template.defaultfilters import date, time
|
|
4
|
+
from django.utils.safestring import mark_safe
|
|
2
5
|
from django.utils.translation import gettext_lazy as _
|
|
3
6
|
from django.utils import timezone
|
|
4
7
|
|
|
5
8
|
|
|
9
|
+
class BadgeType(Enum):
|
|
10
|
+
SUCCESS = "positive"
|
|
11
|
+
NOTICE = "notice"
|
|
12
|
+
WARNING = "warning"
|
|
13
|
+
ERROR = "negative"
|
|
14
|
+
|
|
15
|
+
|
|
6
16
|
def datetime_formatter(object_id, value):
|
|
7
17
|
if value is None:
|
|
8
18
|
return None
|
|
@@ -12,27 +22,38 @@ def datetime_formatter(object_id, value):
|
|
|
12
22
|
|
|
13
23
|
def datetime_formatter_with_format(date_format=None, time_format=None):
|
|
14
24
|
def inner_formatter(object_id, value):
|
|
25
|
+
if value is None:
|
|
26
|
+
return None
|
|
15
27
|
value = timezone.localtime(value)
|
|
16
|
-
|
|
28
|
+
return_value = ""
|
|
29
|
+
if date_format:
|
|
30
|
+
return_value += date(value, date_format)
|
|
31
|
+
if time_format:
|
|
32
|
+
if return_value:
|
|
33
|
+
return_value += " "
|
|
34
|
+
return_value += time(value, time_format)
|
|
35
|
+
return return_value
|
|
17
36
|
|
|
18
37
|
return inner_formatter
|
|
19
38
|
|
|
20
39
|
|
|
21
40
|
def boolean_formatter(object_id, value):
|
|
22
41
|
if value:
|
|
23
|
-
return
|
|
24
|
-
|
|
42
|
+
return mark_safe(
|
|
43
|
+
f'<span class="badge badge-simple badge-positive">{_("Yes")}</span>'
|
|
44
|
+
)
|
|
45
|
+
return mark_safe(f'<span class="badge badge-simple badge-neutral">{_("No")}</span>')
|
|
25
46
|
|
|
26
47
|
|
|
27
|
-
def format_array(value_list, separator=""):
|
|
48
|
+
def format_array(value_list, separator="", badge_type: BadgeType = BadgeType.NOTICE):
|
|
28
49
|
result = ""
|
|
29
50
|
if not value_list:
|
|
30
51
|
return result
|
|
31
52
|
for value in value_list:
|
|
32
53
|
if not value:
|
|
33
54
|
continue
|
|
34
|
-
result += f'<span class="badge badge-simple badge-
|
|
35
|
-
return result
|
|
55
|
+
result += f'<span class="badge badge-simple badge-{badge_type.value} mr-4">{value}</span>{separator}'
|
|
56
|
+
return mark_safe(result)
|
|
36
57
|
|
|
37
58
|
|
|
38
59
|
def array_badge_formatter(object_id, value_list):
|
|
@@ -40,12 +61,14 @@ def array_badge_formatter(object_id, value_list):
|
|
|
40
61
|
|
|
41
62
|
|
|
42
63
|
def newline_separated_array_badge_formatter(object_id, value_list):
|
|
43
|
-
return format_array(value_list, separator="<br>")
|
|
64
|
+
return mark_safe(f'<div>{format_array(value_list, separator="<br>")}</div>')
|
|
44
65
|
|
|
45
66
|
|
|
46
67
|
def rich_text_formatter(object_id, value):
|
|
47
|
-
return
|
|
68
|
+
return mark_safe(
|
|
69
|
+
f'<div style="max-width: 500px; white-space: normal;">{value}</div>'
|
|
70
|
+
)
|
|
48
71
|
|
|
49
72
|
|
|
50
73
|
def link_formatter(object_id, value):
|
|
51
|
-
return f'<a href="{value}">{value}</a>'
|
|
74
|
+
return mark_safe(f'<a href="{value}">{value}</a>')
|
|
@@ -2,10 +2,11 @@ import json
|
|
|
2
2
|
from datetime import datetime, timedelta
|
|
3
3
|
|
|
4
4
|
from django.core.exceptions import ImproperlyConfigured
|
|
5
|
-
from django.
|
|
5
|
+
from django.contrib.postgres.fields import ArrayField
|
|
6
|
+
from django.db.models import Q, fields, FilteredRelation, Count
|
|
6
7
|
from django.http import JsonResponse
|
|
7
8
|
from django.utils import timezone
|
|
8
|
-
from django.utils.translation import gettext_lazy as _
|
|
9
|
+
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
|
9
10
|
|
|
10
11
|
from django_smartbase_admin.actions.advanced_filters import (
|
|
11
12
|
AllOperators,
|
|
@@ -20,6 +21,7 @@ from django_smartbase_admin.engine.const import (
|
|
|
20
21
|
Action,
|
|
21
22
|
AUTOCOMPLETE_PAGE_NUM,
|
|
22
23
|
AUTOCOMPLETE_FORWARD_NAME,
|
|
24
|
+
SELECT_ALL_KEYWORD,
|
|
23
25
|
)
|
|
24
26
|
from django_smartbase_admin.services.translations import SBAdminTranslationsService
|
|
25
27
|
from django_smartbase_admin.services.views import SBAdminViewService
|
|
@@ -44,6 +46,22 @@ class AutocompleteParseMixin:
|
|
|
44
46
|
value = input_value
|
|
45
47
|
return value
|
|
46
48
|
|
|
49
|
+
def parse_is_create_from_input(self, request, input_value):
|
|
50
|
+
try:
|
|
51
|
+
input_value = json.loads(input_value)
|
|
52
|
+
except:
|
|
53
|
+
pass
|
|
54
|
+
if isinstance(input_value, list):
|
|
55
|
+
value = []
|
|
56
|
+
for data in input_value:
|
|
57
|
+
if type(data) is dict:
|
|
58
|
+
value.append(data.get("create", False))
|
|
59
|
+
else:
|
|
60
|
+
value.append(False)
|
|
61
|
+
else:
|
|
62
|
+
value = False
|
|
63
|
+
return value
|
|
64
|
+
|
|
47
65
|
|
|
48
66
|
class SBAdminFilterWidget(JSONSerializableMixin):
|
|
49
67
|
template_name = None
|
|
@@ -56,6 +74,7 @@ class SBAdminFilterWidget(JSONSerializableMixin):
|
|
|
56
74
|
default_value = None
|
|
57
75
|
default_label = None
|
|
58
76
|
filter_query_lambda = None
|
|
77
|
+
exclude_null_operators = False
|
|
59
78
|
|
|
60
79
|
def __init__(
|
|
61
80
|
self,
|
|
@@ -63,6 +82,7 @@ class SBAdminFilterWidget(JSONSerializableMixin):
|
|
|
63
82
|
default_value=None,
|
|
64
83
|
default_label=None,
|
|
65
84
|
filter_query_lambda=None,
|
|
85
|
+
exclude_null_operators=None,
|
|
66
86
|
**kwargs,
|
|
67
87
|
) -> None:
|
|
68
88
|
super().__init__()
|
|
@@ -70,6 +90,9 @@ class SBAdminFilterWidget(JSONSerializableMixin):
|
|
|
70
90
|
self.default_value = self.default_value or default_value
|
|
71
91
|
self.default_label = self.default_label or default_label
|
|
72
92
|
self.filter_query_lambda = filter_query_lambda or self.filter_query_lambda
|
|
93
|
+
self.exclude_null_operators = (
|
|
94
|
+
exclude_null_operators or self.exclude_null_operators
|
|
95
|
+
)
|
|
73
96
|
|
|
74
97
|
def init_filter_widget_static(self, field, view, configuration):
|
|
75
98
|
self.field = field
|
|
@@ -206,6 +229,8 @@ class ChoiceFilterWidget(SBAdminFilterWidget):
|
|
|
206
229
|
return found_label[0] if found_label else default_value
|
|
207
230
|
|
|
208
231
|
def get_base_filter_query_for_parsed_value(self, request, filter_value):
|
|
232
|
+
if isinstance(self.model_field, ArrayField):
|
|
233
|
+
return Q(**{f"{self.field.filter_field}__contains": [filter_value]})
|
|
209
234
|
return Q(**{self.field.filter_field: filter_value})
|
|
210
235
|
|
|
211
236
|
|
|
@@ -215,8 +240,38 @@ class RadioChoiceFilterWidget(ChoiceFilterWidget):
|
|
|
215
240
|
|
|
216
241
|
class MultipleChoiceFilterWidget(AutocompleteParseMixin, ChoiceFilterWidget):
|
|
217
242
|
template_name = "sb_admin/filter_widgets/multiple_choice_field.html"
|
|
243
|
+
enable_select_all = False
|
|
244
|
+
select_all_keyword = None
|
|
245
|
+
select_all_label = None
|
|
246
|
+
|
|
247
|
+
def __init__(
|
|
248
|
+
self,
|
|
249
|
+
choices,
|
|
250
|
+
template_name=None,
|
|
251
|
+
default_value=None,
|
|
252
|
+
default_label=None,
|
|
253
|
+
enable_select_all=False,
|
|
254
|
+
select_all_keyword=SELECT_ALL_KEYWORD,
|
|
255
|
+
select_all_label=_("All"),
|
|
256
|
+
**kwargs,
|
|
257
|
+
) -> None:
|
|
258
|
+
super().__init__(
|
|
259
|
+
choices=choices,
|
|
260
|
+
template_name=template_name,
|
|
261
|
+
default_value=default_value,
|
|
262
|
+
default_label=default_label,
|
|
263
|
+
**kwargs,
|
|
264
|
+
)
|
|
265
|
+
self.enable_select_all = enable_select_all
|
|
266
|
+
self.select_all_keyword = select_all_keyword
|
|
267
|
+
self.select_all_label = select_all_label
|
|
218
268
|
|
|
219
269
|
def get_base_filter_query_for_parsed_value(self, request, filter_value):
|
|
270
|
+
if isinstance(self.model_field, ArrayField):
|
|
271
|
+
q_objects = Q()
|
|
272
|
+
for value in filter_value:
|
|
273
|
+
q_objects |= Q(**{f"{self.field.filter_field}__contains": [value]})
|
|
274
|
+
return q_objects
|
|
220
275
|
return Q(**{f"{self.field.filter_field}__in": filter_value})
|
|
221
276
|
|
|
222
277
|
def get_advanced_filter_operators(self):
|
|
@@ -279,6 +334,7 @@ class DateFilterWidget(SBAdminFilterWidget):
|
|
|
279
334
|
"label": _("Last 12 months"),
|
|
280
335
|
},
|
|
281
336
|
]
|
|
337
|
+
shortcuts_dict = {AllOperators.IN_THE_LAST.value: shortcuts}
|
|
282
338
|
default_value_shortcut_index = None
|
|
283
339
|
|
|
284
340
|
def __init__(
|
|
@@ -301,33 +357,43 @@ class DateFilterWidget(SBAdminFilterWidget):
|
|
|
301
357
|
def get_advanced_filter_operators(self):
|
|
302
358
|
return DATE_ATTRIBUTES
|
|
303
359
|
|
|
360
|
+
def process_shortcut(self, shortcut, now):
|
|
361
|
+
return shortcut
|
|
362
|
+
|
|
304
363
|
def get_shortcuts(self):
|
|
305
364
|
now = timezone.now()
|
|
306
365
|
shortcuts = []
|
|
307
366
|
for shortcut in self.shortcuts:
|
|
308
|
-
shortcuts.append(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
367
|
+
shortcuts.append(self.process_shortcut(shortcut, now))
|
|
368
|
+
return shortcuts
|
|
369
|
+
|
|
370
|
+
def get_shortcuts_dict(self):
|
|
371
|
+
now = timezone.now()
|
|
372
|
+
shortcuts = {}
|
|
373
|
+
for key, shortcuts_group in self.shortcuts_dict.items():
|
|
374
|
+
shortcuts[key] = []
|
|
375
|
+
for shortcut in shortcuts_group:
|
|
376
|
+
shortcuts[key].append(self.process_shortcut(shortcut, now))
|
|
317
377
|
return shortcuts
|
|
318
378
|
|
|
379
|
+
def get_default_label(self):
|
|
380
|
+
if self.default_value_shortcut_index is not None:
|
|
381
|
+
return self.get_shortcuts()[self.default_value_shortcut_index]["label"]
|
|
382
|
+
return super().get_default_value()
|
|
383
|
+
|
|
319
384
|
def get_default_value(self):
|
|
320
385
|
if self.default_value_shortcut_index is not None:
|
|
321
386
|
selected_shortcut_value = self.get_shortcuts()[
|
|
322
387
|
self.default_value_shortcut_index
|
|
323
388
|
]["value"]
|
|
324
|
-
return
|
|
389
|
+
return SBAdminViewService.json_dumps_for_url(
|
|
390
|
+
self.get_value_from_date_or_range(selected_shortcut_value)
|
|
391
|
+
)
|
|
325
392
|
return super().get_default_value()
|
|
326
393
|
|
|
327
394
|
def get_data(self):
|
|
328
395
|
return json.dumps(
|
|
329
396
|
{
|
|
330
|
-
"shortcuts": self.get_shortcuts(),
|
|
331
397
|
"flatpickrOptions": {
|
|
332
398
|
"locale": {
|
|
333
399
|
"rangeSeparator": self.DATE_RANGE_SPLIT,
|
|
@@ -337,6 +403,19 @@ class DateFilterWidget(SBAdminFilterWidget):
|
|
|
337
403
|
cls=SBAdminJSONEncoder,
|
|
338
404
|
)
|
|
339
405
|
|
|
406
|
+
def get_shortcuts_data(self):
|
|
407
|
+
return json.dumps(
|
|
408
|
+
self.get_shortcuts(),
|
|
409
|
+
cls=SBAdminJSONEncoder,
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
def get_shortcuts_dict_data(self):
|
|
413
|
+
# used for advanced filters with different calendar operators "in the last", "in the next", etc.
|
|
414
|
+
return json.dumps(
|
|
415
|
+
self.get_shortcuts_dict(),
|
|
416
|
+
cls=SBAdminJSONEncoder,
|
|
417
|
+
)
|
|
418
|
+
|
|
340
419
|
@classmethod
|
|
341
420
|
def is_used_for_model_field_type(cls, model_field):
|
|
342
421
|
return isinstance(model_field, fields.DateField)
|
|
@@ -351,12 +430,28 @@ class DateFilterWidget(SBAdminFilterWidget):
|
|
|
351
430
|
if filter_value is None:
|
|
352
431
|
return [None, None]
|
|
353
432
|
date_format = cls.DATE_FORMAT
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
433
|
+
if type(filter_value) is list:
|
|
434
|
+
if type(filter_value[0]) is int:
|
|
435
|
+
return [
|
|
436
|
+
timezone.now() + timedelta(days=filter_value[0]),
|
|
437
|
+
timezone.now() + timedelta(days=filter_value[1]),
|
|
438
|
+
]
|
|
439
|
+
return [
|
|
440
|
+
datetime.strptime(filter_value[0], date_format),
|
|
441
|
+
datetime.strptime(filter_value[1], date_format),
|
|
442
|
+
]
|
|
443
|
+
try:
|
|
444
|
+
days_range = json.loads(filter_value)
|
|
445
|
+
return [
|
|
446
|
+
timezone.now() + timedelta(days=days_range[0]),
|
|
447
|
+
timezone.now() + timedelta(days=days_range[1]),
|
|
448
|
+
]
|
|
449
|
+
except json.decoder.JSONDecodeError:
|
|
450
|
+
date_range = filter_value.split(cls.DATE_RANGE_SPLIT)
|
|
451
|
+
if len(date_range) == 2:
|
|
452
|
+
date_from = datetime.strptime(date_range[0], date_format)
|
|
453
|
+
date_to = datetime.strptime(date_range[1], date_format)
|
|
454
|
+
return [date_from, date_to]
|
|
360
455
|
date_value = datetime.strptime(filter_value, date_format)
|
|
361
456
|
return [date_value, date_value]
|
|
362
457
|
|
|
@@ -364,9 +459,11 @@ class DateFilterWidget(SBAdminFilterWidget):
|
|
|
364
459
|
def get_value_from_date_or_range(cls, date_or_range):
|
|
365
460
|
if not isinstance(date_or_range, list):
|
|
366
461
|
return datetime.strftime(date_or_range, cls.DATE_FORMAT)
|
|
462
|
+
if type(date_or_range[0]) is int:
|
|
463
|
+
return date_or_range
|
|
367
464
|
date_from = datetime.strftime(date_or_range[0], cls.DATE_FORMAT)
|
|
368
465
|
date_to = datetime.strftime(date_or_range[1], cls.DATE_FORMAT)
|
|
369
|
-
return
|
|
466
|
+
return [date_from, date_to]
|
|
370
467
|
|
|
371
468
|
def parse_value_from_input(self, request, filter_value):
|
|
372
469
|
return self.get_range_from_value(filter_value)
|
|
@@ -398,6 +495,7 @@ class AutocompleteFilterWidget(
|
|
|
398
495
|
allow_add = False
|
|
399
496
|
hide_clear_button = False
|
|
400
497
|
search_query_lambda = None
|
|
498
|
+
create_value_field = None
|
|
401
499
|
|
|
402
500
|
def get_field_name(self):
|
|
403
501
|
return self.field.name
|
|
@@ -420,13 +518,16 @@ class AutocompleteFilterWidget(
|
|
|
420
518
|
allow_add=None,
|
|
421
519
|
hide_clear_button=None,
|
|
422
520
|
search_query_lambda=None,
|
|
521
|
+
create_value_field=None,
|
|
423
522
|
**kwargs,
|
|
424
523
|
) -> None:
|
|
425
524
|
super().__init__(template_name, default_value, **kwargs)
|
|
426
525
|
self.model = model or self.model
|
|
427
526
|
self.value_field = value_field or self.value_field
|
|
428
527
|
self.filter_query_lambda = filter_query_lambda or self.filter_query_lambda
|
|
528
|
+
# filters queryset to search in
|
|
429
529
|
self.filter_search_lambda = filter_search_lambda or self.filter_search_lambda
|
|
530
|
+
# defines fields to search on
|
|
430
531
|
self.search_query_lambda = search_query_lambda or self.search_query_lambda
|
|
431
532
|
self.label_lambda = label_lambda or self.label_lambda
|
|
432
533
|
self.value_lambda = value_lambda or self.value_lambda
|
|
@@ -434,6 +535,7 @@ class AutocompleteFilterWidget(
|
|
|
434
535
|
self.multiselect = self.multiselect if self.multiselect is not None else True
|
|
435
536
|
self.forward = forward or self.forward
|
|
436
537
|
self.allow_add = allow_add or self.allow_add
|
|
538
|
+
self.create_value_field = create_value_field or self.create_value_field
|
|
437
539
|
self.hide_clear_button = (
|
|
438
540
|
hide_clear_button
|
|
439
541
|
if hide_clear_button is not None
|
|
@@ -542,8 +644,9 @@ class AutocompleteFilterWidget(
|
|
|
542
644
|
def get_value_field(self):
|
|
543
645
|
return self.value_field or self.model._meta.pk.name
|
|
544
646
|
|
|
545
|
-
def filter_search_queryset(self, request, qs, search_term, forward_data):
|
|
647
|
+
def filter_search_queryset(self, request, qs, search_term="", forward_data=None):
|
|
546
648
|
if self.filter_search_lambda:
|
|
649
|
+
forward_data = forward_data or {}
|
|
547
650
|
qs = qs.filter(
|
|
548
651
|
self.filter_search_lambda(request, search_term, forward_data)
|
|
549
652
|
)
|
|
@@ -555,9 +658,16 @@ class AutocompleteFilterWidget(
|
|
|
555
658
|
page_num = int(post_data.get(AUTOCOMPLETE_PAGE_NUM, 1))
|
|
556
659
|
from_item = (page_num - 1) * AUTOCOMPLETE_PAGE_SIZE
|
|
557
660
|
to_item = (page_num) * AUTOCOMPLETE_PAGE_SIZE
|
|
661
|
+
|
|
662
|
+
# filter queryset
|
|
663
|
+
# base restricted queryset
|
|
558
664
|
qs = self.get_queryset(request)
|
|
665
|
+
# filters queryset to search in, uses filter_search_lambda
|
|
559
666
|
qs = self.filter_search_queryset(request, qs, search_term, forward_data)
|
|
667
|
+
|
|
668
|
+
# search in queryset
|
|
560
669
|
if self.search_query_lambda:
|
|
670
|
+
# defines fields to search on
|
|
561
671
|
qs = self.search_query_lambda(
|
|
562
672
|
request,
|
|
563
673
|
qs,
|
|
@@ -566,6 +676,7 @@ class AutocompleteFilterWidget(
|
|
|
566
676
|
SBAdminTranslationsService.get_main_lang_code(),
|
|
567
677
|
)
|
|
568
678
|
else:
|
|
679
|
+
# defines default fields to search on - all char fields
|
|
569
680
|
qs = self.get_default_search_query(
|
|
570
681
|
request,
|
|
571
682
|
qs,
|
|
@@ -593,6 +704,8 @@ class AutocompleteFilterWidget(
|
|
|
593
704
|
def get_label(self, request, item):
|
|
594
705
|
if self.label_lambda:
|
|
595
706
|
return self.label_lambda(request, item)
|
|
707
|
+
if isinstance(item, list):
|
|
708
|
+
return ", ".join(map(str, item))
|
|
596
709
|
return str(item)
|
|
597
710
|
|
|
598
711
|
def get_context(self, name, value, attrs):
|
|
@@ -611,3 +724,225 @@ class AutocompleteFilterWidget(
|
|
|
611
724
|
AllOperators.IS_NULL,
|
|
612
725
|
AllOperators.IS_NOT_NULL,
|
|
613
726
|
]
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
class FromValuesAutocompleteWidget(AutocompleteFilterWidget):
|
|
730
|
+
def init_filter_widget_static(self, field, view, configuration):
|
|
731
|
+
self.model = field.view.model
|
|
732
|
+
super().init_filter_widget_static(field, view, configuration)
|
|
733
|
+
|
|
734
|
+
def get_queryset(self, request=None):
|
|
735
|
+
qs = super().get_queryset(request)
|
|
736
|
+
qs = qs.annotate(**self.field.get_field_annotates(values=[]))
|
|
737
|
+
return qs
|
|
738
|
+
|
|
739
|
+
def search(self, request, post_data):
|
|
740
|
+
search_term = post_data.get(AUTOCOMPLETE_SEARCH_NAME)
|
|
741
|
+
forward_data = json.loads(post_data.get(AUTOCOMPLETE_FORWARD_NAME, "{}"))
|
|
742
|
+
page_num = int(post_data.get(AUTOCOMPLETE_PAGE_NUM, 1))
|
|
743
|
+
from_item = (page_num - 1) * AUTOCOMPLETE_PAGE_SIZE
|
|
744
|
+
to_item = (page_num) * AUTOCOMPLETE_PAGE_SIZE
|
|
745
|
+
qs = self.get_queryset(request)
|
|
746
|
+
qs = self.filter_search_queryset(request, qs, search_term, forward_data)
|
|
747
|
+
qs = (
|
|
748
|
+
qs.filter(**{f"{self.field.name}__icontains": search_term})
|
|
749
|
+
.values(self.field.name)
|
|
750
|
+
.annotate(remove_duplicates_count=Count(self.field.name))
|
|
751
|
+
.order_by(self.field.name)[from_item:to_item]
|
|
752
|
+
)
|
|
753
|
+
result = []
|
|
754
|
+
for item in qs:
|
|
755
|
+
result.append(
|
|
756
|
+
{
|
|
757
|
+
"value": self.get_value(request, item),
|
|
758
|
+
"label": self.get_label(request, item),
|
|
759
|
+
}
|
|
760
|
+
)
|
|
761
|
+
return result
|
|
762
|
+
|
|
763
|
+
def get_value(self, request, item):
|
|
764
|
+
return item.get(self.field.name)
|
|
765
|
+
|
|
766
|
+
def get_label(self, request, item):
|
|
767
|
+
return item.get(self.field.name)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
class SBAdminTreeWidgetMixin:
|
|
771
|
+
order_by = None
|
|
772
|
+
inline = False
|
|
773
|
+
RELATIONSHIP_PICK_MODE_NONE = None
|
|
774
|
+
RELATIONSHIP_PICK_MODE_PARENT = "parent"
|
|
775
|
+
relationship_pick_mode = RELATIONSHIP_PICK_MODE_NONE
|
|
776
|
+
additional_columns = None
|
|
777
|
+
tree_strings = {
|
|
778
|
+
"loading": pgettext_lazy("Tree widget", "Loading..."),
|
|
779
|
+
"loadError": pgettext_lazy("Tree widget", "Load error!"),
|
|
780
|
+
"moreData": pgettext_lazy("Tree widget", "More..."),
|
|
781
|
+
"noData": pgettext_lazy("Tree widget", "No data."),
|
|
782
|
+
}
|
|
783
|
+
model = None
|
|
784
|
+
path_field = "path"
|
|
785
|
+
|
|
786
|
+
def __init__(
|
|
787
|
+
self,
|
|
788
|
+
order_by=None,
|
|
789
|
+
relationship_pick_mode=None,
|
|
790
|
+
inline=None,
|
|
791
|
+
additional_columns=None,
|
|
792
|
+
tree_strings=None,
|
|
793
|
+
*args,
|
|
794
|
+
**kwargs,
|
|
795
|
+
):
|
|
796
|
+
self.inline = inline if inline is not None else self.inline
|
|
797
|
+
self.order_by = order_by if order_by is not None else self.order_by
|
|
798
|
+
self.relationship_pick_mode = relationship_pick_mode
|
|
799
|
+
self.additional_columns = (
|
|
800
|
+
additional_columns
|
|
801
|
+
if additional_columns is not None
|
|
802
|
+
else self.additional_columns
|
|
803
|
+
)
|
|
804
|
+
self.tree_strings = (
|
|
805
|
+
tree_strings if tree_strings is not None else self.tree_strings
|
|
806
|
+
)
|
|
807
|
+
if self.inline:
|
|
808
|
+
self.template_name = "sb_admin/widgets/tree_select_inline.html"
|
|
809
|
+
super().__init__(*args, **kwargs)
|
|
810
|
+
|
|
811
|
+
def action_autocomplete(self, request, modifier):
|
|
812
|
+
result = self.format_tree_data(request, self.get_queryset(request))
|
|
813
|
+
return JsonResponse(data=result, safe=False)
|
|
814
|
+
|
|
815
|
+
def format_tree_data(self, request, queryset):
|
|
816
|
+
self_id = None
|
|
817
|
+
if self.relationship_pick_mode == self.RELATIONSHIP_PICK_MODE_PARENT:
|
|
818
|
+
# disable selecting self and children if selecting parent
|
|
819
|
+
self_id = self.form.instance.id if self.form.instance else None
|
|
820
|
+
return self.get_tree_data(request, queryset, self_id=self_id)
|
|
821
|
+
|
|
822
|
+
@classmethod
|
|
823
|
+
def get_tree_base_values(cls):
|
|
824
|
+
return ["id", cls.path_field]
|
|
825
|
+
|
|
826
|
+
@classmethod
|
|
827
|
+
def get_tree_key(cls, request, item):
|
|
828
|
+
return item.get(cls.path_field)
|
|
829
|
+
|
|
830
|
+
@classmethod
|
|
831
|
+
def get_tree_title(cls, request, item):
|
|
832
|
+
raise NotImplementedError
|
|
833
|
+
|
|
834
|
+
@classmethod
|
|
835
|
+
def get_value(cls, request, item):
|
|
836
|
+
return getattr(item, cls.path_field)
|
|
837
|
+
|
|
838
|
+
@classmethod
|
|
839
|
+
def get_label(cls, request, item):
|
|
840
|
+
raise NotImplementedError
|
|
841
|
+
|
|
842
|
+
@classmethod
|
|
843
|
+
def tree_process_global_data(cls, request, queryset, **kwargs):
|
|
844
|
+
return {}
|
|
845
|
+
|
|
846
|
+
@classmethod
|
|
847
|
+
def get_additional_data(cls, request, item, tree_process_global_data):
|
|
848
|
+
return {}
|
|
849
|
+
|
|
850
|
+
@classmethod
|
|
851
|
+
def get_tree_data(cls, request, queryset, values=None, self_id=None, **kwargs):
|
|
852
|
+
tree_values = cls.get_tree_base_values()
|
|
853
|
+
tree_values.extend(values if values else [])
|
|
854
|
+
|
|
855
|
+
queryset = queryset.order_by(*cls.order_by)
|
|
856
|
+
queryset = queryset.annotate(
|
|
857
|
+
**SBAdminViewService.get_annotates(cls.model, tree_values, [])
|
|
858
|
+
)
|
|
859
|
+
flat_data = []
|
|
860
|
+
tree_data, lnk = [], {}
|
|
861
|
+
tree_process_global_data = cls.tree_process_global_data(
|
|
862
|
+
request, queryset, **kwargs
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
data = list(queryset.values(*tree_values))
|
|
866
|
+
for item in data:
|
|
867
|
+
path = item.get("path")
|
|
868
|
+
depth = int(len(path) / cls.model.steplen)
|
|
869
|
+
item_id = cls.get_tree_key(request, item)
|
|
870
|
+
item_label = cls.get_tree_title(request, item)
|
|
871
|
+
newobj = {
|
|
872
|
+
"title": item_label,
|
|
873
|
+
"key": str(item_id),
|
|
874
|
+
"data": {"id": item.get("id")},
|
|
875
|
+
}
|
|
876
|
+
if item_id == self_id:
|
|
877
|
+
# disable selecting self and children if selecting parent
|
|
878
|
+
newobj["checkbox"] = False
|
|
879
|
+
|
|
880
|
+
additional_data = cls.get_additional_data(
|
|
881
|
+
request, item, tree_process_global_data
|
|
882
|
+
)
|
|
883
|
+
newobj.update(additional_data)
|
|
884
|
+
|
|
885
|
+
if depth == 1:
|
|
886
|
+
tree_data.append(newobj)
|
|
887
|
+
flat_data.append(newobj)
|
|
888
|
+
else:
|
|
889
|
+
parentpath = cls.model._get_basepath(path, depth - 1)
|
|
890
|
+
parentobj = lnk[parentpath]
|
|
891
|
+
if "children" not in parentobj:
|
|
892
|
+
parentobj["children"] = []
|
|
893
|
+
if parentobj.get("checkbox") is False:
|
|
894
|
+
# disable selecting self and children if selecting parent
|
|
895
|
+
newobj["checkbox"] = False
|
|
896
|
+
parentobj["children"].append(newobj)
|
|
897
|
+
flat_data.append(newobj)
|
|
898
|
+
lnk[path] = newobj
|
|
899
|
+
return tree_data
|
|
900
|
+
|
|
901
|
+
# tree_widget_data: [{"key":"path", "children": [{...}]}]
|
|
902
|
+
@classmethod
|
|
903
|
+
def process_treebeard_tree(
|
|
904
|
+
cls,
|
|
905
|
+
tree_widget_data,
|
|
906
|
+
treebeard_objs_by_path,
|
|
907
|
+
depth=1,
|
|
908
|
+
parent_path="",
|
|
909
|
+
path_base="",
|
|
910
|
+
):
|
|
911
|
+
if not path_base:
|
|
912
|
+
path_base = ((cls.model.steplen - 1) * "0") + "1"
|
|
913
|
+
previous = None
|
|
914
|
+
objs_to_update = []
|
|
915
|
+
for tree_widget_node in tree_widget_data:
|
|
916
|
+
treebeard_obj = treebeard_objs_by_path.get(tree_widget_node["key"])
|
|
917
|
+
old_depth = treebeard_obj.depth
|
|
918
|
+
old_path = getattr(treebeard_obj, cls.path_field)
|
|
919
|
+
old_numchild = treebeard_obj.numchild
|
|
920
|
+
treebeard_obj.depth = depth
|
|
921
|
+
if not previous:
|
|
922
|
+
previous = treebeard_obj
|
|
923
|
+
setattr(treebeard_obj, cls.path_field, parent_path + path_base)
|
|
924
|
+
else:
|
|
925
|
+
setattr(treebeard_obj, cls.path_field, previous._inc_path())
|
|
926
|
+
previous = treebeard_obj
|
|
927
|
+
children = tree_widget_node.get("children", [])
|
|
928
|
+
treebeard_obj.numchild = len(children)
|
|
929
|
+
if (
|
|
930
|
+
treebeard_obj.depth != old_depth
|
|
931
|
+
or getattr(treebeard_obj, cls.path_field) != old_path
|
|
932
|
+
or treebeard_obj.numchild != old_numchild
|
|
933
|
+
):
|
|
934
|
+
objs_to_update.append(treebeard_obj)
|
|
935
|
+
objs_to_update.extend(
|
|
936
|
+
cls.process_treebeard_tree(
|
|
937
|
+
children,
|
|
938
|
+
treebeard_objs_by_path,
|
|
939
|
+
depth + 1,
|
|
940
|
+
getattr(treebeard_obj, cls.path_field),
|
|
941
|
+
path_base,
|
|
942
|
+
)
|
|
943
|
+
)
|
|
944
|
+
return objs_to_update
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
class SBAdminTreeFilterWidget(SBAdminTreeWidgetMixin, AutocompleteFilterWidget):
|
|
948
|
+
template_name = "sb_admin/filter_widgets/tree_select_filter.html"
|
|
@@ -56,11 +56,14 @@ class SBAdminMenuItem(object):
|
|
|
56
56
|
return self.label or self.view.get_menu_label()
|
|
57
57
|
|
|
58
58
|
def get_url(self, request):
|
|
59
|
-
|
|
60
|
-
self.url
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
if callable(self.url):
|
|
60
|
+
return self.url(request)
|
|
61
|
+
elif self.url:
|
|
62
|
+
return self.url
|
|
63
|
+
elif self.view:
|
|
64
|
+
return self.view.get_menu_view_url(request)
|
|
65
|
+
else:
|
|
66
|
+
return ""
|
|
64
67
|
|
|
65
68
|
def get_icon(self):
|
|
66
69
|
return self.icon or getattr(self.view, "icon", None)
|