django-smartbase-admin 1.0.5__py3-none-any.whl → 1.0.6b2__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.
Files changed (25) hide show
  1. django_smartbase_admin/admin/admin_base.py +142 -35
  2. django_smartbase_admin/admin/widgets.py +63 -1
  3. django_smartbase_admin/engine/admin_base_view.py +30 -21
  4. django_smartbase_admin/engine/configuration.py +30 -21
  5. django_smartbase_admin/static/sb_admin/dist/main.js +1 -1
  6. django_smartbase_admin/static/sb_admin/dist/main_style.css +1 -1
  7. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Caution.svg +3 -0
  8. django_smartbase_admin/static/sb_admin/src/css/components/_input.css +19 -15
  9. django_smartbase_admin/static/sb_admin/src/js/autocomplete.js +18 -0
  10. django_smartbase_admin/static/sb_admin/src/js/choices.js +8 -0
  11. django_smartbase_admin/static/sb_admin/src/js/datepicker.js +7 -5
  12. django_smartbase_admin/static/sb_admin/src/js/main.js +64 -26
  13. django_smartbase_admin/static/sb_admin/src/js/range.js +3 -2
  14. django_smartbase_admin/templates/sb_admin/actions/change_form.html +75 -36
  15. django_smartbase_admin/templates/sb_admin/inlines/table_inline.html +49 -33
  16. django_smartbase_admin/templates/sb_admin/sprites/sb_admin.svg +1 -1
  17. django_smartbase_admin/templates/sb_admin/widgets/autocomplete.html +13 -2
  18. django_smartbase_admin/templates/sb_admin/widgets/date.html +1 -1
  19. django_smartbase_admin/templates/sb_admin/widgets/includes/related_item_buttons.html +31 -0
  20. django_smartbase_admin/templates/sb_admin/widgets/time.html +1 -1
  21. django_smartbase_admin/utils.py +5 -0
  22. {django_smartbase_admin-1.0.5.dist-info → django_smartbase_admin-1.0.6b2.dist-info}/METADATA +2 -2
  23. {django_smartbase_admin-1.0.5.dist-info → django_smartbase_admin-1.0.6b2.dist-info}/RECORD +25 -23
  24. {django_smartbase_admin-1.0.5.dist-info → django_smartbase_admin-1.0.6b2.dist-info}/LICENSE.md +0 -0
  25. {django_smartbase_admin-1.0.5.dist-info → django_smartbase_admin-1.0.6b2.dist-info}/WHEEL +0 -0
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import logging
2
3
  import urllib.parse
3
4
  from collections.abc import Iterable
4
5
  from functools import partial
@@ -12,6 +13,8 @@ from django.contrib.admin.options import get_content_type_for_model
12
13
  from django.contrib.admin.utils import unquote
13
14
  from django.contrib.admin.widgets import AdminTextareaWidget
14
15
  from django.contrib.auth.forms import UsernameField, ReadOnlyPasswordHashWidget
