django-smartbase-admin 1.0.38__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/field_formatter.py +25 -15
- django_smartbase_admin/engine/filter_widgets.py +0 -5
- 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/_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 +1 -1
- django_smartbase_admin/templatetags/sb_admin_tags.py +30 -0
- {django_smartbase_admin-1.0.38.dist-info → django_smartbase_admin-1.0.40.dist-info}/METADATA +1 -1
- {django_smartbase_admin-1.0.38.dist-info → django_smartbase_admin-1.0.40.dist-info}/RECORD +14 -13
- {django_smartbase_admin-1.0.38.dist-info → django_smartbase_admin-1.0.40.dist-info}/LICENSE.md +0 -0
- {django_smartbase_admin-1.0.38.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, {}
|
|
@@ -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
|
|
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
|