django-smartbase-admin 0.2.54__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 +74 -38
- django_smartbase_admin/actions/advanced_filters.py +24 -1
- django_smartbase_admin/admin/admin_base.py +401 -96
- django_smartbase_admin/admin/site.py +93 -35
- django_smartbase_admin/admin/widgets.py +589 -26
- django_smartbase_admin/apps.py +2 -0
- django_smartbase_admin/engine/actions.py +34 -16
- django_smartbase_admin/engine/admin_base_view.py +252 -115
- django_smartbase_admin/engine/configuration.py +186 -4
- django_smartbase_admin/engine/const.py +6 -0
- django_smartbase_admin/engine/dashboard.py +44 -23
- django_smartbase_admin/engine/fake_inline.py +15 -11
- django_smartbase_admin/engine/field.py +42 -12
- django_smartbase_admin/engine/field_formatter.py +22 -8
- django_smartbase_admin/engine/filter_widgets.py +309 -20
- 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 +80 -13
- 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/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 +61 -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 +304 -31
- 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/table.js +34 -5
- 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 +6 -0
- 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 -114
- django_smartbase_admin/templates/sb_admin/actions/dashboard.html +2 -2
- django_smartbase_admin/templates/sb_admin/actions/list.html +79 -39
- django_smartbase_admin/templates/sb_admin/actions/partials/action_link.html +14 -0
- 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/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/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 +76 -34
- 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 +27 -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/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/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 +85 -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.54.dist-info → django_smartbase_admin-1.0.38.dist-info}/RECORD +177 -115
- {django_smartbase_admin-0.2.54.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.54.dist-info/METADATA +0 -25
- {django_smartbase_admin-0.2.54.dist-info → django_smartbase_admin-1.0.38.dist-info}/LICENSE.md +0 -0
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import logging
|
|
2
3
|
import urllib.parse
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from functools import partial
|
|
6
|
+
from typing import Any
|
|
7
|
+
from urllib.parse import urlparse
|
|
3
8
|
|
|
4
9
|
from ckeditor.fields import RichTextFormField
|
|
5
10
|
from ckeditor_uploader.fields import RichTextUploadingFormField
|
|
@@ -9,24 +14,32 @@ from django.contrib.admin.options import get_content_type_for_model
|
|
|
9
14
|
from django.contrib.admin.utils import unquote
|
|
10
15
|
from django.contrib.admin.widgets import AdminTextareaWidget
|
|
11
16
|
from django.contrib.auth.forms import UsernameField, ReadOnlyPasswordHashWidget
|
|
12
|
-
from django.contrib.
|
|
17
|
+
from django.contrib.contenttypes.forms import BaseGenericInlineFormSet
|
|
18
|
+
from django.contrib.contenttypes.models import ContentType
|
|
13
19
|
from django.core.exceptions import (
|
|
14
20
|
FieldDoesNotExist,
|
|
15
21
|
ImproperlyConfigured,
|
|
16
22
|
PermissionDenied,
|
|
17
23
|
)
|
|
18
24
|
from django.db import models
|
|
25
|
+
from django.db.models import QuerySet, Q, Model
|
|
19
26
|
from django.forms import HiddenInput
|
|
20
|
-
from django.forms.models import
|
|
27
|
+
from django.forms.models import (
|
|
28
|
+
ModelFormMetaclass,
|
|
29
|
+
modelform_factory,
|
|
30
|
+
)
|
|
31
|
+
from django.http import HttpResponse
|
|
21
32
|
from django.template.loader import render_to_string
|
|
22
33
|
from django.template.response import TemplateResponse
|
|
23
|
-
from django.urls import reverse
|
|
24
|
-
from django.utils.safestring import mark_safe
|
|
34
|
+
from django.urls import reverse, NoReverseMatch, resolve
|
|
35
|
+
from django.utils.safestring import mark_safe, SafeString
|
|
25
36
|
from django.utils.text import capfirst
|
|
26
37
|
from django.utils.translation import gettext_lazy as _
|
|
27
|
-
from django.utils.translation import override as translation_override
|
|
28
38
|
from django_admin_inline_paginator.admin import TabularInlinePaginated
|
|
29
|
-
from
|
|
39
|
+
from django_htmx.http import trigger_client_event
|
|
40
|
+
from filer.fields.file import FilerFileField
|
|
41
|
+
from filer.fields.image import AdminImageFormField, FilerImageField
|
|
42
|
+
from nested_admin.formsets import NestedInlineFormSet
|
|
30
43
|
from nested_admin.nested import (
|
|
31
44
|
NestedModelAdmin,
|
|
32
45
|
NestedTabularInline,
|
|
@@ -35,12 +48,14 @@ from nested_admin.nested import (
|
|
|
35
48
|
NestedGenericStackedInline,
|
|
36
49
|
)
|
|
37
50
|
|
|
38
|
-
from django_smartbase_admin.actions.admin_action_list import SBAdminListAction
|
|
39
51
|
from django_smartbase_admin.engine.actions import SBAdminCustomAction
|
|
40
|
-
from django_smartbase_admin.
|
|
52
|
+
from django_smartbase_admin.services.thread_local import SBAdminThreadLocalService
|
|
53
|
+
from django_smartbase_admin.utils import FormFieldsetMixin, is_modal
|
|
41
54
|
|
|
42
55
|
parler_enabled = None
|
|
43
56
|
try:
|
|
57
|
+
from parler.admin import TranslatableAdmin
|
|
58
|
+
|
|
44
59
|
from parler.forms import (
|
|
45
60
|
TranslatableModelForm,
|
|
46
61
|
TranslatableModelFormMetaclass,
|
|
@@ -54,6 +69,30 @@ try:
|
|
|
54
69
|
except ImportError:
|
|
55
70
|
pass
|
|
56
71
|
|
|
72
|
+
postrgres_enabled = None
|
|
73
|
+
try:
|
|
74
|
+
from django.contrib.postgres.forms import SimpleArrayField
|
|
75
|
+
|
|
76
|
+
postrgres_enabled = True
|
|
77
|
+
except ImportError:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
django_cms_attributes = None
|
|
81
|
+
try:
|
|
82
|
+
from djangocms_attributes_field.fields import AttributesFormField
|
|
83
|
+
|
|
84
|
+
django_cms_attributes = True
|
|
85
|
+
except ImportError:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
color_field_enabled = None
|
|
89
|
+
try:
|
|
90
|
+
from colorfield.fields import ColorField
|
|
91
|
+
|
|
92
|
+
color_field_enabled = True
|
|
93
|
+
except ImportError:
|
|
94
|
+
pass
|
|
95
|
+
|
|
57
96
|
from django_smartbase_admin.admin.widgets import (
|
|
58
97
|
SBAdminTextInputWidget,
|
|
59
98
|
SBAdminTextareaWidget,
|
|
@@ -74,19 +113,32 @@ from django_smartbase_admin.admin.widgets import (
|
|
|
74
113
|
SBAdminPasswordInputWidget,
|
|
75
114
|
SBAdminReadOnlyPasswordHashWidget,
|
|
76
115
|
SBAdminHiddenWidget,
|
|
116
|
+
SBAdminCKEditorUploadingWidget,
|
|
117
|
+
SBAdminAttributesWidget,
|
|
118
|
+
SBAdminMultipleChoiceInlineWidget,
|
|
119
|
+
SBAdminColorWidget,
|
|
120
|
+
SBAdminFilerFileWidget,
|
|
77
121
|
)
|
|
78
122
|
from django_smartbase_admin.engine.admin_base_view import (
|
|
79
123
|
SBAdminBaseListView,
|
|
80
124
|
SBAdminBaseView,
|
|
81
125
|
SBAdminBaseQuerysetMixin,
|
|
126
|
+
SBADMIN_IS_MODAL_VAR,
|
|
127
|
+
SBADMIN_PARENT_INSTANCE_PK_VAR,
|
|
128
|
+
SBADMIN_PARENT_INSTANCE_LABEL_VAR,
|
|
129
|
+
SBADMIN_PARENT_INSTANCE_FIELD_NAME_VAR,
|
|
130
|
+
SBADMIN_RELOAD_ON_SAVE_VAR,
|
|
82
131
|
)
|
|
83
132
|
from django_smartbase_admin.engine.const import (
|
|
84
133
|
OBJECT_ID_PLACEHOLDER,
|
|
85
134
|
TRANSLATIONS_SELECTED_LANGUAGES,
|
|
135
|
+
ROW_CLASS_FIELD,
|
|
86
136
|
)
|
|
87
137
|
from django_smartbase_admin.services.translations import SBAdminTranslationsService
|
|
88
138
|
from django_smartbase_admin.services.views import SBAdminViewService
|
|
89
139
|
|
|
140
|
+
logger = logging.getLogger(__name__)
|
|
141
|
+
|
|
90
142
|
|
|
91
143
|
class SBAdminFormFieldWidgetsMixin:
|
|
92
144
|
formfield_widgets = {
|
|
@@ -109,15 +161,26 @@ class SBAdminFormFieldWidgetsMixin:
|
|
|
109
161
|
forms.BooleanField: SBAdminToggleWidget,
|
|
110
162
|
forms.SlugField: SBAdminTextInputWidget,
|
|
111
163
|
RichTextFormField: SBAdminCKEditorWidget,
|
|
112
|
-
RichTextUploadingFormField:
|
|
164
|
+
RichTextUploadingFormField: SBAdminCKEditorUploadingWidget,
|
|
113
165
|
forms.ChoiceField: SBAdminSelectWidget,
|
|
114
166
|
forms.TypedChoiceField: SBAdminSelectWidget,
|
|
167
|
+
forms.MultipleChoiceField: SBAdminMultipleChoiceInlineWidget,
|
|
168
|
+
forms.TypedMultipleChoiceField: SBAdminMultipleChoiceInlineWidget,
|
|
115
169
|
forms.NullBooleanField: SBAdminNullBooleanSelectWidget,
|
|
116
|
-
SimpleArrayField: SBAdminArrayWidget,
|
|
117
170
|
AdminImageFormField: SBAdminImageWidget,
|
|
118
171
|
ReadOnlyPasswordHashWidget: SBAdminReadOnlyPasswordHashWidget,
|
|
119
172
|
forms.HiddenInput: SBAdminHiddenWidget,
|
|
120
173
|
}
|
|
174
|
+
db_field_widgets = {
|
|
175
|
+
FilerImageField: SBAdminFilerFileWidget,
|
|
176
|
+
FilerFileField: SBAdminFilerFileWidget,
|
|
177
|
+
}
|
|
178
|
+
if postrgres_enabled:
|
|
179
|
+
formfield_widgets[SimpleArrayField] = SBAdminArrayWidget
|
|
180
|
+
if django_cms_attributes:
|
|
181
|
+
formfield_widgets[AttributesFormField] = SBAdminAttributesWidget
|
|
182
|
+
if color_field_enabled:
|
|
183
|
+
db_field_widgets[ColorField] = SBAdminColorWidget
|
|
121
184
|
|
|
122
185
|
django_widget_to_widget = {
|
|
123
186
|
forms.PasswordInput: SBAdminPasswordInputWidget,
|
|
@@ -125,7 +188,9 @@ class SBAdminFormFieldWidgetsMixin:
|
|
|
125
188
|
}
|
|
126
189
|
|
|
127
190
|
def get_form_field_widget_class(self, form_field, db_field, request):
|
|
128
|
-
default_widget_class = self.
|
|
191
|
+
default_widget_class = self.db_field_widgets.get(
|
|
192
|
+
db_field.__class__, self.formfield_widgets.get(form_field.__class__)
|
|
193
|
+
)
|
|
129
194
|
if not hasattr(request, "request_data"):
|
|
130
195
|
# in case of login the view is not wrapped and we have no request_data present
|
|
131
196
|
return default_widget_class
|
|
@@ -161,7 +226,19 @@ class SBAdminFormFieldWidgetsMixin:
|
|
|
161
226
|
widget_attrs.pop(
|
|
162
227
|
"class", None
|
|
163
228
|
) # remove origin classes to prevent override our custom widget class
|
|
164
|
-
|
|
229
|
+
kwargs = {}
|
|
230
|
+
if isinstance(form_field, RichTextFormField):
|
|
231
|
+
kwargs["config_name"] = getattr(
|
|
232
|
+
form_field.widget, "config_name", None
|
|
233
|
+
) or getattr(db_field, "config_name", "default")
|
|
234
|
+
|
|
235
|
+
kwargs["external_plugin_resources"] = getattr(
|
|
236
|
+
form_field.widget, "external_plugin_resources", None
|
|
237
|
+
) or getattr(db_field, "external_plugin_resources", [])
|
|
238
|
+
kwargs["extra_plugins"] = getattr(
|
|
239
|
+
form_field.widget, "extra_plugins", None
|
|
240
|
+
) or getattr(db_field, "extra_plugins", [])
|
|
241
|
+
form_field.widget = widget(form_field=form_field, attrs=widget_attrs, **kwargs)
|
|
165
242
|
return form_field
|
|
166
243
|
|
|
167
244
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
|
@@ -233,13 +310,21 @@ class SBAdminFormFieldWidgetsMixin:
|
|
|
233
310
|
|
|
234
311
|
|
|
235
312
|
class SBAdminBaseFormInit(SBAdminFormFieldWidgetsMixin, FormFieldsetMixin):
|
|
236
|
-
threadsafe_request = None
|
|
237
313
|
view = None
|
|
238
314
|
|
|
239
315
|
def __init__(self, *args, **kwargs):
|
|
240
316
|
self.view = kwargs.pop("view", self.view)
|
|
241
|
-
|
|
317
|
+
threadsafe_request = kwargs.pop(
|
|
318
|
+
"request", SBAdminThreadLocalService.get_request()
|
|
319
|
+
)
|
|
242
320
|
super().__init__(*args, **kwargs)
|
|
321
|
+
self.init_widgets_dynamic(threadsafe_request)
|
|
322
|
+
for field in self.declared_fields:
|
|
323
|
+
form_field = self.fields.get(field)
|
|
324
|
+
if form_field:
|
|
325
|
+
self.assign_widget_to_form_field(form_field, request=threadsafe_request)
|
|
326
|
+
|
|
327
|
+
def init_widgets_dynamic(self, request):
|
|
243
328
|
for field in self.fields:
|
|
244
329
|
if not hasattr(self.fields[field].widget, "init_widget_dynamic"):
|
|
245
330
|
continue
|
|
@@ -248,19 +333,11 @@ class SBAdminBaseFormInit(SBAdminFormFieldWidgetsMixin, FormFieldsetMixin):
|
|
|
248
333
|
self.fields[field],
|
|
249
334
|
field,
|
|
250
335
|
self.view,
|
|
251
|
-
|
|
336
|
+
request,
|
|
252
337
|
)
|
|
253
|
-
for field in self.declared_fields:
|
|
254
|
-
form_field = self.fields.get(field)
|
|
255
|
-
if form_field:
|
|
256
|
-
self.assign_widget_to_form_field(
|
|
257
|
-
form_field, request=self.threadsafe_request
|
|
258
|
-
)
|
|
259
338
|
|
|
260
339
|
|
|
261
|
-
class SBAdminBaseForm(
|
|
262
|
-
SBAdminBaseFormInit, forms.ModelForm, SBAdminFormFieldWidgetsMixin
|
|
263
|
-
):
|
|
340
|
+
class SBAdminBaseForm(SBAdminBaseFormInit, forms.ModelForm):
|
|
264
341
|
pass
|
|
265
342
|
|
|
266
343
|
|
|
@@ -403,6 +480,7 @@ if parler_enabled:
|
|
|
403
480
|
|
|
404
481
|
class SBAdminInlineAndAdminCommon(SBAdminFormFieldWidgetsMixin):
|
|
405
482
|
sbadmin_fake_inlines = None
|
|
483
|
+
all_base_fields_form = None
|
|
406
484
|
|
|
407
485
|
def init_view_static(self, configuration, model, admin_site):
|
|
408
486
|
configuration.view_map[self.get_id()] = self
|
|
@@ -413,17 +491,14 @@ class SBAdminInlineAndAdminCommon(SBAdminFormFieldWidgetsMixin):
|
|
|
413
491
|
for inline_view in inlines:
|
|
414
492
|
if issubclass(inline_view, SBAdminInline):
|
|
415
493
|
inline_view_instance = inline_view(model, admin_site)
|
|
416
|
-
configuration.view_map[inline_view_instance.get_id()] = (
|
|
417
|
-
inline_view_instance
|
|
418
|
-
)
|
|
419
494
|
inline_view_instance.init_view_static(
|
|
420
495
|
configuration, inline_view_instance.model, admin_site
|
|
421
496
|
)
|
|
422
497
|
|
|
423
|
-
def get_sbadmin_fake_inlines(self, request, obj):
|
|
498
|
+
def get_sbadmin_fake_inlines(self, request, obj) -> Iterable:
|
|
424
499
|
return self.sbadmin_fake_inlines or []
|
|
425
500
|
|
|
426
|
-
def get_inline_instances(self, request, obj=None):
|
|
501
|
+
def get_inline_instances(self, request, obj=None) -> list:
|
|
427
502
|
inline_classes = self.get_inlines(request, obj)
|
|
428
503
|
inline_classes = [*inline_classes] or []
|
|
429
504
|
inline_classes.extend(self.get_sbadmin_fake_inlines(request, obj))
|
|
@@ -442,7 +517,7 @@ class SBAdminInlineAndAdminCommon(SBAdminFormFieldWidgetsMixin):
|
|
|
442
517
|
inlines.append(inline)
|
|
443
518
|
return inlines
|
|
444
519
|
|
|
445
|
-
def init_view_dynamic(self, request, request_data=None, **kwargs):
|
|
520
|
+
def init_view_dynamic(self, request, request_data=None, **kwargs) -> None:
|
|
446
521
|
if SBAdminTranslationsService.is_translated_model(self.model):
|
|
447
522
|
has_default_form = (
|
|
448
523
|
self.form == TranslatableModelForm or self.form == forms.ModelForm
|
|
@@ -454,23 +529,26 @@ class SBAdminInlineAndAdminCommon(SBAdminFormFieldWidgetsMixin):
|
|
|
454
529
|
f"Admin '{self}' form class '{self.form}' needs to extend SBTranslatableModelForm in case of translatable model."
|
|
455
530
|
)
|
|
456
531
|
super().init_view_dynamic(request, request_data, **kwargs)
|
|
457
|
-
self.initialize_form_class(self.form)
|
|
532
|
+
self.initialize_form_class(self.form, request)
|
|
458
533
|
|
|
459
|
-
def initialize_form_class(self, form):
|
|
534
|
+
def initialize_form_class(self, form, request) -> None:
|
|
460
535
|
if form:
|
|
461
536
|
form.view = self
|
|
462
537
|
|
|
463
|
-
def
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
538
|
+
def initialize_all_base_fields_form(self, request) -> None:
|
|
539
|
+
params = {
|
|
540
|
+
"form": self.form,
|
|
541
|
+
"fields": "__all__",
|
|
542
|
+
"formfield_callback": partial(self.formfield_for_dbfield, request=request),
|
|
543
|
+
}
|
|
544
|
+
self.all_base_fields_form = modelform_factory(self.model, **params)
|
|
467
545
|
|
|
468
546
|
|
|
469
547
|
class SBAdminThirdParty(SBAdminInlineAndAdminCommon, SBAdminBaseView):
|
|
470
|
-
def get_menu_view_url(self, request):
|
|
548
|
+
def get_menu_view_url(self, request) -> str:
|
|
471
549
|
return reverse(f"sb_admin:{self.get_id()}_changelist")
|
|
472
550
|
|
|
473
|
-
def get_id(self):
|
|
551
|
+
def get_id(self) -> str:
|
|
474
552
|
return self.get_model_path()
|
|
475
553
|
|
|
476
554
|
def change_view(self, request, object_id, form_url="", extra_context=None):
|
|
@@ -483,10 +561,14 @@ class SBAdminThirdParty(SBAdminInlineAndAdminCommon, SBAdminBaseView):
|
|
|
483
561
|
extra_context.update(self.get_global_context(request))
|
|
484
562
|
return super().changelist_view(request, extra_context)
|
|
485
563
|
|
|
486
|
-
def get_action_url(self, action, modifier="template"):
|
|
564
|
+
def get_action_url(self, action, modifier="template") -> str:
|
|
487
565
|
return reverse(
|
|
488
|
-
|
|
489
|
-
kwargs={
|
|
566
|
+
"sb_admin:sb_admin_base",
|
|
567
|
+
kwargs={
|
|
568
|
+
"view": self.get_id(),
|
|
569
|
+
"action": action,
|
|
570
|
+
"modifier": modifier,
|
|
571
|
+
},
|
|
490
572
|
)
|
|
491
573
|
|
|
492
574
|
|
|
@@ -498,7 +580,7 @@ class SBAdminTranslationStatusMixin:
|
|
|
498
580
|
main_language_code,
|
|
499
581
|
current_lang_code,
|
|
500
582
|
translations_edit_url,
|
|
501
|
-
):
|
|
583
|
+
) -> dict[str, Any]:
|
|
502
584
|
language_code = language[0]
|
|
503
585
|
language_title = language[1]
|
|
504
586
|
this_lang_count = languages_count.get(language_code, 0)
|
|
@@ -534,11 +616,11 @@ class SBAdminTranslationStatusMixin:
|
|
|
534
616
|
}
|
|
535
617
|
|
|
536
618
|
@classmethod
|
|
537
|
-
def get_empty_state(cls):
|
|
619
|
+
def get_empty_state(cls) -> SafeString:
|
|
538
620
|
return mark_safe("<div class='is-empty'></div>")
|
|
539
621
|
|
|
540
622
|
@admin.display(description="")
|
|
541
|
-
def sbadmin_translation_status(self, obj):
|
|
623
|
+
def sbadmin_translation_status(self, obj) -> SafeString:
|
|
542
624
|
if not SBAdminTranslationsService.is_i18n_enabled():
|
|
543
625
|
return self.get_empty_state()
|
|
544
626
|
|
|
@@ -582,6 +664,29 @@ class SBAdminTranslationStatusMixin:
|
|
|
582
664
|
return mark_safe(result)
|
|
583
665
|
|
|
584
666
|
|
|
667
|
+
class SBAdminInlineFormSetMixin:
|
|
668
|
+
@classmethod
|
|
669
|
+
def get_default_prefix(cls):
|
|
670
|
+
view = getattr(cls.form, "view", None)
|
|
671
|
+
if view and view.parent_model and view.opts:
|
|
672
|
+
parent_opts = view.parent_model._meta
|
|
673
|
+
opts = view.opts
|
|
674
|
+
modal_prefix = (
|
|
675
|
+
"modal_" if is_modal(SBAdminThreadLocalService.get_request()) else ""
|
|
676
|
+
)
|
|
677
|
+
return f"{modal_prefix}{parent_opts.app_label}_{parent_opts.model_name}_{opts.app_label}-{opts.model_name}"
|
|
678
|
+
|
|
679
|
+
return super().get_default_prefix()
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
class SBAdminGenericInlineFormSet(SBAdminInlineFormSetMixin, BaseGenericInlineFormSet):
|
|
683
|
+
pass
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
class SBAdminNestedInlineFormSet(SBAdminInlineFormSetMixin, NestedInlineFormSet):
|
|
687
|
+
pass
|
|
688
|
+
|
|
689
|
+
|
|
585
690
|
class SBAdmin(
|
|
586
691
|
SBAdminInlineAndAdminCommon,
|
|
587
692
|
SBAdminBaseQuerysetMixin,
|
|
@@ -590,8 +695,8 @@ class SBAdmin(
|
|
|
590
695
|
NestedModelAdmin,
|
|
591
696
|
):
|
|
592
697
|
change_list_template = "sb_admin/actions/list.html"
|
|
698
|
+
reorder_list_template = "sb_admin/actions/list.html"
|
|
593
699
|
change_form_template = "sb_admin/actions/change_form.html"
|
|
594
|
-
delete_confirmation_template = "sb_admin/actions/delete_confirmation.html"
|
|
595
700
|
delete_selected_confirmation_template = (
|
|
596
701
|
"sb_admin/actions/delete_selected_confirmation.html"
|
|
597
702
|
)
|
|
@@ -602,19 +707,29 @@ class SBAdmin(
|
|
|
602
707
|
sbadmin_tabs = None
|
|
603
708
|
request_data = None
|
|
604
709
|
menu_label = None
|
|
710
|
+
sbadmin_is_generic_model = False
|
|
711
|
+
|
|
712
|
+
def save_formset(self, request, form, formset, change):
|
|
713
|
+
if not change and hasattr(formset, "inline_instance"):
|
|
714
|
+
# update inline_instance parent_instance on formset when creating new object
|
|
715
|
+
formset.inline_instance.parent_instance = form.instance
|
|
716
|
+
super().save_formset(request, form, formset, change)
|
|
605
717
|
|
|
606
|
-
def get_sbadmin_list_filter(self, request):
|
|
718
|
+
def get_sbadmin_list_filter(self, request) -> Iterable:
|
|
607
719
|
return self.sbadmin_list_filter or self.get_list_filter(request)
|
|
608
720
|
|
|
609
721
|
def get_form(self, request, obj=None, **kwargs):
|
|
722
|
+
self.initialize_all_base_fields_form(request)
|
|
610
723
|
form = super().get_form(request, obj, **kwargs)
|
|
611
|
-
self.
|
|
724
|
+
self.initialize_form_class(form, request)
|
|
612
725
|
return form
|
|
613
726
|
|
|
614
|
-
def get_id(self):
|
|
727
|
+
def get_id(self) -> str:
|
|
615
728
|
return self.get_model_path()
|
|
616
729
|
|
|
617
|
-
def get_sbadmin_fieldsets(
|
|
730
|
+
def get_sbadmin_fieldsets(
|
|
731
|
+
self, request, object_id=None
|
|
732
|
+
) -> list[tuple[str | None, dict[str, Any]]]:
|
|
618
733
|
fieldsets = self.sbadmin_fieldsets or self.fieldsets
|
|
619
734
|
if fieldsets:
|
|
620
735
|
return fieldsets
|
|
@@ -623,9 +738,12 @@ class SBAdmin(
|
|
|
623
738
|
)
|
|
624
739
|
|
|
625
740
|
def register_autocomplete_views(self, request):
|
|
741
|
+
super().register_autocomplete_views(request)
|
|
626
742
|
self.get_form(request)()
|
|
627
743
|
|
|
628
|
-
def get_fieldsets(
|
|
744
|
+
def get_fieldsets(
|
|
745
|
+
self, request, obj=None
|
|
746
|
+
) -> list[tuple[str | None, dict[str, Any]]]:
|
|
629
747
|
fieldsets = []
|
|
630
748
|
object_id = obj.id if obj else None
|
|
631
749
|
for fieldset in self.get_sbadmin_fieldsets(request, object_id):
|
|
@@ -640,7 +758,9 @@ class SBAdmin(
|
|
|
640
758
|
fieldsets.append(fieldset_django)
|
|
641
759
|
return fieldsets
|
|
642
760
|
|
|
643
|
-
def get_fieldsets_context(
|
|
761
|
+
def get_fieldsets_context(
|
|
762
|
+
self, request, object_id
|
|
763
|
+
) -> dict[str, dict[str | None, dict[str, Any]]]:
|
|
644
764
|
fielsets_context = {}
|
|
645
765
|
for fieldset in self.get_sbadmin_fieldsets(request, object_id):
|
|
646
766
|
actions = fieldset[1].get("actions", [])
|
|
@@ -654,53 +774,69 @@ class SBAdmin(
|
|
|
654
774
|
fielsets_context[fieldset[0]] = fieldset[1]
|
|
655
775
|
return {"fieldsets_context": fielsets_context}
|
|
656
776
|
|
|
657
|
-
def get_sbadmin_tabs(self, request, object_id):
|
|
777
|
+
def get_sbadmin_tabs(self, request, object_id) -> Iterable:
|
|
658
778
|
return self.sbadmin_tabs
|
|
659
779
|
|
|
660
|
-
def get_tabs_context(self, request, object_id):
|
|
780
|
+
def get_tabs_context(self, request, object_id) -> dict[str, Iterable]:
|
|
661
781
|
return {"tabs_context": self.get_sbadmin_tabs(request, object_id)}
|
|
662
782
|
|
|
663
|
-
def get_context_data(self, request):
|
|
783
|
+
def get_context_data(self, request) -> dict[str, Any]:
|
|
664
784
|
return {
|
|
665
785
|
"base_change_list_template": self.change_list_template,
|
|
666
786
|
}
|
|
667
787
|
|
|
668
|
-
def get_menu_view_url(self, request):
|
|
788
|
+
def get_menu_view_url(self, request) -> str:
|
|
669
789
|
all_config = self.get_all_config(request)
|
|
670
790
|
url_suffix = ""
|
|
671
791
|
if all_config and all_config.get("all_params_changed", False):
|
|
672
|
-
url_params_dict =
|
|
792
|
+
url_params_dict = SBAdminViewService.process_url_params(
|
|
793
|
+
view_id=self.get_id(),
|
|
794
|
+
url_params=all_config.get("url_params"),
|
|
795
|
+
filter_version=self.get_filters_version(request),
|
|
796
|
+
)
|
|
673
797
|
if url_params_dict:
|
|
674
798
|
url_suffix = f"?{SBAdminViewService.build_list_url(self.get_id(), url_params_dict)}"
|
|
675
799
|
|
|
676
800
|
return f'{reverse(f"sb_admin:{self.get_id()}_changelist")}{url_suffix}'
|
|
677
801
|
|
|
678
|
-
def get_menu_label(self):
|
|
802
|
+
def get_menu_label(self) -> str:
|
|
679
803
|
return self.menu_label or self.model._meta.verbose_name_plural
|
|
680
804
|
|
|
681
|
-
def get_action_url(self, action, modifier="template"):
|
|
805
|
+
def get_action_url(self, action, modifier="template") -> str:
|
|
682
806
|
if not hasattr(self, action):
|
|
683
807
|
raise ImproperlyConfigured(f"Action {action} does not exist on {self}")
|
|
684
808
|
return reverse(
|
|
685
|
-
|
|
809
|
+
"sb_admin:sb_admin_base",
|
|
686
810
|
kwargs={
|
|
811
|
+
"view": self.get_id(),
|
|
687
812
|
"action": action,
|
|
688
|
-
"modifier":
|
|
689
|
-
urllib.parse.quote(str(modifier), safe="") if modifier else None
|
|
690
|
-
),
|
|
813
|
+
"modifier": modifier,
|
|
691
814
|
},
|
|
692
815
|
)
|
|
693
816
|
|
|
694
|
-
def get_detail_url(self, object_id=None):
|
|
817
|
+
def get_detail_url(self, object_id=None) -> str:
|
|
695
818
|
return reverse(
|
|
696
819
|
f"sb_admin:{self.get_id()}_change",
|
|
697
820
|
kwargs={"object_id": object_id or OBJECT_ID_PLACEHOLDER},
|
|
698
821
|
)
|
|
699
822
|
|
|
700
|
-
def get_new_url(self):
|
|
823
|
+
def get_new_url(self, request) -> str:
|
|
701
824
|
return reverse(f"sb_admin:{self.get_id()}_add")
|
|
702
825
|
|
|
703
|
-
def
|
|
826
|
+
def get_additional_filter_for_previous_next_context(self, request, object_id) -> Q:
|
|
827
|
+
return Q()
|
|
828
|
+
|
|
829
|
+
def get_change_view_context(self, request, object_id) -> dict | dict[str, Any]:
|
|
830
|
+
return {
|
|
831
|
+
"show_back_button": True,
|
|
832
|
+
"back_url": reverse(
|
|
833
|
+
"sb_admin:{}_{}_changelist".format(
|
|
834
|
+
self.opts.app_label, self.opts.model_name
|
|
835
|
+
)
|
|
836
|
+
),
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
def get_previous_next_context(self, request, object_id) -> dict | dict[str, Any]:
|
|
704
840
|
if not self.sbadmin_previous_next_buttons_enabled or not object_id:
|
|
705
841
|
return {}
|
|
706
842
|
changelist_filters = request.GET.get("_changelist_filters", "")
|
|
@@ -712,15 +848,20 @@ class SBAdmin(
|
|
|
712
848
|
)
|
|
713
849
|
except:
|
|
714
850
|
all_params = {}
|
|
715
|
-
list_action =
|
|
851
|
+
list_action = self.sbadmin_list_action_class(
|
|
852
|
+
self, request, all_params=all_params
|
|
853
|
+
)
|
|
854
|
+
additional_filter = self.get_additional_filter_for_previous_next_context(
|
|
855
|
+
request, object_id
|
|
856
|
+
)
|
|
716
857
|
all_ids = list(
|
|
717
|
-
list_action.build_final_data_count_queryset()
|
|
858
|
+
list_action.build_final_data_count_queryset(additional_filter)
|
|
718
859
|
.order_by(*list_action.get_order_by_from_request())
|
|
719
860
|
.values_list("id", flat=True)
|
|
720
861
|
)
|
|
721
862
|
index = all_ids.index(int(object_id))
|
|
722
|
-
previous_id =
|
|
723
|
-
next_id =
|
|
863
|
+
previous_id = all_ids[-1] if index == 0 else all_ids[index - 1]
|
|
864
|
+
next_id = all_ids[0] if index == len(all_ids) - 1 else all_ids[index + 1]
|
|
724
865
|
return {
|
|
725
866
|
"previous_url": (
|
|
726
867
|
f"{self.get_detail_url(previous_id)}?_changelist_filters={changelist_filters}"
|
|
@@ -736,8 +877,16 @@ class SBAdmin(
|
|
|
736
877
|
),
|
|
737
878
|
}
|
|
738
879
|
|
|
880
|
+
def add_view(self, request, form_url="", extra_context=None):
|
|
881
|
+
extra_context = extra_context or {}
|
|
882
|
+
extra_context.update(self.get_global_context(request, None))
|
|
883
|
+
extra_context.update(self.get_fieldsets_context(request, None))
|
|
884
|
+
extra_context.update(self.get_tabs_context(request, None))
|
|
885
|
+
return self.changeform_view(request, None, form_url, extra_context)
|
|
886
|
+
|
|
739
887
|
def change_view(self, request, object_id, form_url="", extra_context=None):
|
|
740
888
|
extra_context = extra_context or {}
|
|
889
|
+
extra_context.update(self.get_change_view_context(request, object_id))
|
|
741
890
|
extra_context.update(self.get_global_context(request, object_id))
|
|
742
891
|
extra_context.update(self.get_fieldsets_context(request, object_id))
|
|
743
892
|
extra_context.update(self.get_tabs_context(request, object_id))
|
|
@@ -747,16 +896,6 @@ class SBAdmin(
|
|
|
747
896
|
def changelist_view(self, request, extra_context=None):
|
|
748
897
|
return self.action_list(request, extra_context=extra_context)
|
|
749
898
|
|
|
750
|
-
def _get_changed_field_labels_from_form(form, changed_data):
|
|
751
|
-
changed_field_labels = []
|
|
752
|
-
for field_name in changed_data:
|
|
753
|
-
try:
|
|
754
|
-
verbose_field_name = form.fields[field_name].label or field_name
|
|
755
|
-
except KeyError:
|
|
756
|
-
verbose_field_name = field_name
|
|
757
|
-
changed_field_labels.append(str(verbose_field_name))
|
|
758
|
-
return changed_field_labels
|
|
759
|
-
|
|
760
899
|
def history_view(self, request, object_id, extra_context=None):
|
|
761
900
|
try:
|
|
762
901
|
"The 'history' admin view for this model."
|
|
@@ -821,6 +960,52 @@ class SBAdmin(
|
|
|
821
960
|
except Exception as e:
|
|
822
961
|
return super().history_view(request, object_id, extra_context)
|
|
823
962
|
|
|
963
|
+
@classmethod
|
|
964
|
+
def get_modal_save_response(cls, request, obj):
|
|
965
|
+
response = HttpResponse()
|
|
966
|
+
trigger_client_event(
|
|
967
|
+
response,
|
|
968
|
+
"sbadmin:modal-change-form-response",
|
|
969
|
+
{
|
|
970
|
+
"field": request.POST.get("sb_admin_source_field"),
|
|
971
|
+
"id": obj.pk,
|
|
972
|
+
"label": str(obj),
|
|
973
|
+
"reload": request.POST.get(SBADMIN_RELOAD_ON_SAVE_VAR) == "1",
|
|
974
|
+
},
|
|
975
|
+
)
|
|
976
|
+
trigger_client_event(response, "hideModal", {"elt": "sb-admin-modal"})
|
|
977
|
+
return response
|
|
978
|
+
|
|
979
|
+
def response_add(self, request, obj, post_url_continue=None):
|
|
980
|
+
if is_modal(request):
|
|
981
|
+
return self.get_modal_save_response(request, obj)
|
|
982
|
+
return super().response_add(request, obj, post_url_continue)
|
|
983
|
+
|
|
984
|
+
def response_change(self, request, obj):
|
|
985
|
+
if is_modal(request):
|
|
986
|
+
return self.get_modal_save_response(request, obj)
|
|
987
|
+
return super().response_change(request, obj)
|
|
988
|
+
|
|
989
|
+
@classmethod
|
|
990
|
+
def set_generic_relation_from_parent(cls, request, obj):
|
|
991
|
+
parent_model_path = request.POST.get(SBADMIN_PARENT_INSTANCE_FIELD_NAME_VAR)
|
|
992
|
+
parent_pk = request.POST.get(SBADMIN_PARENT_INSTANCE_PK_VAR)
|
|
993
|
+
|
|
994
|
+
if parent_model_path and parent_pk:
|
|
995
|
+
prefix, app_label, model_name, field, parent_model = (
|
|
996
|
+
parent_model_path.split("_", 5)
|
|
997
|
+
)
|
|
998
|
+
content_type = ContentType.objects.get(
|
|
999
|
+
app_label=app_label, model=parent_model
|
|
1000
|
+
)
|
|
1001
|
+
obj.content_type = content_type
|
|
1002
|
+
obj.object_id = int(parent_pk)
|
|
1003
|
+
|
|
1004
|
+
def save_model(self, request, obj, form, change):
|
|
1005
|
+
if self.sbadmin_is_generic_model and SBADMIN_IS_MODAL_VAR in request.POST:
|
|
1006
|
+
self.set_generic_relation_from_parent(request, obj)
|
|
1007
|
+
super().save_model(request, obj, form, change)
|
|
1008
|
+
|
|
824
1009
|
|
|
825
1010
|
class SBAdminInline(
|
|
826
1011
|
SBAdminInlineAndAdminCommon, SBAdminBaseQuerysetMixin, SBAdminBaseView
|
|
@@ -831,21 +1016,43 @@ class SBAdminInline(
|
|
|
831
1016
|
sbadmin_inline_list_actions = None
|
|
832
1017
|
extra = 0
|
|
833
1018
|
ordering = None
|
|
834
|
-
|
|
835
|
-
|
|
1019
|
+
all_base_fields_form = None
|
|
1020
|
+
sb_admin_add_modal = False
|
|
1021
|
+
|
|
1022
|
+
def get_instance_label(self, request, obj: Model | None = None) -> str | None:
|
|
1023
|
+
if obj:
|
|
1024
|
+
return str(obj)
|
|
1025
|
+
return None
|
|
1026
|
+
|
|
1027
|
+
def get_readonly_fields(self, request, obj=None):
|
|
1028
|
+
readonly_fields = super().get_readonly_fields(request, obj)
|
|
1029
|
+
if ROW_CLASS_FIELD not in readonly_fields:
|
|
1030
|
+
readonly_fields += (ROW_CLASS_FIELD,)
|
|
1031
|
+
return readonly_fields
|
|
1032
|
+
|
|
1033
|
+
def get_fields(self, request, obj=None):
|
|
1034
|
+
fields = super().get_fields(request, obj)
|
|
1035
|
+
if ROW_CLASS_FIELD not in fields:
|
|
1036
|
+
fields += (ROW_CLASS_FIELD,)
|
|
1037
|
+
return fields
|
|
1038
|
+
|
|
1039
|
+
def get_sbadmin_row_class(self, obj):
|
|
1040
|
+
return ""
|
|
1041
|
+
|
|
1042
|
+
def get_ordering(self, request) -> tuple[str]:
|
|
836
1043
|
"""
|
|
837
1044
|
Hook for specifying field ordering.
|
|
838
1045
|
"""
|
|
839
1046
|
return self.ordering or ("-id",)
|
|
840
1047
|
|
|
841
|
-
def get_queryset(self, request=None):
|
|
1048
|
+
def get_queryset(self, request=None) -> QuerySet:
|
|
842
1049
|
qs = super().get_queryset(request)
|
|
843
1050
|
return qs.order_by(*self.get_ordering(request))
|
|
844
1051
|
|
|
845
|
-
def get_sbadmin_inline_list_actions(self):
|
|
1052
|
+
def get_sbadmin_inline_list_actions(self, request) -> list:
|
|
846
1053
|
return [*(self.sbadmin_inline_list_actions or [])]
|
|
847
1054
|
|
|
848
|
-
def get_action_url(self, action, modifier="template"):
|
|
1055
|
+
def get_action_url(self, action, modifier="template") -> str:
|
|
849
1056
|
return reverse(
|
|
850
1057
|
"sb_admin:sb_admin_base",
|
|
851
1058
|
kwargs={
|
|
@@ -855,17 +1062,72 @@ class SBAdminInline(
|
|
|
855
1062
|
},
|
|
856
1063
|
)
|
|
857
1064
|
|
|
858
|
-
def register_autocomplete_views(self, request):
|
|
1065
|
+
def register_autocomplete_views(self, request) -> None:
|
|
859
1066
|
super().register_autocomplete_views(request)
|
|
860
1067
|
form_class = self.get_formset(request, self.model()).form
|
|
861
|
-
self.
|
|
1068
|
+
self.initialize_form_class(form_class, request)
|
|
862
1069
|
form_class()
|
|
863
1070
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1071
|
+
def get_parent_instance_from_request(self):
|
|
1072
|
+
# Try to get parent instance from request referrer
|
|
1073
|
+
request = (
|
|
1074
|
+
getattr(self, "threadsafe_request", None)
|
|
1075
|
+
or SBAdminThreadLocalService.get_request()
|
|
1076
|
+
)
|
|
1077
|
+
allowed = SBAdminViewService.has_permission(
|
|
1078
|
+
request=request, model=self.parent_model, permission="view"
|
|
1079
|
+
)
|
|
1080
|
+
if not allowed:
|
|
1081
|
+
return None
|
|
1082
|
+
|
|
1083
|
+
referer = request.META.get("HTTP_REFERER")
|
|
1084
|
+
if not referer:
|
|
1085
|
+
return None
|
|
1086
|
+
resolved = resolve(urlparse(referer).path)
|
|
1087
|
+
# Try common kwargs for object ID
|
|
1088
|
+
object_id = resolved.kwargs.get("object_id")
|
|
1089
|
+
if not object_id:
|
|
1090
|
+
return None
|
|
1091
|
+
base_qs = SBAdminViewService.get_restricted_queryset(
|
|
1092
|
+
self.parent_model, request, request.request_data
|
|
1093
|
+
)
|
|
1094
|
+
return base_qs.get(pk=object_id)
|
|
1095
|
+
|
|
1096
|
+
def get_context_data(self, request) -> dict[str, Any]:
|
|
1097
|
+
is_sortable_active: bool = self.sortable_field_name and (
|
|
1098
|
+
self.has_add_permission(request) or self.has_change_permission(request)
|
|
1099
|
+
)
|
|
1100
|
+
add_url = None
|
|
1101
|
+
try:
|
|
1102
|
+
if self.sb_admin_add_modal and self.has_add_permission(request):
|
|
1103
|
+
add_url = reverse(
|
|
1104
|
+
"sb_admin:{}_{}_add".format(
|
|
1105
|
+
self.opts.app_label, self.opts.model_name
|
|
1106
|
+
)
|
|
1107
|
+
)
|
|
1108
|
+
except NoReverseMatch:
|
|
1109
|
+
logger.warning(
|
|
1110
|
+
"To use Add in modal, You have to specify SBAdmin view for %s model",
|
|
1111
|
+
self.opts.model_name,
|
|
1112
|
+
)
|
|
1113
|
+
context_data = {
|
|
1114
|
+
"inline_list_actions": self.get_sbadmin_inline_list_actions(request),
|
|
1115
|
+
"is_sortable_active": is_sortable_active,
|
|
1116
|
+
"add_url": add_url,
|
|
1117
|
+
}
|
|
1118
|
+
if self.parent_instance:
|
|
1119
|
+
context_data["parent_data"] = {
|
|
1120
|
+
SBADMIN_PARENT_INSTANCE_PK_VAR: self.parent_instance.pk,
|
|
1121
|
+
SBADMIN_PARENT_INSTANCE_LABEL_VAR: str(self.parent_instance),
|
|
1122
|
+
SBADMIN_PARENT_INSTANCE_FIELD_NAME_VAR: "{}_{}_id_{}".format(
|
|
1123
|
+
self.model._meta.app_label,
|
|
1124
|
+
self.model._meta.model_name,
|
|
1125
|
+
self.parent_model._meta.model_name,
|
|
1126
|
+
),
|
|
1127
|
+
}
|
|
1128
|
+
return context_data
|
|
867
1129
|
|
|
868
|
-
def init_sortable_field(self):
|
|
1130
|
+
def init_sortable_field(self) -> None:
|
|
869
1131
|
if not self.sortable_field_name:
|
|
870
1132
|
for field_name in self.sbadmin_sortable_field_options:
|
|
871
1133
|
is_sortable_field_present = False
|
|
@@ -881,15 +1143,15 @@ class SBAdminInline(
|
|
|
881
1143
|
self.init_sortable_field()
|
|
882
1144
|
super().__init__(parent_model, admin_site)
|
|
883
1145
|
|
|
884
|
-
def init_view_dynamic(self, request, request_data=None, **kwargs):
|
|
1146
|
+
def init_view_dynamic(self, request, request_data=None, **kwargs) -> None:
|
|
885
1147
|
return super().init_view_dynamic(request, request_data, **kwargs)
|
|
886
1148
|
|
|
887
|
-
def get_id(self):
|
|
1149
|
+
def get_id(self) -> str:
|
|
888
1150
|
return (
|
|
889
1151
|
f"{self.__class__.__name__}_{SBAdminViewService.get_model_path(self.model)}"
|
|
890
1152
|
)
|
|
891
1153
|
|
|
892
|
-
def init_inline_dynamic(self, request, obj=None):
|
|
1154
|
+
def init_inline_dynamic(self, request, obj=None) -> None:
|
|
893
1155
|
self.threadsafe_request = request
|
|
894
1156
|
self.parent_instance = obj
|
|
895
1157
|
|
|
@@ -900,18 +1162,21 @@ class SBAdminInline(
|
|
|
900
1162
|
return formfield
|
|
901
1163
|
|
|
902
1164
|
def get_formset(self, request, obj=None, **kwargs):
|
|
1165
|
+
self.initialize_all_base_fields_form(request)
|
|
903
1166
|
formset = super().get_formset(request, obj, **kwargs)
|
|
904
1167
|
form_class = formset.form
|
|
905
|
-
self.
|
|
1168
|
+
self.initialize_form_class(form_class, request)
|
|
906
1169
|
return formset
|
|
907
1170
|
|
|
908
1171
|
|
|
909
1172
|
class SBAdminTableInline(SBAdminInline, NestedTabularInline):
|
|
910
1173
|
template = "sb_admin/inlines/table_inline.html"
|
|
1174
|
+
formset = SBAdminNestedInlineFormSet
|
|
911
1175
|
|
|
912
1176
|
|
|
913
1177
|
class SBAdminGenericTableInline(SBAdminInline, NestedGenericTabularInline):
|
|
914
1178
|
template = "sb_admin/inlines/table_inline.html"
|
|
1179
|
+
formset = SBAdminGenericInlineFormSet
|
|
915
1180
|
|
|
916
1181
|
|
|
917
1182
|
class SBAdminTableInlinePaginated(SBAdminTableInline, TabularInlinePaginated):
|
|
@@ -924,11 +1189,51 @@ class SBAdminGenericTableInlinePaginated(SBAdminGenericTableInline):
|
|
|
924
1189
|
per_page = 50
|
|
925
1190
|
|
|
926
1191
|
|
|
927
|
-
class
|
|
1192
|
+
class SBAdminStackedInlineBase(SBAdminInline):
|
|
1193
|
+
default_collapsed = False
|
|
1194
|
+
|
|
1195
|
+
def get_sbadmin_default_collapsed(self, request):
|
|
1196
|
+
return self.default_collapsed
|
|
1197
|
+
|
|
1198
|
+
def get_context_data(self, request) -> dict[str, Any]:
|
|
1199
|
+
context_data = super().get_context_data(request)
|
|
1200
|
+
context_data["default_collapsed"] = self.get_sbadmin_default_collapsed(request)
|
|
1201
|
+
return context_data
|
|
1202
|
+
|
|
1203
|
+
def get_sbadmin_inline_list_actions(self, request) -> list:
|
|
1204
|
+
actions = super().get_sbadmin_inline_list_actions(request)
|
|
1205
|
+
actions.append(
|
|
1206
|
+
SBAdminCustomAction(
|
|
1207
|
+
title="Collapse",
|
|
1208
|
+
css_class=f"collapse-all-stacked-inlines {'collapsed' if self.get_sbadmin_default_collapsed(request) else ''}",
|
|
1209
|
+
url=request.get_full_path(),
|
|
1210
|
+
)
|
|
1211
|
+
)
|
|
1212
|
+
return actions
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
class SBAdminStackedInline(SBAdminStackedInlineBase, NestedStackedInline):
|
|
928
1216
|
template = "sb_admin/inlines/stacked_inline.html"
|
|
929
1217
|
fieldset_template = "sb_admin/includes/inline_fieldset.html"
|
|
1218
|
+
formset = SBAdminNestedInlineFormSet
|
|
930
1219
|
|
|
931
1220
|
|
|
932
|
-
class SBAdminGenericStackedInline(
|
|
1221
|
+
class SBAdminGenericStackedInline(SBAdminStackedInlineBase, NestedGenericStackedInline):
|
|
933
1222
|
template = "sb_admin/inlines/stacked_inline.html"
|
|
934
1223
|
fieldset_template = "sb_admin/includes/inline_fieldset.html"
|
|
1224
|
+
formset = SBAdminGenericInlineFormSet
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
if parler_enabled:
|
|
1228
|
+
|
|
1229
|
+
class SBTranslatableAdmin(SBAdmin, TranslatableAdmin):
|
|
1230
|
+
def get_readonly_fields(self, request, obj=...):
|
|
1231
|
+
readonly_fields = super().get_readonly_fields(request, obj)
|
|
1232
|
+
if "sbadmin_translation_status" not in readonly_fields:
|
|
1233
|
+
readonly_fields += ("sbadmin_translation_status",)
|
|
1234
|
+
return readonly_fields
|
|
1235
|
+
|
|
1236
|
+
def get_fieldsets(self, request, obj=...):
|
|
1237
|
+
fieldsets = super().get_fieldsets(request, obj)
|
|
1238
|
+
fieldsets.append(SBAdminTranslationsService.get_translation_fieldset())
|
|
1239
|
+
return fieldsets
|