django-smartbase-admin 1.0.37__py3-none-any.whl → 1.0.40__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 +5 -0
- django_smartbase_admin/admin/widgets.py +52 -5
- django_smartbase_admin/engine/actions.py +3 -0
- django_smartbase_admin/engine/admin_base_view.py +28 -38
- django_smartbase_admin/engine/configuration.py +144 -1
- django_smartbase_admin/engine/field_formatter.py +25 -15
- django_smartbase_admin/engine/filter_widgets.py +0 -5
- django_smartbase_admin/monkeypatch/admin_readonly_field_monkeypatch.py +1 -0
- django_smartbase_admin/services/configuration.py +24 -10
- django_smartbase_admin/services/views.py +7 -4
- django_smartbase_admin/static/sb_admin/dist/main_style.css +1 -1
- django_smartbase_admin/static/sb_admin/js/sbadmin_prepopulated_fields_init.js +25 -0
- django_smartbase_admin/static/sb_admin/src/css/_components.css +17 -12
- django_smartbase_admin/static/sb_admin/src/css/components/_button.css +6 -0
- django_smartbase_admin/static/sb_admin/src/css/components/_dropdown.css +6 -0
- django_smartbase_admin/templates/sb_admin/actions/change_form.html +7 -2
- django_smartbase_admin/templates/sb_admin/actions/list.html +3 -3
- django_smartbase_admin/templates/sb_admin/includes/change_form_title.html +2 -0
- django_smartbase_admin/templates/sb_admin/includes/readonly_field.html +3 -1
- django_smartbase_admin/templatetags/sb_admin_tags.py +30 -0
- {django_smartbase_admin-1.0.37.dist-info → django_smartbase_admin-1.0.40.dist-info}/METADATA +1 -1
- {django_smartbase_admin-1.0.37.dist-info → django_smartbase_admin-1.0.40.dist-info}/RECORD +24 -23
- {django_smartbase_admin-1.0.37.dist-info → django_smartbase_admin-1.0.40.dist-info}/LICENSE.md +0 -0
- {django_smartbase_admin-1.0.37.dist-info → django_smartbase_admin-1.0.40.dist-info}/WHEEL +0 -0
|
@@ -427,6 +427,11 @@ class SBAdminListAction(SBAdminAction):
|
|
|
427
427
|
data: row.get(data, None)
|
|
428
428
|
for data in self.view.sbadmin_list_display_data
|
|
429
429
|
}
|
|
430
|
+
# Include supporting_annotates values in additional_data
|
|
431
|
+
for field in visible_columns:
|
|
432
|
+
if field.supporting_annotates:
|
|
433
|
+
for key in field.supporting_annotates.keys():
|
|
434
|
+
additional_data[key] = row.get(key, None)
|
|
430
435
|
for field_key, value in row.items():
|
|
431
436
|
if field_key in field_key_field_map:
|
|
432
437
|
field = field_key_field_map[field_key]
|
|
@@ -11,7 +11,12 @@ from django.contrib.admin.widgets import (
|
|
|
11
11
|
ForeignKeyRawIdWidget,
|
|
12
12
|
)
|
|
13
13
|
from django.contrib.auth.forms import ReadOnlyPasswordHashWidget
|
|
14
|
-
from django.core.exceptions import
|
|
14
|
+
from django.core.exceptions import (
|
|
15
|
+
FieldDoesNotExist,
|
|
16
|
+
ImproperlyConfigured,
|
|
17
|
+
ValidationError,
|
|
18
|
+
)
|
|
19
|
+
from django.db.models import ForeignKey, OneToOneField
|
|
15
20
|
from django.template.loader import render_to_string
|
|
16
21
|
from django.urls import reverse
|
|
17
22
|
from django.utils.formats import get_format
|
|
@@ -359,13 +364,19 @@ class SBAdminAutocompleteWidget(
|
|
|
359
364
|
form = None
|
|
360
365
|
field_name = None
|
|
361
366
|
initialised = None
|
|
367
|
+
allow_add = None
|
|
368
|
+
create_value_field = None
|
|
362
369
|
default_create_data = None
|
|
370
|
+
forward_to_create = None
|
|
363
371
|
reload_on_save = None
|
|
364
372
|
REQUEST_CREATED_DATA_KEY = "autocomplete_created_data"
|
|
365
373
|
|
|
366
374
|
def __init__(self, form_field=None, *args, **kwargs):
|
|
367
375
|
attrs = kwargs.pop("attrs", None)
|
|
368
376
|
self.reload_on_save = kwargs.pop("reload_on_save", False)
|
|
377
|
+
self.allow_add = kwargs.pop("allow_add", None)
|
|
378
|
+
self.create_value_field = kwargs.pop("create_value_field", None)
|
|
379
|
+
self.forward_to_create = kwargs.pop("forward_to_create", [])
|
|
369
380
|
super().__init__(form_field, *args, **kwargs)
|
|
370
381
|
self.attrs = {} if attrs is None else attrs.copy()
|
|
371
382
|
if self.multiselect and self.allow_add:
|
|
@@ -595,6 +606,34 @@ class SBAdminAutocompleteWidget(
|
|
|
595
606
|
|
|
596
607
|
return forward_data
|
|
597
608
|
|
|
609
|
+
def get_forward_data_to_create(self, request, forward_data):
|
|
610
|
+
forward_data_to_create = {}
|
|
611
|
+
for field_name in self.forward_to_create:
|
|
612
|
+
value = forward_data.get(field_name)
|
|
613
|
+
if value is None:
|
|
614
|
+
continue
|
|
615
|
+
# If forwarding a FK value from the parent form (e.g. for dependent dropdowns),
|
|
616
|
+
# store it under `<field>_id` so `Model(**kwargs)` accepts the raw PK.
|
|
617
|
+
store_key = field_name
|
|
618
|
+
form_model = getattr(getattr(self, "form", None), "model", None)
|
|
619
|
+
if form_model is not None:
|
|
620
|
+
try:
|
|
621
|
+
form_model_field = form_model._meta.get_field(field_name)
|
|
622
|
+
except FieldDoesNotExist:
|
|
623
|
+
form_model_field = None
|
|
624
|
+
if isinstance(form_model_field, (ForeignKey, OneToOneField)):
|
|
625
|
+
store_key = form_model_field.attname
|
|
626
|
+
|
|
627
|
+
forward_data_to_create[store_key] = self.parse_value_from_input(
|
|
628
|
+
request, value
|
|
629
|
+
)
|
|
630
|
+
if not self.is_multiselect():
|
|
631
|
+
forward_data_to_create[store_key] = next(
|
|
632
|
+
iter(forward_data_to_create[store_key]), None
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
return forward_data_to_create
|
|
636
|
+
|
|
598
637
|
def value_from_datadict(self, data, files, name):
|
|
599
638
|
input_value = super().value_from_datadict(data, files, name)
|
|
600
639
|
threadsafe_request = SBAdminThreadLocalService.get_request()
|
|
@@ -630,7 +669,11 @@ class SBAdminAutocompleteWidget(
|
|
|
630
669
|
)
|
|
631
670
|
self.form_field.queryset = qs
|
|
632
671
|
parsed_value = self.validate(
|
|
633
|
-
parsed_value,
|
|
672
|
+
parsed_value,
|
|
673
|
+
qs,
|
|
674
|
+
threadsafe_request,
|
|
675
|
+
forward_data,
|
|
676
|
+
parsed_is_create,
|
|
634
677
|
)
|
|
635
678
|
|
|
636
679
|
return parsed_value
|
|
@@ -638,14 +681,18 @@ class SBAdminAutocompleteWidget(
|
|
|
638
681
|
def should_create_new_obj(self):
|
|
639
682
|
return self.allow_add and self.create_value_field
|
|
640
683
|
|
|
641
|
-
def create_new_obj(self, value, queryset,
|
|
684
|
+
def create_new_obj(self, value, queryset, request, forward_data):
|
|
642
685
|
if isinstance(value, list):
|
|
643
686
|
# TODO: multiselect creation
|
|
644
687
|
return self.form_field.to_python(value)
|
|
645
688
|
else:
|
|
689
|
+
forward_data_to_create = self.get_forward_data_to_create(
|
|
690
|
+
request, forward_data
|
|
691
|
+
)
|
|
646
692
|
data_to_create = {
|
|
647
693
|
self.create_value_field: value,
|
|
648
694
|
**self.default_create_data,
|
|
695
|
+
**forward_data_to_create,
|
|
649
696
|
}
|
|
650
697
|
new_obj = queryset.model.objects.create(**data_to_create)
|
|
651
698
|
try:
|
|
@@ -658,12 +705,12 @@ class SBAdminAutocompleteWidget(
|
|
|
658
705
|
params={"value": value},
|
|
659
706
|
)
|
|
660
707
|
|
|
661
|
-
def validate(self, value, queryset, request, is_create=False):
|
|
708
|
+
def validate(self, value, queryset, request, forward_data, is_create=False):
|
|
662
709
|
is_create_value = (
|
|
663
710
|
True in is_create if isinstance(is_create, list) else is_create
|
|
664
711
|
)
|
|
665
712
|
if is_create_value and self.should_create_new_obj():
|
|
666
|
-
new_object = self.create_new_obj(value, queryset,
|
|
713
|
+
new_object = self.create_new_obj(value, queryset, request, forward_data)
|
|
667
714
|
request.request_data.additional_data[self.REQUEST_CREATED_DATA_KEY] = (
|
|
668
715
|
request.request_data.additional_data.get(
|
|
669
716
|
self.REQUEST_CREATED_DATA_KEY, {}
|
|
@@ -11,6 +11,7 @@ class SBAdminCustomAction(object):
|
|
|
11
11
|
no_params = False
|
|
12
12
|
open_in_modal = False
|
|
13
13
|
open_in_new_tab = False
|
|
14
|
+
template = None
|
|
14
15
|
|
|
15
16
|
def __init__(
|
|
16
17
|
self,
|
|
@@ -26,6 +27,7 @@ class SBAdminCustomAction(object):
|
|
|
26
27
|
sub_actions=None,
|
|
27
28
|
icon=None,
|
|
28
29
|
open_in_new_tab=None,
|
|
30
|
+
template=None,
|
|
29
31
|
) -> None:
|
|
30
32
|
super().__init__()
|
|
31
33
|
self.title = title
|
|
@@ -40,6 +42,7 @@ class SBAdminCustomAction(object):
|
|
|
40
42
|
self.sub_actions = sub_actions
|
|
41
43
|
self.icon = icon
|
|
42
44
|
self.open_in_new_tab = open_in_new_tab
|
|
45
|
+
self.template = template or "sb_admin/actions/partials/action_link.html"
|
|
43
46
|
self.resolve_url()
|
|
44
47
|
|
|
45
48
|
def resolve_url(self):
|
|
@@ -9,7 +9,7 @@ from django.contrib import messages
|
|
|
9
9
|
from django.contrib.admin.actions import delete_selected
|
|
10
10
|
from django.core.exceptions import PermissionDenied
|
|
11
11
|
from django.db.models import F
|
|
12
|
-
from django.http import HttpResponse, Http404, JsonResponse
|
|
12
|
+
from django.http import HttpResponse, Http404, JsonResponse, HttpRequest
|
|
13
13
|
from django.shortcuts import redirect
|
|
14
14
|
from django.template.response import TemplateResponse
|
|
15
15
|
from django.urls import reverse
|
|
@@ -60,11 +60,12 @@ SBADMIN_RELOAD_ON_SAVE_VAR = "sbadmin_reload_on_save"
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
class SBAdminBaseView(object):
|
|
63
|
-
menu_label = None
|
|
64
63
|
global_filter_data_map = None
|
|
65
64
|
field_cache = None
|
|
66
65
|
sbadmin_detail_actions = None
|
|
67
|
-
|
|
66
|
+
menu_label: str | None = None
|
|
67
|
+
add_label: str | None = None
|
|
68
|
+
change_label: str | None = None
|
|
68
69
|
delete_confirmation_template = "sb_admin/actions/delete_confirmation.html"
|
|
69
70
|
|
|
70
71
|
def init_view_static(self, configuration, model, admin_site):
|
|
@@ -222,9 +223,16 @@ class SBAdminBaseView(object):
|
|
|
222
223
|
"color_scheme_form": color_scheme_form,
|
|
223
224
|
}
|
|
224
225
|
|
|
225
|
-
def get_add_label(
|
|
226
|
+
def get_add_label(
|
|
227
|
+
self, request: HttpRequest, object_id: str | None = None
|
|
228
|
+
) -> str | None:
|
|
226
229
|
return self.add_label
|
|
227
230
|
|
|
231
|
+
def get_change_label(
|
|
232
|
+
self, request: HttpRequest, object_id: str | None = None
|
|
233
|
+
) -> str | None:
|
|
234
|
+
return self.change_label
|
|
235
|
+
|
|
228
236
|
def get_global_context(
|
|
229
237
|
self, request, object_id: int | str | None = None
|
|
230
238
|
) -> dict[str, Any]:
|
|
@@ -233,6 +241,7 @@ class SBAdminBaseView(object):
|
|
|
233
241
|
"configuration": request.request_data.configuration,
|
|
234
242
|
"request_data": request.request_data,
|
|
235
243
|
"add_label": self.get_add_label(request, object_id),
|
|
244
|
+
"change_label": self.get_change_label(request, object_id),
|
|
236
245
|
"DETAIL_STRUCTURE_RIGHT_CLASS": DETAIL_STRUCTURE_RIGHT_CLASS,
|
|
237
246
|
"OVERRIDE_CONTENT_OF_NOTIFICATION": OVERRIDE_CONTENT_OF_NOTIFICATION,
|
|
238
247
|
"username_data": self.get_username_data(request),
|
|
@@ -616,8 +625,6 @@ class SBAdminBaseListView(SBAdminBaseView):
|
|
|
616
625
|
return response
|
|
617
626
|
|
|
618
627
|
def action_config(self, request, config_id=None):
|
|
619
|
-
from django_smartbase_admin.models import SBAdminListViewConfiguration
|
|
620
|
-
|
|
621
628
|
config_id = config_id if config_id != "None" else None
|
|
622
629
|
|
|
623
630
|
config_name = request.POST.get(CONFIG_NAME, None)
|
|
@@ -625,32 +632,21 @@ class SBAdminBaseListView(SBAdminBaseView):
|
|
|
625
632
|
config_name = urllib.parse.unquote(config_name)
|
|
626
633
|
updated_configuration = None
|
|
627
634
|
if request.request_data.request_method == "POST":
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
updated_configuration,
|
|
636
|
-
created,
|
|
637
|
-
) = SBAdminListViewConfiguration.objects.update_or_create(
|
|
638
|
-
user_id=request.request_data.user.id,
|
|
639
|
-
**config_params,
|
|
640
|
-
defaults={
|
|
641
|
-
"url_params": request.request_data.request_post.get(
|
|
642
|
-
URL_PARAMS_NAME
|
|
643
|
-
),
|
|
644
|
-
"view": self.get_id(),
|
|
645
|
-
"action": None,
|
|
646
|
-
"modifier": None,
|
|
647
|
-
},
|
|
635
|
+
updated_configuration = (
|
|
636
|
+
SBAdminUserConfigurationService.create_or_update_saved_view(
|
|
637
|
+
request,
|
|
638
|
+
view_id=self.get_id(),
|
|
639
|
+
config_id=config_id,
|
|
640
|
+
config_name=config_name,
|
|
641
|
+
url_params=request.request_data.request_post.get(URL_PARAMS_NAME),
|
|
648
642
|
)
|
|
643
|
+
)
|
|
649
644
|
if request.request_data.request_method == "DELETE":
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
645
|
+
SBAdminUserConfigurationService.delete_saved_view(
|
|
646
|
+
request,
|
|
647
|
+
view_id=self.get_id(),
|
|
648
|
+
config_id=config_id,
|
|
649
|
+
)
|
|
654
650
|
|
|
655
651
|
redirect_to = self.get_redirect_url_from_request(request, updated_configuration)
|
|
656
652
|
|
|
@@ -767,14 +763,8 @@ class SBAdminBaseListView(SBAdminBaseView):
|
|
|
767
763
|
return views
|
|
768
764
|
|
|
769
765
|
def get_config_data(self, request) -> dict[str, list[dict[str, Any]]]:
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
current_views = list(
|
|
773
|
-
SBAdminListViewConfiguration.objects.by_user_id(
|
|
774
|
-
request.request_data.user.id
|
|
775
|
-
)
|
|
776
|
-
.by_view_action_modifier(view=self.get_id())
|
|
777
|
-
.values()
|
|
766
|
+
current_views = SBAdminUserConfigurationService.get_saved_views(
|
|
767
|
+
request, view_id=self.get_id()
|
|
778
768
|
)
|
|
779
769
|
for view in current_views:
|
|
780
770
|
view["detail_url"] = self.get_config_url(request, view["id"])
|
|
@@ -9,7 +9,11 @@ from django_smartbase_admin.engine.const import (
|
|
|
9
9
|
FilterVersions,
|
|
10
10
|
Action,
|
|
11
11
|
)
|
|
12
|
-
from django_smartbase_admin.models import
|
|
12
|
+
from django_smartbase_admin.models import (
|
|
13
|
+
ColorScheme,
|
|
14
|
+
SBAdminListViewConfiguration,
|
|
15
|
+
SBAdminUserConfiguration,
|
|
16
|
+
)
|
|
13
17
|
from django_smartbase_admin.utils import to_list, is_modal
|
|
14
18
|
|
|
15
19
|
|
|
@@ -23,6 +27,145 @@ class SBAdminConfigurationBase(object):
|
|
|
23
27
|
def get_configuration_for_roles(self, user_roles):
|
|
24
28
|
raise NotImplementedError
|
|
25
29
|
|
|
30
|
+
# User configuration hooks - override these methods to customize user identification
|
|
31
|
+
# (e.g., use email instead of user_id for OAuth/external auth scenarios)
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def get_user_config(cls, request):
|
|
35
|
+
"""
|
|
36
|
+
Get or create user configuration (e.g., color scheme preferences).
|
|
37
|
+
|
|
38
|
+
Override this method to customize user identification. Default uses user_id.
|
|
39
|
+
|
|
40
|
+
Example for email-based users::
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def get_user_config(cls, request):
|
|
44
|
+
from myapp.models import MyUserConfig
|
|
45
|
+
email = getattr(request.user, "email", None)
|
|
46
|
+
if not email:
|
|
47
|
+
return MyUserConfig(email="anonymous", color_scheme=ColorScheme.AUTO.value)
|
|
48
|
+
config, _ = MyUserConfig.objects.get_or_create(
|
|
49
|
+
email=email,
|
|
50
|
+
defaults={"color_scheme": request.request_data.configuration.default_color_scheme},
|
|
51
|
+
)
|
|
52
|
+
return config
|
|
53
|
+
"""
|
|
54
|
+
if not request.user or request.user.is_anonymous:
|
|
55
|
+
return None
|
|
56
|
+
user_config, _ = SBAdminUserConfiguration.objects.get_or_create(
|
|
57
|
+
defaults={
|
|
58
|
+
"color_scheme": request.request_data.configuration.default_color_scheme
|
|
59
|
+
},
|
|
60
|
+
user_id=request.user.id,
|
|
61
|
+
)
|
|
62
|
+
return user_config
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def get_saved_views(cls, request, view_id):
|
|
66
|
+
"""
|
|
67
|
+
Get saved views for the current user and view.
|
|
68
|
+
|
|
69
|
+
Override this method to customize user identification. Default uses user_id.
|
|
70
|
+
Returns a list of dicts with keys: id, name, url_params, view (view_id).
|
|
71
|
+
|
|
72
|
+
Example for email-based users::
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def get_saved_views(cls, request, view_id):
|
|
76
|
+
from myapp.models import MySavedView
|
|
77
|
+
email = getattr(request.user, "email", None)
|
|
78
|
+
if not email:
|
|
79
|
+
return []
|
|
80
|
+
return list(
|
|
81
|
+
MySavedView.objects.filter(email=email, view_id=view_id)
|
|
82
|
+
.values("id", "name", "config", "view_id")
|
|
83
|
+
)
|
|
84
|
+
"""
|
|
85
|
+
if not request.user or request.user.is_anonymous:
|
|
86
|
+
return []
|
|
87
|
+
return list(
|
|
88
|
+
SBAdminListViewConfiguration.objects.by_user_id(request.user.id)
|
|
89
|
+
.by_view_action_modifier(view=view_id)
|
|
90
|
+
.values()
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@classmethod
|
|
94
|
+
def create_or_update_saved_view(
|
|
95
|
+
cls, request, view_id, config_id, config_name, url_params
|
|
96
|
+
):
|
|
97
|
+
"""
|
|
98
|
+
Create or update a saved view for the current user.
|
|
99
|
+
|
|
100
|
+
Override this method to customize user identification. Default uses user_id.
|
|
101
|
+
Returns the created/updated saved view object.
|
|
102
|
+
|
|
103
|
+
Example for email-based users::
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def create_or_update_saved_view(cls, request, view_id, config_id, config_name, url_params):
|
|
107
|
+
from myapp.models import MySavedView
|
|
108
|
+
email = getattr(request.user, "email", None)
|
|
109
|
+
if not email:
|
|
110
|
+
return None
|
|
111
|
+
config_params = {}
|
|
112
|
+
if config_id:
|
|
113
|
+
config_params["id"] = config_id
|
|
114
|
+
if config_name:
|
|
115
|
+
config_params["name"] = config_name
|
|
116
|
+
saved_view, _ = MySavedView.objects.update_or_create(
|
|
117
|
+
email=email,
|
|
118
|
+
**config_params,
|
|
119
|
+
defaults={"config": {"url_params": url_params}, "view_id": view_id},
|
|
120
|
+
)
|
|
121
|
+
return saved_view
|
|
122
|
+
"""
|
|
123
|
+
if not request.user or request.user.is_anonymous:
|
|
124
|
+
return None
|
|
125
|
+
config_params = {}
|
|
126
|
+
if config_id:
|
|
127
|
+
config_params["id"] = config_id
|
|
128
|
+
elif config_name:
|
|
129
|
+
config_params["name"] = config_name
|
|
130
|
+
if not config_params:
|
|
131
|
+
return None
|
|
132
|
+
saved_view, _ = SBAdminListViewConfiguration.objects.update_or_create(
|
|
133
|
+
user_id=request.user.id,
|
|
134
|
+
**config_params,
|
|
135
|
+
defaults={
|
|
136
|
+
"url_params": url_params,
|
|
137
|
+
"view": view_id,
|
|
138
|
+
"action": None,
|
|
139
|
+
"modifier": None,
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
return saved_view
|
|
143
|
+
|
|
144
|
+
@classmethod
|
|
145
|
+
def delete_saved_view(cls, request, view_id, config_id):
|
|
146
|
+
"""
|
|
147
|
+
Delete a saved view for the current user.
|
|
148
|
+
|
|
149
|
+
Override this method to customize user identification. Default uses user_id.
|
|
150
|
+
|
|
151
|
+
Example for email-based users::
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
def delete_saved_view(cls, request, view_id, config_id):
|
|
155
|
+
from myapp.models import MySavedView
|
|
156
|
+
email = getattr(request.user, "email", None)
|
|
157
|
+
if not email or not config_id:
|
|
158
|
+
return
|
|
159
|
+
MySavedView.objects.filter(
|
|
160
|
+
email=email, id=config_id, view_id=view_id
|
|
161
|
+
).delete()
|
|
162
|
+
"""
|
|
163
|
+
if not request.user or request.user.is_anonymous or not config_id:
|
|
164
|
+
return
|
|
165
|
+
SBAdminListViewConfiguration.objects.by_user_id(request.user.id).by_id(
|
|
166
|
+
config_id
|
|
167
|
+
).by_view_action_modifier(view=view_id).delete()
|
|
168
|
+
|
|
26
169
|
|
|
27
170
|
class Singleton(type):
|
|
28
171
|
_instances = {}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
3
|
from django.template.defaultfilters import date, time
|
|
4
|
+
from django.utils import timezone
|
|
5
|
+
from django.utils.html import format_html, format_html_join
|
|
4
6
|
from django.utils.safestring import mark_safe
|
|
5
7
|
from django.utils.translation import gettext_lazy as _
|
|
6
|
-
from django.utils import timezone
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class BadgeType(Enum):
|
|
@@ -39,21 +40,26 @@ def datetime_formatter_with_format(date_format=None, time_format=None):
|
|
|
39
40
|
|
|
40
41
|
def boolean_formatter(object_id, value):
|
|
41
42
|
if value:
|
|
42
|
-
return
|
|
43
|
-
|
|
43
|
+
return format_html(
|
|
44
|
+
'<span class="badge badge-simple badge-positive">{}</span>', _("Yes")
|
|
44
45
|
)
|
|
45
|
-
return
|
|
46
|
+
return format_html(
|
|
47
|
+
'<span class="badge badge-simple badge-neutral">{}</span>', _("No")
|
|
48
|
+
)
|
|
46
49
|
|
|
47
50
|
|
|
48
51
|
def format_array(value_list, separator="", badge_type: BadgeType = BadgeType.NOTICE):
|
|
49
|
-
result = ""
|
|
50
52
|
if not value_list:
|
|
51
|
-
return
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return
|
|
53
|
+
return ""
|
|
54
|
+
|
|
55
|
+
# `separator` is intended to be an internal constant (e.g. "" or "<br>").
|
|
56
|
+
# We mark it safe so HTML separators render as HTML rather than being escaped.
|
|
57
|
+
sep = mark_safe(separator) if separator else ""
|
|
58
|
+
return format_html_join(
|
|
59
|
+
sep,
|
|
60
|
+
'<span class="badge badge-simple badge-{} mr-4">{}</span>',
|
|
61
|
+
((badge_type.value, value) for value in value_list if value),
|
|
62
|
+
)
|
|
57
63
|
|
|
58
64
|
|
|
59
65
|
def array_badge_formatter(object_id, value_list):
|
|
@@ -61,14 +67,18 @@ def array_badge_formatter(object_id, value_list):
|
|
|
61
67
|
|
|
62
68
|
|
|
63
69
|
def newline_separated_array_badge_formatter(object_id, value_list):
|
|
64
|
-
return format_array(value_list, separator="<br>")
|
|
70
|
+
return format_html("<div>{}</div>", format_array(value_list, separator="<br>"))
|
|
65
71
|
|
|
66
72
|
|
|
67
73
|
def rich_text_formatter(object_id, value):
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
# Intentionally renders HTML (e.g. from a rich text editor field).
|
|
75
|
+
return format_html(
|
|
76
|
+
'<div style="max-width: 500px; white-space: normal;">{}</div>',
|
|
77
|
+
mark_safe(value) if value else "",
|
|
70
78
|
)
|
|
71
79
|
|
|
72
80
|
|
|
73
81
|
def link_formatter(object_id, value):
|
|
74
|
-
|
|
82
|
+
if not value:
|
|
83
|
+
return ""
|
|
84
|
+
return format_html('<a href="{0}">{0}</a>', value)
|
|
@@ -492,7 +492,6 @@ class AutocompleteFilterWidget(
|
|
|
492
492
|
forward = None
|
|
493
493
|
label_lambda = None
|
|
494
494
|
value_lambda = None
|
|
495
|
-
allow_add = False
|
|
496
495
|
hide_clear_button = False
|
|
497
496
|
search_query_lambda = None
|
|
498
497
|
create_value_field = None
|
|
@@ -515,10 +514,8 @@ class AutocompleteFilterWidget(
|
|
|
515
514
|
value_lambda=None,
|
|
516
515
|
multiselect=None,
|
|
517
516
|
forward=None,
|
|
518
|
-
allow_add=None,
|
|
519
517
|
hide_clear_button=None,
|
|
520
518
|
search_query_lambda=None,
|
|
521
|
-
create_value_field=None,
|
|
522
519
|
**kwargs,
|
|
523
520
|
) -> None:
|
|
524
521
|
super().__init__(template_name, default_value, **kwargs)
|
|
@@ -534,8 +531,6 @@ class AutocompleteFilterWidget(
|
|
|
534
531
|
self.multiselect = multiselect if multiselect is not None else self.multiselect
|
|
535
532
|
self.multiselect = self.multiselect if self.multiselect is not None else True
|
|
536
533
|
self.forward = forward or self.forward
|
|
537
|
-
self.allow_add = allow_add or self.allow_add
|
|
538
|
-
self.create_value_field = create_value_field or self.create_value_field
|
|
539
534
|
self.hide_clear_button = (
|
|
540
535
|
hide_clear_button
|
|
541
536
|
if hide_clear_button is not None
|
|
@@ -25,14 +25,28 @@ class SBAdminConfigurationService(object):
|
|
|
25
25
|
class SBAdminUserConfigurationService(object):
|
|
26
26
|
@classmethod
|
|
27
27
|
def get_user_config(cls, request):
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
"""Delegate to the configuration class's get_user_config method."""
|
|
29
|
+
configuration_class = import_string(settings.SB_ADMIN_CONFIGURATION)
|
|
30
|
+
return configuration_class.get_user_config(request)
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def get_saved_views(cls, request, view_id):
|
|
34
|
+
"""Delegate to the configuration class's get_saved_views method."""
|
|
35
|
+
configuration_class = import_string(settings.SB_ADMIN_CONFIGURATION)
|
|
36
|
+
return configuration_class.get_saved_views(request, view_id)
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def create_or_update_saved_view(
|
|
40
|
+
cls, request, view_id, config_id, config_name, url_params
|
|
41
|
+
):
|
|
42
|
+
"""Delegate to the configuration class's create_or_update_saved_view method."""
|
|
43
|
+
configuration_class = import_string(settings.SB_ADMIN_CONFIGURATION)
|
|
44
|
+
return configuration_class.create_or_update_saved_view(
|
|
45
|
+
request, view_id, config_id, config_name, url_params
|
|
37
46
|
)
|
|
38
|
-
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def delete_saved_view(cls, request, view_id, config_id):
|
|
50
|
+
"""Delegate to the configuration class's delete_saved_view method."""
|
|
51
|
+
configuration_class = import_string(settings.SB_ADMIN_CONFIGURATION)
|
|
52
|
+
return configuration_class.delete_saved_view(request, view_id, config_id)
|
|
@@ -64,10 +64,13 @@ class SBAdminViewService(object):
|
|
|
64
64
|
else:
|
|
65
65
|
filter_data_processed = {}
|
|
66
66
|
for filter_key, filter_value in filter_data.items():
|
|
67
|
-
|
|
68
|
-
filter_value
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
if isinstance(filter_value, str):
|
|
68
|
+
filter_data_processed[filter_key] = filter_value
|
|
69
|
+
else:
|
|
70
|
+
filter_data_processed[filter_key] = cls.json_dumps_and_replace(
|
|
71
|
+
filter_value
|
|
72
|
+
)
|
|
73
|
+
return filter_data_processed
|
|
71
74
|
|
|
72
75
|
@classmethod
|
|
73
76
|
def build_list_params_url(cls, view_id, filter_data=None, filter_version=None):
|