16
+ from django.contrib.contenttypes.forms import BaseGenericInlineFormSet
17
+ from django.contrib.contenttypes.models import ContentType
15
18
  from django.core.exceptions import (
16
19
  FieldDoesNotExist,
17
20
  ImproperlyConfigured,
@@ -24,15 +27,18 @@ from django.forms.models import (
24
27
  ModelFormMetaclass,
25
28
  modelform_factory,
26
29
  )
30
+ from django.http import HttpResponse
27
31
  from django.template.loader import render_to_string
28
32
  from django.template.response import TemplateResponse
29
- from django.urls import reverse
33
+ from django.urls import reverse, NoReverseMatch
30
34
  from django.utils.safestring import mark_safe, SafeString
31
35
  from django.utils.text import capfirst
32
36
  from django.utils.translation import gettext_lazy as _
33
37
  from django_admin_inline_paginator.admin import TabularInlinePaginated
38
+ from django_htmx.http import trigger_client_event
34
39
  from filer.fields.file import FilerFileField
35
40
  from filer.fields.image import AdminImageFormField, FilerImageField
41
+ from nested_admin.formsets import NestedInlineFormSet
36
42
  from nested_admin.nested import (
37
43
  NestedModelAdmin,
38
44
  NestedTabularInline,
@@ -43,7 +49,7 @@ from nested_admin.nested import (
43
49
 
44
50
  from django_smartbase_admin.engine.actions import SBAdminCustomAction
45
51
  from django_smartbase_admin.services.thread_local import SBAdminThreadLocalService
46
- from django_smartbase_admin.utils import FormFieldsetMixin
52
+ from django_smartbase_admin.utils import FormFieldsetMixin, is_modal
47
53
 
48
54
  parler_enabled = None
49
55
  try:
@@ -68,7 +74,6 @@ try:
68
74
  except ImportError:
69
75
  pass
70
76
 
71
-
72
77
  django_cms_attributes = None
73
78
  try:
74
79
  from djangocms_attributes_field.fields import AttributesFormField
@@ -77,7 +82,6 @@ try:
77
82
  except ImportError:
78
83
  pass
79
84
 
80
-
81
85
  color_field_enabled = None
82
86
  try:
83
87
  from colorfield.fields import ColorField
@@ -86,7 +90,6 @@ try:
86
90
  except ImportError:
87
91
  pass
88
92
 
89
-
90
93
  from django_smartbase_admin.admin.widgets import (
91
94
  SBAdminTextInputWidget,
92
95
  SBAdminTextareaWidget,
@@ -117,6 +120,10 @@ from django_smartbase_admin.engine.admin_base_view import (
117
120
  SBAdminBaseListView,
118
121
  SBAdminBaseView,
119
122
  SBAdminBaseQuerysetMixin,
123
+ SBADMIN_IS_MODAL_VAR,
124
+ SBADMIN_PARENT_INSTANCE_PK_VAR,
125
+ SBADMIN_PARENT_INSTANCE_LABEL_VAR,
126
+ SBADMIN_PARENT_INSTANCE_FIELD_NAME_VAR,
120
127
  )
121
128
  from django_smartbase_admin.engine.const import (
122
129
  OBJECT_ID_PLACEHOLDER,
@@ -126,6 +133,8 @@ from django_smartbase_admin.engine.const import (
126
133
  from django_smartbase_admin.services.translations import SBAdminTranslationsService
127
134
  from django_smartbase_admin.services.views import SBAdminViewService
128
135
 
136
+ logger = logging.getLogger(__name__)
137
+
129
138
 
130
139
  class SBAdminFormFieldWidgetsMixin:
131
140
  formfield_widgets = {
@@ -186,7 +195,7 @@ class SBAdminFormFieldWidgetsMixin:
186
195
  )
187
196
 
188
197
  def get_autocomplete_widget(
189
- self, request, form_field, db_field, model, multiselect=False
198
+ self, request, form_field, db_field, model, multiselect=False
190
199
  ):
191
200
  return request.request_data.configuration.get_autocomplete_widget(
192
201
  self, request, form_field, db_field, model, multiselect
@@ -277,7 +286,7 @@ class SBAdminFormFieldWidgetsMixin:
277
286
  )
278
287
  form_field.widget = form_field_widget_instance
279
288
  if form_field.help_text == _(
280
- "Hold down “Control”, or “Command” on a Mac, to select more than one."
289
+ "Hold down “Control”, or “Command” on a Mac, to select more than one."
281
290
  ):
282
291
  form_field.help_text = ""
283
292
  return form_field
@@ -358,25 +367,25 @@ if parler_enabled:
358
367
  for translations_model in form_model._parler_meta.get_all_models():
359
368
  fields = getattr(form_new_meta, "fields", form_meta.fields)
360
369
  exclude = (
361
- getattr(form_new_meta, "exclude", form_meta.exclude) or ()
370
+ getattr(form_new_meta, "exclude", form_meta.exclude) or ()
362
371
  )
363
372
  widgets = (
364
- getattr(form_new_meta, "widgets", form_meta.widgets) or ()
373
+ getattr(form_new_meta, "widgets", form_meta.widgets) or ()
365
374
  )
366
375
  labels = (
367
- getattr(form_new_meta, "labels", form_meta.labels) or ()
376
+ getattr(form_new_meta, "labels", form_meta.labels) or ()
368
377
  )
369
378
  help_texts = (
370
- getattr(form_new_meta, "help_texts", form_meta.help_texts)
371
- or ()
379
+ getattr(form_new_meta, "help_texts", form_meta.help_texts)
380
+ or ()
372
381
  )
373
382
  error_messages = (
374
- getattr(
375
- form_new_meta,
376
- "error_messages",
377
- form_meta.error_messages,
378
- )
379
- or ()
383
+ getattr(
384
+ form_new_meta,
385
+ "error_messages",
386
+ form_meta.error_messages,
387
+ )
388
+ or ()
380
389
  )
381
390
  formfield_callback = attrs.get("formfield_callback", None)
382
391
 
@@ -397,10 +406,10 @@ if parler_enabled:
397
406
  # The next code holds the same logic as fields_for_model()
398
407
  # The f.editable check happens in _get_model_form_field()
399
408
  elif (
400
- f_name not in form_base_fields
401
- and (fields is None or f_name in fields)
402
- and f_name not in exclude
403
- and not f_name in attrs
409
+ f_name not in form_base_fields
410
+ and (fields is None or f_name in fields)
411
+ and f_name not in exclude
412
+ and not f_name in attrs
404
413
  ):
405
414
  # Get declared widget kwargs
406
415
  if f_name in widgets:
@@ -421,7 +430,7 @@ if parler_enabled:
421
430
  # See if this formfield was previously defined using a TranslatedField placeholder.
422
431
  placeholder = _get_mro_attribute(bases, f_name)
423
432
  if placeholder and isinstance(
424
- placeholder, TranslatedField
433
+ placeholder, TranslatedField
425
434
  ):
426
435
  kwargs.update(placeholder.kwargs)
427
436
 
@@ -454,6 +463,7 @@ if parler_enabled:
454
463
  mcs.parler_orig__new__(mcs, name, bases, attrs)
455
464
  return super().__new__(mcs, name, bases, attrs)
456
465
 
466
+
457
467
  class SBTranslatableModelForm(
458
468
  BaseTranslatableModelForm,
459
469
  SBAdminBaseForm,
@@ -504,7 +514,7 @@ class SBAdminInlineAndAdminCommon(SBAdminFormFieldWidgetsMixin):
504
514
  def init_view_dynamic(self, request, request_data=None, **kwargs) -> None:
505
515
  if SBAdminTranslationsService.is_translated_model(self.model):
506
516
  has_default_form = (
507
- self.form == TranslatableModelForm or self.form == forms.ModelForm
517
+ self.form == TranslatableModelForm or self.form == forms.ModelForm
508
518
  )
509
519
  if not self.form or has_default_form:
510
520
  self.form = SBTranslatableModelForm
@@ -558,12 +568,12 @@ class SBAdminThirdParty(SBAdminInlineAndAdminCommon, SBAdminBaseView):
558
568
 
559
569
  class SBAdminTranslationStatusMixin:
560
570
  def sbadmin_translation_status_row_context(
561
- self,
562
- language,
563
- languages_count,
564
- main_language_code,
565
- current_lang_code,
566
- translations_edit_url,
571
+ self,
572
+ language,
573
+ languages_count,
574
+ main_language_code,
575
+ current_lang_code,
576
+ translations_edit_url,
567
577
  ) -> dict[str, Any]:
568
578
  language_code = language[0]
569
579
  language_title = language[1]
@@ -648,6 +658,27 @@ class SBAdminTranslationStatusMixin:
648
658
  return mark_safe(result)
649
659
 
650
660
 
661
+ class SBAdminInlineFormSetMixin:
662
+ @classmethod
663
+ def get_default_prefix(cls):
664
+ view = getattr(cls.form, "view", None)
665
+ if view and view.parent_model and view.opts:
666
+ parent_opts = view.parent_model._meta
667
+ opts = view.opts
668
+ modal_prefix = "modal__" if is_modal(SBAdminThreadLocalService.get_request()) else ""
669
+ return f"{modal_prefix}{parent_opts.app_label}_{parent_opts.model_name}_{opts.app_label}-{opts.model_name}"
670
+
671
+ return super().get_default_prefix()
672
+
673
+
674
+ class SBAdminGenericInlineFormSet(SBAdminInlineFormSetMixin, BaseGenericInlineFormSet):
675
+ pass
676
+
677
+
678
+ class SBAdminNestedInlineFormSet(SBAdminInlineFormSetMixin, NestedInlineFormSet):
679
+ pass
680
+
681
+
651
682
  class SBAdmin(
652
683
  SBAdminInlineAndAdminCommon,
653
684
  SBAdminBaseQuerysetMixin,
@@ -668,6 +699,10 @@ class SBAdmin(
668
699
  sbadmin_tabs = None
669
700
  request_data = None
670
701
  menu_label = None
702
+ sbadmin_is_generic_model = False
703
+
704
+ def __init__(self, model, admin_site):
705
+ super().__init__(model, admin_site)
671
706
 
672
707
  def save_formset(self, request, form, formset, change):
673
708
  if not change and hasattr(formset, "inline_instance"):
@@ -688,7 +723,7 @@ class SBAdmin(
688
723
  return self.get_model_path()
689
724
 
690
725
  def get_sbadmin_fieldsets(
691
- self, request, object_id=None
726
+ self, request, object_id=None
692
727
  ) -> Iterable[tuple[str | None, dict[str, Any]]]:
693
728
  fieldsets = self.sbadmin_fieldsets or self.fieldsets
694
729
  if fieldsets:
@@ -702,7 +737,7 @@ class SBAdmin(
702
737
  self.get_form(request)()
703
738
 
704
739
  def get_fieldsets(
705
- self, request, obj=None
740
+ self, request, obj=None
706
741
  ) -> list[tuple[str | None, dict[str, Any]]]:
707
742
  fieldsets = []
708
743
  object_id = obj.id if obj else None
@@ -719,7 +754,7 @@ class SBAdmin(
719
754
  return fieldsets
720
755
 
721
756
  def get_fieldsets_context(
722
- self, request, object_id
757
+ self, request, object_id
723
758
  ) -> dict[str, dict[str | None, dict[str, Any]]]:
724
759
  fielsets_context = {}
725
760
  for fieldset in self.get_sbadmin_fieldsets(request, object_id):
@@ -903,6 +938,50 @@ class SBAdmin(
903
938
  except Exception as e:
904
939
  return super().history_view(request, object_id, extra_context)
905
940
 
941
+ @classmethod
942
+ def get_modal_save_response(cls, request, obj):
943
+ response = HttpResponse()
944
+ trigger_client_event(
945
+ response,
946
+ "sbadmin:modal-change-form-response",
947
+ {
948
+ "field": request.POST.get("sb_admin_source_field"),
949
+ "id": obj.pk,
950
+ "label": str(obj),
951
+ "reload": request.POST.get("sbadmin_reload_on_save") == "1",
952
+ },
953
+ )
954
+ trigger_client_event(response, "hideModal", {"elt": "sb-admin-modal"})
955
+ return response
956
+
957
+ def response_add(self, request, obj, post_url_continue=None):
958
+ if is_modal(request):
959
+ return self.get_modal_save_response(request, obj)
960
+ return super().response_add(request, obj, post_url_continue)
961
+
962
+ def response_change(self, request, obj):
963
+ if is_modal(request):
964
+ return self.get_modal_save_response(request, obj)
965
+ return super().response_change(request, obj)
966
+
967
+ @classmethod
968
+ def set_generic_relation_from_parent(cls, request, obj):
969
+ parent_model_path = request.POST.get(SBADMIN_PARENT_INSTANCE_FIELD_NAME_VAR)
970
+ parent_pk = request.POST.get(SBADMIN_PARENT_INSTANCE_PK_VAR)
971
+
972
+ if parent_model_path and parent_pk:
973
+ app_label, model_name, field, parent_model = parent_model_path.split("_", 4)
974
+ content_type = ContentType.objects.get(
975
+ app_label=app_label, model=parent_model
976
+ )
977
+ obj.content_type = content_type
978
+ obj.object_id = int(parent_pk)
979
+
980
+ def save_model(self, request, obj, form, change):
981
+ if self.sbadmin_is_generic_model and SBADMIN_IS_MODAL_VAR in request.POST:
982
+ self.set_generic_relation_from_parent(request, obj)
983
+ super().save_model(request, obj, form, change)
984
+
906
985
 
907
986
  class SBAdminInline(
908
987
  SBAdminInlineAndAdminCommon, SBAdminBaseQuerysetMixin, SBAdminBaseView
@@ -914,6 +993,7 @@ class SBAdminInline(
914
993
  extra = 0
915
994
  ordering = None
916
995
  all_base_fields_form = None
996
+ sb_admin_add_modal = False
917
997
 
918
998
  def get_readonly_fields(self, request, obj=None):
919
999
  readonly_fields = super().get_readonly_fields(request, obj)
@@ -961,12 +1041,37 @@ class SBAdminInline(
961
1041
 
962
1042
  def get_context_data(self, request) -> dict[str, Any]:
963
1043
  is_sortable_active: bool = self.sortable_field_name and (
964
- self.has_add_permission(request) or self.has_change_permission(request)
1044
+ self.has_add_permission(request) or self.has_change_permission(request)
965
1045
  )
966
- return {
1046
+ add_url = None
1047
+ try:
1048
+ if self.sb_admin_add_modal and self.has_add_permission(request):
1049
+ add_url = reverse(
1050
+ "sb_admin:{}_{}_add".format(
1051
+ self.opts.app_label, self.opts.model_name
1052
+ )
1053
+ )
1054
+ except NoReverseMatch:
1055
+ logger.warning(
1056
+ "To use Add in modal, You have to specify SBAdmin view for %s model",
1057
+ self.opts.model_name,
1058
+ )
1059
+ context_data = {
967
1060
  "inline_list_actions": self.get_sbadmin_inline_list_actions(request),
968
1061
  "is_sortable_active": is_sortable_active,
1062
+ "add_url": add_url,
969
1063
  }
1064
+ if self.parent_instance:
1065
+ context_data["parent_data"] = {
1066
+ SBADMIN_PARENT_INSTANCE_PK_VAR: self.parent_instance.pk,
1067
+ SBADMIN_PARENT_INSTANCE_LABEL_VAR: str(self.parent_instance),
1068
+ SBADMIN_PARENT_INSTANCE_FIELD_NAME_VAR: "{}_{}_id_{}".format(
1069
+ self.model._meta.app_label,
1070
+ self.model._meta.model_name,
1071
+ self.parent_model._meta.model_name,
1072
+ ),
1073
+ }
1074
+ return context_data
970
1075
 
971
1076
  def init_sortable_field(self) -> None:
972
1077
  if not self.sortable_field_name:
@@ -1012,10 +1117,12 @@ class SBAdminInline(
1012
1117
 
1013
1118
  class SBAdminTableInline(SBAdminInline, NestedTabularInline):
1014
1119
  template = "sb_admin/inlines/table_inline.html"
1120
+ formset = SBAdminNestedInlineFormSet
1015
1121
 
1016
1122
 
1017
1123
  class SBAdminGenericTableInline(SBAdminInline, NestedGenericTabularInline):
1018
1124
  template = "sb_admin/inlines/table_inline.html"
1125
+ formset = SBAdminGenericInlineFormSet
1019
1126
 
1020
1127
 
1021
1128
  class SBAdminTableInlinePaginated(SBAdminTableInline, TabularInlinePaginated):
@@ -12,7 +12,7 @@ from django.contrib.admin.widgets import (
12
12
  from django.contrib.auth.forms import ReadOnlyPasswordHashWidget
13
13
  from django.core.exceptions import ValidationError
14
14
  from django.template.loader import render_to_string
15
- from django.urls import reverse
15
+ from django.urls import reverse, NoReverseMatch
16
16
  from django.utils.formats import get_format
17
17
  from django.utils.http import urlencode
18
18
  from django.utils.safestring import mark_safe
@@ -22,12 +22,17 @@ from filer.fields.file import AdminFileWidget as FilerAdminFileWidget
22
22
  from filer.fields.image import AdminImageWidget
23
23
  from filer.models import File
24
24
 
25
+ from django_smartbase_admin.engine.admin_base_view import (
26
+ SBADMIN_PARENT_INSTANCE_PK_VAR,
27
+ SBADMIN_PARENT_INSTANCE_LABEL_VAR,
28
+ )
25
29
  from django_smartbase_admin.engine.filter_widgets import (
26
30
  AutocompleteFilterWidget,
27
31
  SBAdminTreeWidgetMixin,
28
32
  )
29
33
  from django_smartbase_admin.services.thread_local import SBAdminThreadLocalService
30
34
  from django_smartbase_admin.templatetags.sb_admin_tags import SBAdminJSONEncoder
35
+ from django_smartbase_admin.utils import is_modal
31
36
 
32
37
  logger = logging.getLogger(__name__)
33
38
 
@@ -55,6 +60,25 @@ class SBAdminBaseWidget(ContextMixin):
55
60
  def get_context(self, name, value, attrs):
56
61
  context = super().get_context(name, value, attrs)
57
62
  context["widget"]["form_field"] = self.form_field
63
+ opts = (
64
+ self.form_field.view.opts
65
+ if self.form_field
66
+ and hasattr(self.form_field, "view")
67
+ and hasattr(self.form_field.view, "opts")
68
+ else None
69
+ )
70
+ modal_prefix = ""
71
+ try:
72
+ modal_prefix = (
73
+ "modal__" if is_modal(SBAdminThreadLocalService.get_request()) else ""
74
+ )
75
+ except:
76
+ pass
77
+ if opts:
78
+ context["widget"]["attrs"][
79
+ "id"
80
+ ] = f"{modal_prefix}{opts.app_label}_{opts.model_name}_{context['widget']['attrs']['id']}"
81
+
58
82
  return context
59
83
 
60
84
 
@@ -353,6 +377,16 @@ class SBAdminAutocompleteWidget(
353
377
  if not self.is_multiselect():
354
378
  query_suffix = ""
355
379
  self.multiselect = False
380
+ context["widget"]["attrs"]["preselect_field"] = threadsafe_request.GET.get(
381
+ "sbadmin_parent_instance_field"
382
+ )
383
+ context["widget"]["attrs"]["preselect_field_label"] = (
384
+ threadsafe_request.GET.get(SBADMIN_PARENT_INSTANCE_LABEL_VAR)
385
+ )
386
+ context["widget"]["attrs"]["preselect_field_value"] = (
387
+ threadsafe_request.GET.get(SBADMIN_PARENT_INSTANCE_PK_VAR)
388
+ )
389
+ parsed_value = None
356
390
  if value:
357
391
  parsed_value = self.parse_value_from_input(threadsafe_request, value)
358
392
  if parsed_value:
@@ -366,10 +400,38 @@ class SBAdminAutocompleteWidget(
366
400
  "label": self.get_label(threadsafe_request, item),
367
401
  }
368
402
  )
403
+
369
404
  context["widget"]["value"] = json.dumps(selected_options)
370
405
  context["widget"]["value_list"] = selected_options
406
+
407
+ if threadsafe_request.request_data.configuration.autocomplete_show_related_buttons(
408
+ self.model,
409
+ field_name=self.field_name,
410
+ current_view=self.view,
411
+ request=threadsafe_request,
412
+ ):
413
+ self.add_related_buttons_urls(parsed_value, context)
414
+
371
415
  return context
372
416
 
417
+ def add_related_buttons_urls(self, parsed_value, context):
418
+ related_model = self.model
419
+ app_label = related_model._meta.app_label
420
+ model_name = related_model._meta.model_name
421
+
422
+ try:
423
+ if parsed_value:
424
+ change_url = reverse(
425
+ "sb_admin:{}_{}_change".format(app_label, model_name),
426
+ args=(parsed_value,),
427
+ )
428
+ context["widget"]["attrs"]["related_edit_url"] = change_url
429
+
430
+ add_url = reverse("sb_admin:{}_{}_add".format(app_label, model_name))
431
+ context["widget"]["attrs"]["related_add_url"] = add_url
432
+ except NoReverseMatch:
433
+ pass
434
+
373
435
  def is_multiselect(self):
374
436
  if self.multiselect is not None:
375
437
  return self.multiselect
@@ -41,7 +41,13 @@ from django_smartbase_admin.services.xlsx_export import (
41
41
  SBAdminXLSXOptions,
42
42
  SBAdminXLSXFormat,
43
43
  )
44
- from django_smartbase_admin.utils import is_htmx_request, render_notifications
44
+ from django_smartbase_admin.utils import is_htmx_request, render_notifications, is_modal
45
+
46
+ SBADMIN_IS_MODAL_VAR = "sbadmin_is_modal"
47
+ SBADMIN_PARENT_INSTANCE_FIELD_NAME_VAR = "sbadmin_parent_instance_field"
48
+ SBADMIN_PARENT_INSTANCE_PK_VAR = "sbadmin_parent_instance_pk"
49
+ SBADMIN_PARENT_INSTANCE_LABEL_VAR = "sbadmin_parent_instance_label"
50
+ SBADMIN_RELOAD_ON_SAVE_VAR = "sbadmin_reload_on_save"
45
51
 
46
52
 
47
53
  class SBAdminBaseView(object):
@@ -96,7 +102,7 @@ class SBAdminBaseView(object):
96
102
  return inner_view
97
103
 
98
104
  def process_actions(
99
- self, request, actions: list[SBAdminCustomAction]
105
+ self, request, actions: list[SBAdminCustomAction]
100
106
  ) -> list[SBAdminCustomAction]:
101
107
  processed_actions = self.process_actions_permissions(request, actions)
102
108
  for processed_action in processed_actions:
@@ -113,7 +119,7 @@ class SBAdminBaseView(object):
113
119
  return processed_actions
114
120
 
115
121
  def process_actions_permissions(
116
- self, request, actions: list[SBAdminCustomAction]
122
+ self, request, actions: list[SBAdminCustomAction]
117
123
  ) -> list[SBAdminCustomAction]:
118
124
  result = []
119
125
  for action in actions:
@@ -192,12 +198,12 @@ class SBAdminBaseView(object):
192
198
  }
193
199
 
194
200
  def get_sbadmin_detail_actions(
195
- self, request, object_id: int | str | None = None
201
+ self, request, object_id: int | str | None = None
196
202
  ) -> Iterable[SBAdminCustomAction] | None:
197
203
  return self.sbadmin_detail_actions
198
204
 
199
205
  def get_global_context(
200
- self, request, object_id: int | str | None = None
206
+ self, request, object_id: int | str | None = None
201
207
  ) -> dict[str, Any]:
202
208
  return {
203
209
  "view_id": self.get_id(),
@@ -207,6 +213,9 @@ class SBAdminBaseView(object):
207
213
  "OVERRIDE_CONTENT_OF_NOTIFICATION": OVERRIDE_CONTENT_OF_NOTIFICATION,
208
214
  "username_data": self.get_username_data(request),
209
215
  "detail_actions": self.get_sbadmin_detail_actions(request, object_id),
216
+ SBADMIN_IS_MODAL_VAR: is_modal(request),
217
+ SBADMIN_RELOAD_ON_SAVE_VAR: SBADMIN_RELOAD_ON_SAVE_VAR in request.GET
218
+ or SBADMIN_RELOAD_ON_SAVE_VAR in request.POST,
210
219
  "const": json.dumps(
211
220
  {
212
221
  "MULTISELECT_FILTER_MAX_CHOICES_SHOWN": MULTISELECT_FILTER_MAX_CHOICES_SHOWN,
@@ -287,8 +296,8 @@ class SBAdminBaseListView(SBAdminBaseView):
287
296
 
288
297
  def is_reorder_active(self, request) -> bool:
289
298
  return (
290
- self.is_reorder_available(request)
291
- and getattr(request, "reorder_active", False) == True
299
+ self.is_reorder_available(request)
300
+ and getattr(request, "reorder_active", False) == True
292
301
  )
293
302
 
294
303
  def is_reorder_available(self, request) -> str | None:
@@ -323,7 +332,7 @@ class SBAdminBaseListView(SBAdminBaseView):
323
332
  qs.filter(**{f"{pk_field}__in": item_ids}).update(
324
333
  **{
325
334
  self.sbadmin_list_reorder_field: F(self.sbadmin_list_reorder_field)
326
- + int(diff)
335
+ + int(diff)
327
336
  }
328
337
  )
329
338
  return JsonResponse({"message": request.POST})
@@ -502,7 +511,7 @@ class SBAdminBaseListView(SBAdminBaseView):
502
511
  return self.sbadmin_list_selection_actions
503
512
 
504
513
  def get_sbadmin_list_selection_actions_grouped(
505
- self, request
514
+ self, request
506
515
  ) -> dict[str, list[SBAdminCustomAction]]:
507
516
  result = {}
508
517
  list_selection_actions = self.process_actions(
@@ -534,8 +543,8 @@ class SBAdminBaseListView(SBAdminBaseView):
534
543
  def action_bulk_delete(self, request, modifier):
535
544
  action = self.sbadmin_list_action_class(self, request)
536
545
  if (
537
- request.request_data.request_method == "POST"
538
- and request.headers.get("X-TabulatorRequest", None) == "true"
546
+ request.request_data.request_method == "POST"
547
+ and request.headers.get("X-TabulatorRequest", None) == "true"
539
548
  ):
540
549
  return redirect(
541
550
  self.get_action_url("action_bulk_delete")
@@ -613,13 +622,13 @@ class SBAdminBaseListView(SBAdminBaseView):
613
622
  return redirect_to
614
623
 
615
624
  def action_list(
616
- self,
617
- request,
618
- page_size=None,
619
- tabulator_definition=None,
620
- extra_context=None,
621
- list_actions=None,
622
- template=None,
625
+ self,
626
+ request,
627
+ page_size=None,
628
+ tabulator_definition=None,
629
+ extra_context=None,
630
+ list_actions=None,
631
+ template=None,
623
632
  ):
624
633
  action = self.sbadmin_list_action_class(
625
634
  self,
@@ -672,8 +681,8 @@ class SBAdminBaseListView(SBAdminBaseView):
672
681
  getattr(field, "filter_field", field): ""
673
682
  for field in list_fields
674
683
  if field in list_filter
675
- or getattr(field, "name", None) in list_filter
676
- or getattr(field, "filter_field", None) in list_filter
684
+ or getattr(field, "name", None) in list_filter
685
+ or getattr(field, "filter_field", None) in list_filter
677
686
  }
678
687
  url_params = None
679
688
  if base_filter:
@@ -748,7 +757,7 @@ class SBAdminBaseListView(SBAdminBaseView):
748
757
 
749
758
  def get_filters_version(self, request) -> FilterVersions:
750
759
  return (
751
- self.filters_version or request.request_data.configuration.filters_version
760
+ self.filters_version or request.request_data.configuration.filters_version
752
761
  )
753
762
 
754
763
  def get_filters_template_name(self, request) -> str: