django-smartbase-admin 0.2.72__py3-none-any.whl → 0.2.74__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 +10 -1
- django_smartbase_admin/admin/admin_base.py +30 -15
- django_smartbase_admin/admin/site.py +1 -2
- django_smartbase_admin/admin/widgets.py +36 -8
- django_smartbase_admin/engine/actions.py +20 -16
- django_smartbase_admin/engine/admin_base_view.py +47 -5
- django_smartbase_admin/engine/field.py +6 -1
- django_smartbase_admin/engine/modal_view.py +8 -4
- django_smartbase_admin/services/thread_local.py +6 -19
- django_smartbase_admin/services/views.py +47 -17
- django_smartbase_admin/static/sb_admin/dist/main_style.css +1 -1
- django_smartbase_admin/static/sb_admin/src/css/components/_query-builder.css +4 -0
- django_smartbase_admin/templates/sb_admin/integrations/sorting/change_list.html +2 -2
- django_smartbase_admin/templates/sb_admin/widgets/array.html +0 -1
- django_smartbase_admin/templates/sb_admin/widgets/attributes.html +67 -0
- {django_smartbase_admin-0.2.72.dist-info → django_smartbase_admin-0.2.74.dist-info}/METADATA +1 -1
- {django_smartbase_admin-0.2.72.dist-info → django_smartbase_admin-0.2.74.dist-info}/RECORD +19 -18
- {django_smartbase_admin-0.2.72.dist-info → django_smartbase_admin-0.2.74.dist-info}/LICENSE.md +0 -0
- {django_smartbase_admin-0.2.72.dist-info → django_smartbase_admin-0.2.74.dist-info}/WHEEL +0 -0
|
@@ -6,7 +6,6 @@ from django.db.models import Q
|
|
|
6
6
|
from django.utils import timezone
|
|
7
7
|
from django.utils.text import smart_split, unescape_string_literal
|
|
8
8
|
|
|
9
|
-
from django_smartbase_admin.engine.actions import SBAdminAction
|
|
10
9
|
from django_smartbase_admin.engine.const import (
|
|
11
10
|
XLSX_PAGE_CHUNK_SIZE,
|
|
12
11
|
SELECTED_ROWS_KWARG_NAME,
|
|
@@ -43,6 +42,16 @@ QueryBuilderService = import_with_injection(
|
|
|
43
42
|
)
|
|
44
43
|
|
|
45
44
|
|
|
45
|
+
class SBAdminAction(object):
|
|
46
|
+
view = None
|
|
47
|
+
threadsafe_request = None
|
|
48
|
+
|
|
49
|
+
def __init__(self, view, request) -> None:
|
|
50
|
+
super().__init__()
|
|
51
|
+
self.view = view
|
|
52
|
+
self.threadsafe_request = request
|
|
53
|
+
|
|
54
|
+
|
|
46
55
|
class SBAdminListAction(SBAdminAction):
|
|
47
56
|
def __init__(
|
|
48
57
|
self,
|
|
@@ -37,6 +37,7 @@ from nested_admin.nested import (
|
|
|
37
37
|
|
|
38
38
|
from django_smartbase_admin.actions.admin_action_list import SBAdminListAction
|
|
39
39
|
from django_smartbase_admin.engine.actions import SBAdminCustomAction
|
|
40
|
+
from django_smartbase_admin.services.thread_local import SBAdminThreadLocalService
|
|
40
41
|
from django_smartbase_admin.utils import FormFieldsetMixin
|
|
41
42
|
|
|
42
43
|
parler_enabled = None
|
|
@@ -62,6 +63,16 @@ try:
|
|
|
62
63
|
except ImportError:
|
|
63
64
|
pass
|
|
64
65
|
|
|
66
|
+
|
|
67
|
+
django_cms_attributes = None
|
|
68
|
+
try:
|
|
69
|
+
from djangocms_attributes_field.fields import AttributesFormField
|
|
70
|
+
|
|
71
|
+
django_cms_attributes = True
|
|
72
|
+
except ImportError:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
65
76
|
from django_smartbase_admin.admin.widgets import (
|
|
66
77
|
SBAdminTextInputWidget,
|
|
67
78
|
SBAdminTextareaWidget,
|
|
@@ -83,6 +94,8 @@ from django_smartbase_admin.admin.widgets import (
|
|
|
83
94
|
SBAdminReadOnlyPasswordHashWidget,
|
|
84
95
|
SBAdminHiddenWidget,
|
|
85
96
|
SBAdminCKEditorUploadingWidget,
|
|
97
|
+
SBAdminAttributesWidget,
|
|
98
|
+
SBAdminMultipleChoiceInlineWidget,
|
|
86
99
|
)
|
|
87
100
|
from django_smartbase_admin.engine.admin_base_view import (
|
|
88
101
|
SBAdminBaseListView,
|
|
@@ -121,6 +134,8 @@ class SBAdminFormFieldWidgetsMixin:
|
|
|
121
134
|
RichTextUploadingFormField: SBAdminCKEditorUploadingWidget,
|
|
122
135
|
forms.ChoiceField: SBAdminSelectWidget,
|
|
123
136
|
forms.TypedChoiceField: SBAdminSelectWidget,
|
|
137
|
+
forms.MultipleChoiceField: SBAdminMultipleChoiceInlineWidget,
|
|
138
|
+
forms.TypedMultipleChoiceField: SBAdminMultipleChoiceInlineWidget,
|
|
124
139
|
forms.NullBooleanField: SBAdminNullBooleanSelectWidget,
|
|
125
140
|
AdminImageFormField: SBAdminImageWidget,
|
|
126
141
|
ReadOnlyPasswordHashWidget: SBAdminReadOnlyPasswordHashWidget,
|
|
@@ -128,6 +143,8 @@ class SBAdminFormFieldWidgetsMixin:
|
|
|
128
143
|
}
|
|
129
144
|
if postrgres_enabled:
|
|
130
145
|
formfield_widgets[SimpleArrayField] = SBAdminArrayWidget
|
|
146
|
+
if django_cms_attributes:
|
|
147
|
+
formfield_widgets[AttributesFormField] = SBAdminAttributesWidget
|
|
131
148
|
|
|
132
149
|
django_widget_to_widget = {
|
|
133
150
|
forms.PasswordInput: SBAdminPasswordInputWidget,
|
|
@@ -250,12 +267,13 @@ class SBAdminFormFieldWidgetsMixin:
|
|
|
250
267
|
|
|
251
268
|
|
|
252
269
|
class SBAdminBaseFormInit(SBAdminFormFieldWidgetsMixin, FormFieldsetMixin):
|
|
253
|
-
threadsafe_request = None
|
|
254
270
|
view = None
|
|
255
271
|
|
|
256
272
|
def __init__(self, *args, **kwargs):
|
|
257
273
|
self.view = kwargs.pop("view", self.view)
|
|
258
|
-
|
|
274
|
+
threadsafe_request = kwargs.pop(
|
|
275
|
+
"request", SBAdminThreadLocalService.get_request()
|
|
276
|
+
)
|
|
259
277
|
super().__init__(*args, **kwargs)
|
|
260
278
|
for field in self.fields:
|
|
261
279
|
if not hasattr(self.fields[field].widget, "init_widget_dynamic"):
|
|
@@ -265,14 +283,12 @@ class SBAdminBaseFormInit(SBAdminFormFieldWidgetsMixin, FormFieldsetMixin):
|
|
|
265
283
|
self.fields[field],
|
|
266
284
|
field,
|
|
267
285
|
self.view,
|
|
268
|
-
|
|
286
|
+
threadsafe_request,
|
|
269
287
|
)
|
|
270
288
|
for field in self.declared_fields:
|
|
271
289
|
form_field = self.fields.get(field)
|
|
272
290
|
if form_field:
|
|
273
|
-
self.assign_widget_to_form_field(
|
|
274
|
-
form_field, request=self.threadsafe_request
|
|
275
|
-
)
|
|
291
|
+
self.assign_widget_to_form_field(form_field, request=threadsafe_request)
|
|
276
292
|
|
|
277
293
|
|
|
278
294
|
class SBAdminBaseForm(SBAdminBaseFormInit, forms.ModelForm):
|
|
@@ -472,11 +488,6 @@ class SBAdminInlineAndAdminCommon(SBAdminFormFieldWidgetsMixin):
|
|
|
472
488
|
if form:
|
|
473
489
|
form.view = self
|
|
474
490
|
|
|
475
|
-
def initialize_form_class_threadsafe(self, form, request):
|
|
476
|
-
self.initialize_form_class(form)
|
|
477
|
-
if form:
|
|
478
|
-
form.threadsafe_request = request
|
|
479
|
-
|
|
480
491
|
|
|
481
492
|
class SBAdminThirdParty(SBAdminInlineAndAdminCommon, SBAdminBaseView):
|
|
482
493
|
def get_menu_view_url(self, request):
|
|
@@ -630,7 +641,7 @@ class SBAdmin(
|
|
|
630
641
|
|
|
631
642
|
def get_form(self, request, obj=None, **kwargs):
|
|
632
643
|
form = super().get_form(request, obj, **kwargs)
|
|
633
|
-
self.
|
|
644
|
+
self.initialize_form_class(form)
|
|
634
645
|
return form
|
|
635
646
|
|
|
636
647
|
def get_id(self):
|
|
@@ -692,7 +703,11 @@ class SBAdmin(
|
|
|
692
703
|
all_config = self.get_all_config(request)
|
|
693
704
|
url_suffix = ""
|
|
694
705
|
if all_config and all_config.get("all_params_changed", False):
|
|
695
|
-
url_params_dict =
|
|
706
|
+
url_params_dict = SBAdminViewService.process_url_params(
|
|
707
|
+
view_id=self.get_id(),
|
|
708
|
+
url_params=all_config.get("url_params"),
|
|
709
|
+
filter_version=self.get_filters_version(request),
|
|
710
|
+
)
|
|
696
711
|
if url_params_dict:
|
|
697
712
|
url_suffix = f"?{SBAdminViewService.build_list_url(self.get_id(), url_params_dict)}"
|
|
698
713
|
|
|
@@ -899,7 +914,7 @@ class SBAdminInline(
|
|
|
899
914
|
def register_autocomplete_views(self, request):
|
|
900
915
|
super().register_autocomplete_views(request)
|
|
901
916
|
form_class = self.get_formset(request, self.model()).form
|
|
902
|
-
self.
|
|
917
|
+
self.initialize_form_class(form_class)
|
|
903
918
|
form_class()
|
|
904
919
|
|
|
905
920
|
@property
|
|
@@ -943,7 +958,7 @@ class SBAdminInline(
|
|
|
943
958
|
def get_formset(self, request, obj=None, **kwargs):
|
|
944
959
|
formset = super().get_formset(request, obj, **kwargs)
|
|
945
960
|
form_class = formset.form
|
|
946
|
-
self.
|
|
961
|
+
self.initialize_form_class(form_class)
|
|
947
962
|
return formset
|
|
948
963
|
|
|
949
964
|
|
|
@@ -34,7 +34,7 @@ class SBAdminSite(admin.AdminSite):
|
|
|
34
34
|
request.sbadmin_selected_view = selected_view
|
|
35
35
|
kwargs["view"] = selected_view.get_id() if selected_view else None
|
|
36
36
|
request_data = SBAdminViewRequestData.from_request_and_kwargs(request, **kwargs)
|
|
37
|
-
SBAdminThreadLocalService.
|
|
37
|
+
SBAdminThreadLocalService.set_request(request)
|
|
38
38
|
if selected_view:
|
|
39
39
|
# Initialize SBAdmin, ModelAdmin instances, class-based SBAdminEntrypointView are initialized with request_data
|
|
40
40
|
selected_view.init_view_dynamic(
|
|
@@ -49,7 +49,6 @@ class SBAdminSite(admin.AdminSite):
|
|
|
49
49
|
response
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
SBAdminThreadLocalService.clear_data()
|
|
53
52
|
return response
|
|
54
53
|
|
|
55
54
|
def admin_view(self, view_function, cacheable=False):
|
|
@@ -16,6 +16,7 @@ from filer.fields.image import AdminImageWidget
|
|
|
16
16
|
from django_smartbase_admin.engine.filter_widgets import (
|
|
17
17
|
AutocompleteFilterWidget,
|
|
18
18
|
)
|
|
19
|
+
from django_smartbase_admin.services.thread_local import SBAdminThreadLocalService
|
|
19
20
|
from django_smartbase_admin.templatetags.sb_admin_tags import SBAdminJSONEncoder
|
|
20
21
|
|
|
21
22
|
|
|
@@ -249,6 +250,27 @@ class SBAdminArrayWidget(SBAdminTextInputWidget):
|
|
|
249
250
|
return context
|
|
250
251
|
|
|
251
252
|
|
|
253
|
+
class SBAdminAttributesWidget(SBAdminTextInputWidget):
|
|
254
|
+
template_name = "sb_admin/widgets/attributes.html"
|
|
255
|
+
|
|
256
|
+
def get_context(self, name, value, attrs):
|
|
257
|
+
context = super().get_context(name, value, attrs)
|
|
258
|
+
widget = context.get("widget", None)
|
|
259
|
+
dict_widgets = []
|
|
260
|
+
template_widget = {"attrs": {"class": "input"}}
|
|
261
|
+
if widget and value:
|
|
262
|
+
dict_widgets = [
|
|
263
|
+
{
|
|
264
|
+
"key": {"value": key, **template_widget},
|
|
265
|
+
"value": {"value": value, **template_widget},
|
|
266
|
+
}
|
|
267
|
+
for key, value in value.items()
|
|
268
|
+
]
|
|
269
|
+
context["dict_widgets"] = dict_widgets
|
|
270
|
+
context["template_widget"] = template_widget
|
|
271
|
+
return context
|
|
272
|
+
|
|
273
|
+
|
|
252
274
|
class SBAdminAutocompleteWidget(
|
|
253
275
|
SBAdminBaseWidget, AutocompleteFilterWidget, forms.Widget
|
|
254
276
|
):
|
|
@@ -256,7 +278,6 @@ class SBAdminAutocompleteWidget(
|
|
|
256
278
|
view = None
|
|
257
279
|
form = None
|
|
258
280
|
field_name = None
|
|
259
|
-
threadsafe_request = None
|
|
260
281
|
initialised = None
|
|
261
282
|
|
|
262
283
|
def __init__(self, form_field=None, *args, **kwargs):
|
|
@@ -264,6 +285,12 @@ class SBAdminAutocompleteWidget(
|
|
|
264
285
|
super().__init__(form_field, *args, **kwargs)
|
|
265
286
|
self.attrs = {} if attrs is None else attrs.copy()
|
|
266
287
|
|
|
288
|
+
def get_id(self):
|
|
289
|
+
base_id = super().get_id()
|
|
290
|
+
if self.form:
|
|
291
|
+
base_id += f"_{self.form.__class__.__name__}"
|
|
292
|
+
return base_id
|
|
293
|
+
|
|
267
294
|
def init_widget_dynamic(self, form, form_field, field_name, view, request):
|
|
268
295
|
super().init_widget_dynamic(form, form_field, field_name, view, request)
|
|
269
296
|
if self.initialised:
|
|
@@ -272,11 +299,10 @@ class SBAdminAutocompleteWidget(
|
|
|
272
299
|
self.field_name = field_name
|
|
273
300
|
self.view = view
|
|
274
301
|
self.form = form
|
|
275
|
-
self.threadsafe_request = request
|
|
276
302
|
self.init_autocomplete_widget_static(
|
|
277
303
|
self.field_name,
|
|
278
304
|
self.model,
|
|
279
|
-
|
|
305
|
+
request.request_data.configuration,
|
|
280
306
|
)
|
|
281
307
|
|
|
282
308
|
def get_field_name(self):
|
|
@@ -294,20 +320,21 @@ class SBAdminAutocompleteWidget(
|
|
|
294
320
|
getattr(self.form_field, "empty_label", "---------") or "---------"
|
|
295
321
|
)
|
|
296
322
|
query_suffix = "__in"
|
|
323
|
+
threadsafe_request = SBAdminThreadLocalService.get_request()
|
|
297
324
|
if not self.is_multiselect():
|
|
298
325
|
query_suffix = ""
|
|
299
326
|
self.multiselect = False
|
|
300
327
|
if value:
|
|
301
|
-
parsed_value = self.parse_value_from_input(
|
|
328
|
+
parsed_value = self.parse_value_from_input(threadsafe_request, value)
|
|
302
329
|
if parsed_value:
|
|
303
330
|
selected_options = []
|
|
304
|
-
for item in self.get_queryset(
|
|
331
|
+
for item in self.get_queryset(threadsafe_request).filter(
|
|
305
332
|
**{f"{self.get_value_field()}{query_suffix}": parsed_value}
|
|
306
333
|
):
|
|
307
334
|
selected_options.append(
|
|
308
335
|
{
|
|
309
|
-
"value": self.get_value(
|
|
310
|
-
"label": self.get_label(
|
|
336
|
+
"value": self.get_value(threadsafe_request, item),
|
|
337
|
+
"label": self.get_label(threadsafe_request, item),
|
|
311
338
|
}
|
|
312
339
|
)
|
|
313
340
|
context["widget"]["value"] = json.dumps(selected_options)
|
|
@@ -322,7 +349,8 @@ class SBAdminAutocompleteWidget(
|
|
|
322
349
|
|
|
323
350
|
def value_from_datadict(self, data, files, name):
|
|
324
351
|
input_value = super().value_from_datadict(data, files, name)
|
|
325
|
-
|
|
352
|
+
threadsafe_request = SBAdminThreadLocalService.get_request()
|
|
353
|
+
parsed_value = self.parse_value_from_input(threadsafe_request, input_value)
|
|
326
354
|
if parsed_value is None:
|
|
327
355
|
return parsed_value
|
|
328
356
|
return parsed_value if self.is_multiselect() else next(iter(parsed_value), None)
|
|
@@ -24,12 +24,6 @@ class SBAdminCustomAction(object):
|
|
|
24
24
|
group=None,
|
|
25
25
|
) -> None:
|
|
26
26
|
super().__init__()
|
|
27
|
-
|
|
28
|
-
if not (url or (view and action_id)):
|
|
29
|
-
raise ImproperlyConfigured(
|
|
30
|
-
"You must provide either url or view and action_id"
|
|
31
|
-
)
|
|
32
|
-
|
|
33
27
|
self.title = title
|
|
34
28
|
self.url = url
|
|
35
29
|
self.view = view
|
|
@@ -39,17 +33,27 @@ class SBAdminCustomAction(object):
|
|
|
39
33
|
self.no_params = no_params
|
|
40
34
|
self.open_in_modal = open_in_modal
|
|
41
35
|
self.group = group
|
|
42
|
-
|
|
36
|
+
self.resolve_url()
|
|
37
|
+
|
|
38
|
+
def resolve_url(self):
|
|
39
|
+
if not (self.url or (self.view and self.action_id)):
|
|
40
|
+
raise ImproperlyConfigured(
|
|
41
|
+
"You must provide either url or view and action_id"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if not self.url and not self.action_modifier:
|
|
43
45
|
self.url = self.view.get_action_url(self.action_id)
|
|
44
|
-
if not url and action_modifier is not None:
|
|
45
|
-
self.url = self.view.get_action_url(self.action_id, action_modifier)
|
|
46
|
+
if not self.url and self.action_modifier is not None:
|
|
47
|
+
self.url = self.view.get_action_url(self.action_id, self.action_modifier)
|
|
46
48
|
|
|
47
49
|
|
|
48
|
-
class
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
class SBAdminFormViewAction(SBAdminCustomAction):
|
|
51
|
+
def __init__(self, target_view, *args, **kwargs) -> None:
|
|
52
|
+
self.target_view = target_view
|
|
53
|
+
super().__init__(*args, **kwargs)
|
|
51
54
|
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
self.
|
|
55
|
-
|
|
55
|
+
def resolve_url(self):
|
|
56
|
+
"""
|
|
57
|
+
self.url and self.action_id is resolved in side django_smartbase_admin.engine.admin_base_view.SBAdminBaseView.process_actions
|
|
58
|
+
"""
|
|
59
|
+
pass
|
|
@@ -13,7 +13,10 @@ from django.urls import reverse
|
|
|
13
13
|
from django.utils.translation import gettext_lazy as _
|
|
14
14
|
|
|
15
15
|
from django_smartbase_admin.actions.admin_action_list import SBAdminListAction
|
|
16
|
-
from django_smartbase_admin.engine.actions import
|
|
16
|
+
from django_smartbase_admin.engine.actions import (
|
|
17
|
+
SBAdminCustomAction,
|
|
18
|
+
SBAdminFormViewAction,
|
|
19
|
+
)
|
|
17
20
|
from django_smartbase_admin.engine.const import (
|
|
18
21
|
Action,
|
|
19
22
|
OBJECT_ID_PLACEHOLDER,
|
|
@@ -28,6 +31,7 @@ from django_smartbase_admin.engine.const import (
|
|
|
28
31
|
BASE_PARAMS_NAME,
|
|
29
32
|
TABLE_RELOAD_DATA_EVENT_NAME,
|
|
30
33
|
TABLE_UPDATE_ROW_DATA_EVENT_NAME,
|
|
34
|
+
FILTER_DATA_NAME,
|
|
31
35
|
)
|
|
32
36
|
from django_smartbase_admin.services.views import SBAdminViewService
|
|
33
37
|
from django_smartbase_admin.services.xlsx_export import (
|
|
@@ -82,6 +86,27 @@ class SBAdminBaseView(object):
|
|
|
82
86
|
request, obj
|
|
83
87
|
)
|
|
84
88
|
|
|
89
|
+
def delegate_to_action_view(self, processed_action):
|
|
90
|
+
def inner_view(request, modifier):
|
|
91
|
+
return processed_action.target_view.as_view(view=self)(request)
|
|
92
|
+
|
|
93
|
+
return inner_view
|
|
94
|
+
|
|
95
|
+
def process_actions(self, request, actions):
|
|
96
|
+
processed_actions = self.process_actions_permissions(request, actions)
|
|
97
|
+
for processed_action in processed_actions:
|
|
98
|
+
if isinstance(processed_action, SBAdminFormViewAction):
|
|
99
|
+
action_id = processed_action.target_view.__name__
|
|
100
|
+
setattr(
|
|
101
|
+
self,
|
|
102
|
+
action_id,
|
|
103
|
+
self.delegate_to_action_view(processed_action),
|
|
104
|
+
)
|
|
105
|
+
processed_action.url = self.get_action_url(action_id)
|
|
106
|
+
processed_action.action_id = action_id
|
|
107
|
+
|
|
108
|
+
return processed_actions
|
|
109
|
+
|
|
85
110
|
def process_actions_permissions(self, request, actions):
|
|
86
111
|
result = []
|
|
87
112
|
for action in actions:
|
|
@@ -211,6 +236,7 @@ class SBAdminBaseListView(SBAdminBaseView):
|
|
|
211
236
|
sbadmin_list_reorder_field = None
|
|
212
237
|
search_field_placeholder = _("Search...")
|
|
213
238
|
filters_version = None
|
|
239
|
+
sbadmin_actions_initialized = False
|
|
214
240
|
sbadmin_list_action_class = SBAdminListAction
|
|
215
241
|
|
|
216
242
|
def activate_reorder(self, request):
|
|
@@ -297,11 +323,18 @@ class SBAdminBaseListView(SBAdminBaseView):
|
|
|
297
323
|
messages.add_message(request, messages.ERROR, "Not Implemented")
|
|
298
324
|
return HttpResponse(status=200, content=render_notifications(request))
|
|
299
325
|
|
|
326
|
+
def init_actions(self, request):
|
|
327
|
+
if self.sbadmin_actions_initialized:
|
|
328
|
+
return
|
|
329
|
+
self.process_actions(request, self.get_sbadmin_list_selection_actions())
|
|
330
|
+
self.sbadmin_actions_initialized = True
|
|
331
|
+
|
|
300
332
|
def init_view_dynamic(self, request, request_data=None, **kwargs):
|
|
301
333
|
super().init_view_dynamic(request, request_data, **kwargs)
|
|
302
334
|
self.init_fields_cache(
|
|
303
335
|
self.get_sbamin_list_display(request), request.request_data.configuration
|
|
304
336
|
)
|
|
337
|
+
self.init_actions(request)
|
|
305
338
|
|
|
306
339
|
def get_sbamin_list_display(self, request):
|
|
307
340
|
return self.sbadmin_list_display or self.list_display
|
|
@@ -313,6 +346,11 @@ class SBAdminBaseListView(SBAdminBaseView):
|
|
|
313
346
|
request.request_data.configuration,
|
|
314
347
|
force=True,
|
|
315
348
|
)
|
|
349
|
+
for list_action in self.get_sbadmin_list_selection_actions():
|
|
350
|
+
if isinstance(list_action, SBAdminFormViewAction):
|
|
351
|
+
form = list_action.target_view.form_class
|
|
352
|
+
form.view = self
|
|
353
|
+
form()
|
|
316
354
|
|
|
317
355
|
def get_list_display(self, request):
|
|
318
356
|
return [
|
|
@@ -453,7 +491,7 @@ class SBAdminBaseListView(SBAdminBaseView):
|
|
|
453
491
|
|
|
454
492
|
def get_sbadmin_list_selection_actions_grouped(self, request):
|
|
455
493
|
result = {}
|
|
456
|
-
list_selection_actions = self.
|
|
494
|
+
list_selection_actions = self.process_actions(
|
|
457
495
|
request, self.get_sbadmin_list_selection_actions()
|
|
458
496
|
)
|
|
459
497
|
for action in list_selection_actions:
|
|
@@ -607,6 +645,7 @@ class SBAdminBaseListView(SBAdminBaseView):
|
|
|
607
645
|
if not list_filter:
|
|
608
646
|
return all_config
|
|
609
647
|
list_fields = self.get_sbamin_list_display(request) or []
|
|
648
|
+
self.init_fields_cache(list_fields, request.request_data.configuration)
|
|
610
649
|
base_filter = {
|
|
611
650
|
getattr(field, "filter_field", field): ""
|
|
612
651
|
for field in list_fields
|
|
@@ -632,12 +671,15 @@ class SBAdminBaseListView(SBAdminBaseView):
|
|
|
632
671
|
list_view_config = [self.get_all_config(request), *sbadmin_list_config]
|
|
633
672
|
views = []
|
|
634
673
|
for defined_view in list_view_config:
|
|
674
|
+
url_params = SBAdminViewService.process_url_params(
|
|
675
|
+
view_id=self.get_id(),
|
|
676
|
+
url_params=defined_view["url_params"],
|
|
677
|
+
filter_version=self.get_filters_version(request),
|
|
678
|
+
)
|
|
635
679
|
views.append(
|
|
636
680
|
{
|
|
637
681
|
"name": defined_view["name"],
|
|
638
|
-
"url_params": SBAdminViewService.json_dumps_for_url(
|
|
639
|
-
defined_view["url_params"]
|
|
640
|
-
),
|
|
682
|
+
"url_params": SBAdminViewService.json_dumps_for_url(url_params),
|
|
641
683
|
"default": True,
|
|
642
684
|
}
|
|
643
685
|
)
|
|
@@ -83,6 +83,7 @@ class SBAdminField(JSONSerializableMixin):
|
|
|
83
83
|
python_formatter = None
|
|
84
84
|
tabulator_options = None
|
|
85
85
|
xlsx_options = None
|
|
86
|
+
initialized = False
|
|
86
87
|
|
|
87
88
|
def __init__(
|
|
88
89
|
self,
|
|
@@ -121,6 +122,7 @@ class SBAdminField(JSONSerializableMixin):
|
|
|
121
122
|
if (list_visible is not None)
|
|
122
123
|
else (self.list_visible if self.list_visible is not None else True)
|
|
123
124
|
)
|
|
125
|
+
self.list_visible_arg = list_visible
|
|
124
126
|
self.list_collapsed = list_collapsed or self.list_collapsed or False
|
|
125
127
|
self.auto_created = auto_created or self.auto_created or False
|
|
126
128
|
self.formatter = formatter
|
|
@@ -203,7 +205,9 @@ class SBAdminField(JSONSerializableMixin):
|
|
|
203
205
|
if self.model_field:
|
|
204
206
|
self.editable = self.model_field.editable
|
|
205
207
|
if self.model_field.is_relation:
|
|
206
|
-
self.list_visible =
|
|
208
|
+
self.list_visible = (
|
|
209
|
+
False if self.list_visible_arg is None else self.list_visible_arg
|
|
210
|
+
)
|
|
207
211
|
if self.model_field.auto_created:
|
|
208
212
|
self.detail_visible = False
|
|
209
213
|
self.title = self.title or getattr(
|
|
@@ -225,6 +229,7 @@ class SBAdminField(JSONSerializableMixin):
|
|
|
225
229
|
self.formatter = "html"
|
|
226
230
|
self.filter_field = self.filter_field or self.field
|
|
227
231
|
self.init_filter_for_field(configuration)
|
|
232
|
+
self.initialized = True
|
|
228
233
|
|
|
229
234
|
def serialize_tabulator(self):
|
|
230
235
|
data = {
|
|
@@ -28,11 +28,15 @@ class ActionModalView(FormView):
|
|
|
28
28
|
def get_form_class(self):
|
|
29
29
|
form_class = super().get_form_class()
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
fake_form_class = type(
|
|
32
|
+
form_class.__name__,
|
|
33
|
+
(form_class,),
|
|
34
|
+
{
|
|
35
|
+
"view": self.view,
|
|
36
|
+
},
|
|
37
|
+
)
|
|
34
38
|
|
|
35
|
-
return
|
|
39
|
+
return fake_form_class
|
|
36
40
|
|
|
37
41
|
def post(self, request, *args, **kwargs):
|
|
38
42
|
form = self.get_form()
|
|
@@ -1,26 +1,13 @@
|
|
|
1
|
-
from
|
|
1
|
+
from contextvars import ContextVar
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
sb_admin_request = ContextVar("sb_admin_request")
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class SBAdminThreadLocalService:
|
|
7
|
-
THREAD_DATA_KEY = "SB_ADMIN_DATA"
|
|
8
|
-
|
|
9
|
-
@classmethod
|
|
10
|
-
def get_wrapper(cls):
|
|
11
|
-
return getattr(_thread_locals, cls.THREAD_DATA_KEY, {})
|
|
12
|
-
|
|
13
|
-
@classmethod
|
|
14
|
-
def get_data(cls, key):
|
|
15
|
-
data_wrapper = cls.get_wrapper()
|
|
16
|
-
return data_wrapper.get(key)
|
|
17
|
-
|
|
18
7
|
@classmethod
|
|
19
|
-
def
|
|
20
|
-
|
|
21
|
-
data_wrapper[key] = data
|
|
8
|
+
def get_request(cls):
|
|
9
|
+
return sb_admin_request.get()
|
|
22
10
|
|
|
23
11
|
@classmethod
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
delattr(_thread_locals, cls.THREAD_DATA_KEY)
|
|
12
|
+
def set_request(cls, request):
|
|
13
|
+
sb_admin_request.set(request)
|
|
@@ -4,6 +4,7 @@ import urllib
|
|
|
4
4
|
|
|
5
5
|
from django.db.models import Q, FilteredRelation, F, Value, CharField
|
|
6
6
|
from django.shortcuts import redirect
|
|
7
|
+
from django_smartbase_admin.templatetags.sb_admin_tags import SBAdminJSONEncoder
|
|
7
8
|
|
|
8
9
|
from django_smartbase_admin.engine.const import (
|
|
9
10
|
BASE_PARAMS_NAME,
|
|
@@ -20,17 +21,52 @@ from django_smartbase_admin.services.translations import SBAdminTranslationsServ
|
|
|
20
21
|
class SBAdminViewService(object):
|
|
21
22
|
@classmethod
|
|
22
23
|
def json_dumps_for_url(cls, data):
|
|
23
|
-
return json.dumps(data, separators=(",", ":"))
|
|
24
|
+
return json.dumps(data, separators=(",", ":"), cls=SBAdminJSONEncoder)
|
|
24
25
|
|
|
25
26
|
@classmethod
|
|
26
27
|
def json_dumps_and_replace(cls, data):
|
|
27
|
-
return cls.json_dumps_for_url(data)
|
|
28
|
+
return cls.json_dumps_for_url(data)
|
|
28
29
|
|
|
29
30
|
@classmethod
|
|
30
31
|
def build_list_url(cls, view_id, url_params):
|
|
31
32
|
params = {view_id: url_params}
|
|
32
33
|
return f"{BASE_PARAMS_NAME}={cls.json_dumps_for_url(params)}"
|
|
33
34
|
|
|
35
|
+
@classmethod
|
|
36
|
+
def process_url_params(cls, view_id, url_params, filter_version):
|
|
37
|
+
filter_data = SBAdminViewService.process_filter_data_url(
|
|
38
|
+
view_id=view_id,
|
|
39
|
+
filter_data=url_params.get(FILTER_DATA_NAME, {}),
|
|
40
|
+
filter_version=filter_version,
|
|
41
|
+
)
|
|
42
|
+
url_params_processed = {**url_params}
|
|
43
|
+
if filter_data:
|
|
44
|
+
url_params_processed[FILTER_DATA_NAME] = filter_data
|
|
45
|
+
return url_params_processed
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def process_filter_data_url(cls, view_id, filter_data, filter_version):
|
|
49
|
+
if filter_version == FilterVersions.FILTERS_VERSION_2:
|
|
50
|
+
filter_data_processed = []
|
|
51
|
+
for key, value in filter_data.items():
|
|
52
|
+
filter_value = {
|
|
53
|
+
"id": f"{view_id}-{key}",
|
|
54
|
+
"field": key,
|
|
55
|
+
"type": "string",
|
|
56
|
+
"input": "text",
|
|
57
|
+
"operator": "contains",
|
|
58
|
+
}
|
|
59
|
+
filter_value.update(value)
|
|
60
|
+
filter_data_processed.append(filter_value)
|
|
61
|
+
return filter_data_processed
|
|
62
|
+
else:
|
|
63
|
+
filter_data_processed = {}
|
|
64
|
+
for filter_key, filter_value in filter_data.items():
|
|
65
|
+
filter_data_processed[filter_key] = cls.json_dumps_and_replace(
|
|
66
|
+
filter_value
|
|
67
|
+
)
|
|
68
|
+
return filter_data_processed
|
|
69
|
+
|
|
34
70
|
@classmethod
|
|
35
71
|
def build_list_params_url(cls, view_id, filter_data=None, filter_version=None):
|
|
36
72
|
if filter_version == FilterVersions.FILTERS_VERSION_2:
|
|
@@ -44,24 +80,18 @@ class SBAdminViewService(object):
|
|
|
44
80
|
TABLE_PARAMS_SELECTED_FILTER_TYPE: TABLE_TAB_ADVANCED_FITLERS
|
|
45
81
|
},
|
|
46
82
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"field": key,
|
|
51
|
-
"type": "string",
|
|
52
|
-
"input": "text",
|
|
53
|
-
"operator": "contains",
|
|
54
|
-
}
|
|
55
|
-
filter_value.update(value)
|
|
56
|
-
filter_dict[ADVANCED_FILTER_DATA_NAME]["rules"].append(filter_value)
|
|
83
|
+
filter_dict[ADVANCED_FILTER_DATA_NAME]["rules"].extend(
|
|
84
|
+
cls.process_filter_data_url(view_id, filter_data, filter_version)
|
|
85
|
+
)
|
|
57
86
|
params = {BASE_PARAMS_NAME: cls.json_dumps_for_url({view_id: filter_dict})}
|
|
58
87
|
return urllib.parse.urlencode(params)
|
|
59
88
|
filter_data = filter_data or {}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
89
|
+
view_params = {
|
|
90
|
+
FILTER_DATA_NAME: cls.process_filter_data_url(
|
|
91
|
+
view_id, filter_data, filter_version
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
return cls.build_list_url(view_id, view_params)
|
|
65
95
|
|
|
66
96
|
@classmethod
|
|
67
97
|
def get_pk_field_for_model(cls, model):
|