django-smartbase-admin 0.2.75__py3-none-any.whl → 0.2.77__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.
@@ -184,7 +184,9 @@ class SBAdminListAction(SBAdminAction):
184
184
  tabulator_definition["tableIdColumnName"] = id_column_name
185
185
  tabulator_definition["constants"] = constants
186
186
 
187
- list_actions = self.list_actions or self.view._get_sbadmin_list_actions()
187
+ list_actions = self.list_actions or self.view._get_sbadmin_list_actions(
188
+ self.threadsafe_request
189
+ )
188
190
 
189
191
  context_data.update(
190
192
  {
@@ -452,7 +454,7 @@ class SBAdminListAction(SBAdminAction):
452
454
  additional_filter = ~Q(id__in=self.deselected_rows)
453
455
  return additional_filter
454
456
 
455
- def get_xlsx_data(self):
457
+ def get_xlsx_data(self, request):
456
458
  page_size = XLSX_PAGE_CHUNK_SIZE
457
459
  file_name = (
458
460
  f'{self.view.get_menu_label()}__{timezone.now().strftime("%Y-%m-%d")}.xlsx'
@@ -475,8 +477,8 @@ class SBAdminListAction(SBAdminAction):
475
477
  )["data"]
476
478
  )
477
479
  options = (
478
- self.view.get_sbadmin_xlsx_options().to_json()
479
- if self.view.get_sbadmin_xlsx_options()
480
+ self.view.get_sbadmin_xlsx_options(request).to_json()
481
+ if self.view.get_sbadmin_xlsx_options(request)
480
482
  else {}
481
483
  )
482
484
  return [file_name, data_list, columns, options]
@@ -1,30 +1,32 @@
1
1
  import json
2
2
  import urllib.parse
3
+ from functools import partial
3
4
 
4
5
  from ckeditor.fields import RichTextFormField
5
6
  from ckeditor_uploader.fields import RichTextUploadingFormField
6
7
  from django import forms
7
8
  from django.contrib import admin
8
9
  from django.contrib.admin.options import get_content_type_for_model
9
- from django.contrib.admin.utils import unquote, lookup_field
10
+ from django.contrib.admin.utils import unquote
10
11
  from django.contrib.admin.widgets import AdminTextareaWidget
11
12
  from django.contrib.auth.forms import UsernameField, ReadOnlyPasswordHashWidget
12
13
  from django.core.exceptions import (
13
14
  FieldDoesNotExist,
14
15
  ImproperlyConfigured,
15
16
  PermissionDenied,
16
- ObjectDoesNotExist,
17
17
  )
18
18
  from django.db import models
19
19
  from django.forms import HiddenInput
20
- from django.forms.models import ModelFormMetaclass
20
+ from django.forms.models import (
21
+ ModelFormMetaclass,
22
+ modelform_factory,
23
+ )
21
24
  from django.template.loader import render_to_string
22
25
  from django.template.response import TemplateResponse
23
26
  from django.urls import reverse
24
27
  from django.utils.safestring import mark_safe
25
28
  from django.utils.text import capfirst
26
29
  from django.utils.translation import gettext_lazy as _
27
- from django.utils.translation import override as translation_override
28
30
  from django_admin_inline_paginator.admin import TabularInlinePaginated
29
31
  from filer.fields.image import AdminImageFormField
30
32
  from nested_admin.nested import (
@@ -35,7 +37,6 @@ from nested_admin.nested import (
35
37
  NestedGenericStackedInline,
36
38
  )
37
39
 
38
- from django_smartbase_admin.actions.admin_action_list import SBAdminListAction
39
40
  from django_smartbase_admin.engine.actions import SBAdminCustomAction
40
41
  from django_smartbase_admin.services.thread_local import SBAdminThreadLocalService
41
42
  from django_smartbase_admin.utils import FormFieldsetMixin
@@ -434,6 +435,7 @@ if parler_enabled:
434
435
 
435
436
  class SBAdminInlineAndAdminCommon(SBAdminFormFieldWidgetsMixin):
436
437
  sbadmin_fake_inlines = None
438
+ all_base_fields_form = None
437
439
 
438
440
  def init_view_static(self, configuration, model, admin_site):
439
441
  configuration.view_map[self.get_id()] = self
@@ -488,6 +490,14 @@ class SBAdminInlineAndAdminCommon(SBAdminFormFieldWidgetsMixin):
488
490
  if form:
489
491
  form.view = self
490
492
 
493
+ def initialize_all_base_fields_form(self, request):
494
+ params = {
495
+ "form": self.form,
496
+ "fields": "__all__",
497
+ "formfield_callback": partial(self.formfield_for_dbfield, request=request),
498
+ }
499
+ self.all_base_fields_form = modelform_factory(self.model, **params)
500
+
491
501
 
492
502
  class SBAdminThirdParty(SBAdminInlineAndAdminCommon, SBAdminBaseView):
493
503
  def get_menu_view_url(self, request):
@@ -640,6 +650,7 @@ class SBAdmin(
640
650
  return self.sbadmin_list_filter or self.get_list_filter(request)
641
651
 
642
652
  def get_form(self, request, obj=None, **kwargs):
653
+ self.initialize_all_base_fields_form(request)
643
654
  form = super().get_form(request, obj, **kwargs)
644
655
  self.initialize_form_class(form)
645
656
  return form
@@ -775,30 +786,11 @@ class SBAdmin(
775
786
  ),
776
787
  }
777
788
 
778
- def get_readonly_base_fields_context(self, request, object_id=None):
779
- obj = None
780
- if object_id:
781
- obj = self.get_object(request, object_id)
782
- readonly_base_fields = {}
783
- for field in self.readonly_fields:
784
- base_field = self.form.base_fields.get(field)
785
- if base_field:
786
- try:
787
- f, attr, value = lookup_field(field, obj, self)
788
- except (AttributeError, ValueError, ObjectDoesNotExist):
789
- value = base_field.initial
790
- readonly_base_fields[field] = {
791
- "field": base_field,
792
- "value": value,
793
- }
794
- return {"readonly_base_fields": readonly_base_fields}
795
-
796
789
  def add_view(self, request, form_url="", extra_context=None):
797
790
  extra_context = extra_context or {}
798
791
  extra_context.update(self.get_global_context(request, None))
799
792
  extra_context.update(self.get_fieldsets_context(request, None))
800
793
  extra_context.update(self.get_tabs_context(request, None))
801
- extra_context.update(self.get_readonly_base_fields_context(request, None))
802
794
  return self.changeform_view(request, None, form_url, extra_context)
803
795
 
804
796
  def change_view(self, request, object_id, form_url="", extra_context=None):
@@ -807,7 +799,6 @@ class SBAdmin(
807
799
  extra_context.update(self.get_fieldsets_context(request, object_id))
808
800
  extra_context.update(self.get_tabs_context(request, object_id))
809
801
  extra_context.update(self.get_previous_next_context(request, object_id))
810
- extra_context.update(self.get_readonly_base_fields_context(request, object_id))
811
802
  return super().change_view(request, object_id, form_url, extra_context)
812
803
 
813
804
  def changelist_view(self, request, extra_context=None):
@@ -887,6 +878,7 @@ class SBAdminInline(
887
878
  sbadmin_inline_list_actions = None
888
879
  extra = 0
889
880
  ordering = None
881
+ all_base_fields_form = None
890
882
 
891
883
  def get_ordering(self, request):
892
884
  """
@@ -898,7 +890,7 @@ class SBAdminInline(
898
890
  qs = super().get_queryset(request)
899
891
  return qs.order_by(*self.get_ordering(request))
900
892
 
901
- def get_sbadmin_inline_list_actions(self):
893
+ def get_sbadmin_inline_list_actions(self, request):
902
894
  return [*(self.sbadmin_inline_list_actions or [])]
903
895
 
904
896
  def get_action_url(self, action, modifier="template"):
@@ -917,9 +909,14 @@ class SBAdminInline(
917
909
  self.initialize_form_class(form_class)
918
910
  form_class()
919
911
 
920
- @property
921
- def get_context_data(self):
922
- return {"inline_list_actions": self.get_sbadmin_inline_list_actions()}
912
+ def get_context_data(self, request):
913
+ is_sortable_active = self.sortable_field_name and (
914
+ self.has_add_permission(request) or self.has_change_permission(request)
915
+ )
916
+ return {
917
+ "inline_list_actions": self.get_sbadmin_inline_list_actions(request),
918
+ "is_sortable_active": is_sortable_active,
919
+ }
923
920
 
924
921
  def init_sortable_field(self):
925
922
  if not self.sortable_field_name:
@@ -956,6 +953,7 @@ class SBAdminInline(
956
953
  return formfield
957
954
 
958
955
  def get_formset(self, request, obj=None, **kwargs):
956
+ self.initialize_all_base_fields_form(request)
959
957
  formset = super().get_formset(request, obj, **kwargs)
960
958
  form_class = formset.form
961
959
  self.initialize_form_class(form_class)
@@ -11,6 +11,7 @@ class SBAdminConfig(AppConfig):
11
11
  def ready(self):
12
12
  super().ready()
13
13
  from .monkeypatch import fake_inline_monkeypatch
14
+ from .monkeypatch import admin_readonly_field_monkeypatch
14
15
 
15
16
  if settings.SB_ADMIN_CONFIGURATION:
16
17
  autodiscover_modules("sb_admin", register_to=sb_admin_site)
@@ -184,10 +184,10 @@ class SBAdminBaseView(object):
184
184
  "initials": request.request_data.user.username[0],
185
185
  }
186
186
 
187
- def get_sbadmin_detail_actions(self, object_id):
187
+ def get_sbadmin_detail_actions(self, request, object_id: int | str | None = None):
188
188
  return self.sbadmin_detail_actions
189
189
 
190
- def get_global_context(self, request, object_id=None):
190
+ def get_global_context(self, request, object_id: int | str | None = None):
191
191
  return {
192
192
  "view_id": self.get_id(),
193
193
  "configuration": request.request_data.configuration,
@@ -195,7 +195,7 @@ class SBAdminBaseView(object):
195
195
  "DETAIL_STRUCTURE_RIGHT_CLASS": DETAIL_STRUCTURE_RIGHT_CLASS,
196
196
  "OVERRIDE_CONTENT_OF_NOTIFICATION": OVERRIDE_CONTENT_OF_NOTIFICATION,
197
197
  "username_data": self.get_username_data(request),
198
- "detail_actions": self.get_sbadmin_detail_actions(object_id),
198
+ "detail_actions": self.get_sbadmin_detail_actions(request, object_id),
199
199
  "const": json.dumps(
200
200
  {
201
201
  "MULTISELECT_FILTER_MAX_CHOICES_SHOWN": MULTISELECT_FILTER_MAX_CHOICES_SHOWN,
@@ -326,27 +326,27 @@ class SBAdminBaseListView(SBAdminBaseView):
326
326
  def init_actions(self, request):
327
327
  if self.sbadmin_actions_initialized:
328
328
  return
329
- self.process_actions(request, self.get_sbadmin_list_selection_actions())
329
+ self.process_actions(request, self.get_sbadmin_list_selection_actions(request))
330
330
  self.sbadmin_actions_initialized = True
331
331
 
332
332
  def init_view_dynamic(self, request, request_data=None, **kwargs):
333
333
  super().init_view_dynamic(request, request_data, **kwargs)
334
334
  self.init_fields_cache(
335
- self.get_sbamin_list_display(request), request.request_data.configuration
335
+ self.get_sbadmin_list_display(request), request.request_data.configuration
336
336
  )
337
337
  self.init_actions(request)
338
338
 
339
- def get_sbamin_list_display(self, request):
339
+ def get_sbadmin_list_display(self, request):
340
340
  return self.sbadmin_list_display or self.list_display
341
341
 
342
342
  def register_autocomplete_views(self, request):
343
343
  super().register_autocomplete_views(request)
344
344
  self.init_fields_cache(
345
- self.get_sbamin_list_display(request),
345
+ self.get_sbadmin_list_display(request),
346
346
  request.request_data.configuration,
347
347
  force=True,
348
348
  )
349
- for list_action in self.get_sbadmin_list_selection_actions():
349
+ for list_action in self.get_sbadmin_list_selection_actions(request):
350
350
  if isinstance(list_action, SBAdminFormViewAction):
351
351
  form = list_action.target_view.form_class
352
352
  form.view = self
@@ -355,7 +355,7 @@ class SBAdminBaseListView(SBAdminBaseView):
355
355
  def get_list_display(self, request):
356
356
  return [
357
357
  getattr(field, "name", field)
358
- for field in self.get_sbamin_list_display(request)
358
+ for field in self.get_sbadmin_list_display(request)
359
359
  ]
360
360
 
361
361
  def get_search_fields(self, request):
@@ -447,8 +447,8 @@ class SBAdminBaseListView(SBAdminBaseView):
447
447
  )
448
448
  return tabulator_definition
449
449
 
450
- def _get_sbadmin_list_actions(self):
451
- list_actions = [*(self.get_sbadmin_list_actions() or [])]
450
+ def _get_sbadmin_list_actions(self, request):
451
+ list_actions = [*(self.get_sbadmin_list_actions(request) or [])]
452
452
  if self.is_reorder_available():
453
453
  list_actions = [
454
454
  *list_actions,
@@ -461,7 +461,7 @@ class SBAdminBaseListView(SBAdminBaseView):
461
461
  ]
462
462
  return list_actions
463
463
 
464
- def get_sbadmin_list_actions(self):
464
+ def get_sbadmin_list_actions(self, request):
465
465
  if not self.sbadmin_list_actions:
466
466
  self.sbadmin_list_actions = [
467
467
  SBAdminCustomAction(
@@ -472,7 +472,7 @@ class SBAdminBaseListView(SBAdminBaseView):
472
472
  ]
473
473
  return self.sbadmin_list_actions
474
474
 
475
- def get_sbadmin_list_selection_actions(self):
475
+ def get_sbadmin_list_selection_actions(self, request):
476
476
  if not self.sbadmin_list_selection_actions:
477
477
  self.sbadmin_list_selection_actions = [
478
478
  SBAdminCustomAction(
@@ -492,7 +492,7 @@ class SBAdminBaseListView(SBAdminBaseView):
492
492
  def get_sbadmin_list_selection_actions_grouped(self, request):
493
493
  result = {}
494
494
  list_selection_actions = self.process_actions(
495
- request, self.get_sbadmin_list_selection_actions()
495
+ request, self.get_sbadmin_list_selection_actions(request)
496
496
  )
497
497
  for action in list_selection_actions:
498
498
  if not result.get(action.group):
@@ -500,7 +500,7 @@ class SBAdminBaseListView(SBAdminBaseView):
500
500
  result[action.group].append(action)
501
501
  return result
502
502
 
503
- def get_sbadmin_xlsx_options(self):
503
+ def get_sbadmin_xlsx_options(self, request):
504
504
  self.sbadmin_xlsx_options = self.sbadmin_xlsx_options or SBAdminXLSXOptions(
505
505
  header_cell_format=SBAdminXLSXFormat(
506
506
  bg_color="#00aaa7", font_color="#ffffff", bold=True
@@ -514,7 +514,7 @@ class SBAdminBaseListView(SBAdminBaseView):
514
514
 
515
515
  def action_xlsx_export(self, request, modifier):
516
516
  action = self.sbadmin_list_action_class(self, request)
517
- data = action.get_xlsx_data()
517
+ data = action.get_xlsx_data(request)
518
518
  return SBAdminXLSXExportService.create_workbook_http_respone(*data)
519
519
 
520
520
  def action_bulk_delete(self, request, modifier):
@@ -553,19 +553,20 @@ class SBAdminBaseListView(SBAdminBaseView):
553
553
  updated_configuration = None
554
554
  if request.request_data.request_method == "POST":
555
555
  if name:
556
- updated_configuration, created = (
557
- SBAdminListViewConfiguration.objects.update_or_create(
558
- name=name,
559
- user_id=request.request_data.user.id,
560
- defaults={
561
- "url_params": request.request_data.request_post.get(
562
- URL_PARAMS_NAME
563
- ),
564
- "view": self.get_id(),
565
- "action": None,
566
- "modifier": None,
567
- },
568
- )
556
+ (
557
+ updated_configuration,
558
+ created,
559
+ ) = SBAdminListViewConfiguration.objects.update_or_create(
560
+ name=name,
561
+ user_id=request.request_data.user.id,
562
+ defaults={
563
+ "url_params": request.request_data.request_post.get(
564
+ URL_PARAMS_NAME
565
+ ),
566
+ "view": self.get_id(),
567
+ "action": None,
568
+ "modifier": None,
569
+ },
569
570
  )
570
571
  if request.request_data.request_method == "DELETE":
571
572
  if name:
@@ -644,7 +645,7 @@ class SBAdminBaseListView(SBAdminBaseView):
644
645
  list_filter = self.get_sbadmin_list_filter(request) or []
645
646
  if not list_filter:
646
647
  return all_config
647
- list_fields = self.get_sbamin_list_display(request) or []
648
+ list_fields = self.get_sbadmin_list_display(request) or []
648
649
  self.init_fields_cache(list_fields, request.request_data.configuration)
649
650
  base_filter = {
650
651
  getattr(field, "filter_field", field): ""
@@ -621,6 +621,8 @@ class AutocompleteFilterWidget(
621
621
  def get_label(self, request, item):
622
622
  if self.label_lambda:
623
623
  return self.label_lambda(request, item)
624
+ if isinstance(item, list):
625
+ return ", ".join(map(str, item))
624
626
  return str(item)
625
627
 
626
628
  def get_context(self, name, value, attrs):
@@ -0,0 +1,90 @@
1
+ import django.contrib.admin.helpers
2
+ from django.contrib.admin.utils import lookup_field, display_for_field
3
+ from django.core.exceptions import ObjectDoesNotExist
4
+ from django.db import models
5
+ from django.db.models import ManyToManyRel, ForeignObjectRel, OneToOneField
6
+ from django.template.defaultfilters import linebreaksbr
7
+ from django.template.loader import render_to_string
8
+ from django.utils.html import conditional_escape
9
+ from django.utils.safestring import mark_safe
10
+
11
+
12
+ class SBAdminReadonlyField(django.contrib.admin.helpers.AdminReadonlyField):
13
+ readonly_template = "sb_admin/includes/readonly_field.html"
14
+ readonly_boolean_template = "sb_admin/includes/readonly_boolean_field.html"
15
+
16
+ def _boolean_field_content(self, value):
17
+ return mark_safe(
18
+ render_to_string(
19
+ template_name=self.readonly_boolean_template,
20
+ context={
21
+ "field_label": self.field.get("label"),
22
+ "field_name": self.field.get("name"),
23
+ "value": value,
24
+ },
25
+ ),
26
+ )
27
+
28
+ def contents(self, request=None):
29
+ field, obj, model_admin = (
30
+ self.field["field"],
31
+ self.form.instance,
32
+ self.model_admin,
33
+ )
34
+ try:
35
+ f, attr, value = lookup_field(field, obj, model_admin)
36
+ except (AttributeError, ValueError, ObjectDoesNotExist):
37
+ result_repr = self.empty_value_display
38
+ else:
39
+ if field in self.form.fields:
40
+ widget = self.form[field].field.widget
41
+ # This isn't elegant but suffices for contrib.auth's
42
+ # ReadOnlyPasswordHashWidget.
43
+ if getattr(widget, "read_only", False):
44
+ return widget.render(field, value)
45
+ if f is None:
46
+ if getattr(attr, "boolean", False):
47
+ return self._boolean_field_content(value)
48
+ else:
49
+ if hasattr(value, "__html__"):
50
+ result_repr = value
51
+ else:
52
+ result_repr = linebreaksbr(value)
53
+ else:
54
+ base_field = self.form.fields.get(
55
+ field
56
+ ) or model_admin.all_base_fields_form.base_fields.get(field)
57
+ if isinstance(f.remote_field, ManyToManyRel) and value is not None:
58
+ # get label from widget if has base_field
59
+ if base_field:
60
+ result_repr = base_field.widget.get_label(
61
+ request, list(value.all())
62
+ )
63
+ else:
64
+ result_repr = ", ".join(map(str, value.all()))
65
+ elif (
66
+ isinstance(f.remote_field, (ForeignObjectRel, OneToOneField))
67
+ and value is not None
68
+ ):
69
+ # get label from widget if has base_field
70
+ if base_field:
71
+ result_repr = base_field.widget.get_label(
72
+ request, getattr(obj, field)
73
+ )
74
+ else:
75
+ result_repr = self.get_admin_url(f.remote_field, value)
76
+ else:
77
+ if isinstance(f, models.BooleanField):
78
+ return self._boolean_field_content(value)
79
+ result_repr = display_for_field(value, f, self.empty_value_display)
80
+ result_repr = linebreaksbr(result_repr)
81
+ return render_to_string(
82
+ template_name=self.readonly_template,
83
+ context={
84
+ "readonly_content": conditional_escape(result_repr),
85
+ "field_label": self.field.get("label"),
86
+ },
87
+ )
88
+
89
+
90
+ django.contrib.admin.helpers.AdminReadonlyField = SBAdminReadonlyField
@@ -1,3 +1,5 @@
1
+ {% load sb_admin_tags %}
2
+
1
3
  {% if fieldset.name %}
2
4
  <header class="mb-24">
3
5
  <span class="text-dark-900">{{ fieldset.name }}</span>
@@ -23,7 +25,7 @@
23
25
  {% for field in line %}
24
26
  <div class="mb-16 sm:mb-24 max-xs:w-full sm:flex-1{% if field.field.is_hidden %} hidden{% endif %}">
25
27
  {% if field.is_readonly %}
26
- {% include 'sb_admin/includes/readonly_field.html' %}
28
+ {% call_method field "contents" request %}
27
29
  {% else %}
28
30
  {{ field.field }}
29
31
  {% if not line.fields|length == 1 %}{{ field.errors }}{% endif %}
@@ -0,0 +1,9 @@
1
+ <div class="w-full">
2
+ <div class="text-14 w-full break-all">
3
+ <div class="relative">
4
+ <input type="checkbox" name="{{ field_name }}" id="id_{{ field_name }}" form="#" class="toggle"{% if value %} checked{% endif %} readonly disabled>
5
+ <label for="id_{{ field_name }}"></label>
6
+ <label for="id_{{ field_name }}">{{ field_label }}</label>
7
+ </div>
8
+ </div>
9
+ </div>
@@ -1,19 +1,10 @@
1
1
  {% load sb_admin_tags %}
2
2
 
3
3
  <div class="w-full">
4
- {% get_item readonly_base_fields field.field.name as readonly_field %}
5
- {% if readonly_field.field.widget.input_type == "checkbox" %}
6
- <div class="relative">
7
- <input type="checkbox" name="{{ field.field.name }}" id="id_{{ field.field.name }}" form="#" class="toggle"{% if readonly_field.value %} checked{% endif %} readonly disabled>
8
- <label for="id_{{ field.field.name }}"></label>
9
- <label for="id_{{ field.field.name }}">{{ field.field.label }}</label>
10
- </div>
11
- {% else %}
12
- {% if field.field.label %}
13
- <label class="inline-flex text-dark-900 text-14 leading-18 font-medium mb-8">{{ field.field.label }}:</label>
14
- {% endif %}
15
- <div class="text-14 w-full break-all">
16
- {{ field.contents }}
17
- </div>
4
+ {% if field_label %}
5
+ <label class="inline-flex text-dark-900 text-14 leading-18 font-medium mb-8">{{ field_label }}:</label>
18
6
  {% endif %}
7
+ <div class="text-14 w-full break-all">
8
+ {{ readonly_content }}
9
+ </div>
19
10
  </div>
@@ -1,6 +1,7 @@
1
1
  {% load admin_urlname admin_urlquote from admin_urls %}
2
2
  {% load i18n nested_admin static widget_tweaks sb_admin_tags %}
3
3
 
4
+ {% call_method inline_admin_formset.opts "get_context_data" request as context_data %}
4
5
  {% with inline_admin_formset.formset.is_nested as is_nested %}
5
6
 
6
7
  {% with inline_admin_formset.opts as inline_opts %}
@@ -18,7 +19,7 @@
18
19
  {{ inline_admin_formset.opts.verbose_name_plural|capfirst }}{% endif %}
19
20
  </span>
20
21
  <div class="ml-auto flex gap-8">
21
- {% for list_action in inline_admin_formset.opts.get_context_data.inline_list_actions %}
22
+ {% for list_action in context_data.inline_list_actions %}
22
23
  <a {% if list_action.open_in_modal %}{% include 'sb_admin/actions/partials/open_modal_attrs.html' with action=list_action %}{% endif %} href="{{ list_action.url }}"
23
24
  class="btn {{ list_action.css_class|default_if_none:'' }}">{{ list_action.title }}</a>
24
25
  {% endfor %}
@@ -47,7 +48,9 @@
47
48
  {% with inline_admin_formset.opts.sortable_field_name|default:"" as sortable_field_name %}
48
49
  <thead class="djn-module djn-thead">
49
50
  <tr>
50
- <th class="original{% if sortable_field_name %} is-sortable{% endif %}"></th>
51
+ {% if context_data.is_sortable_active %}
52
+ <th class="original{% if sortable_field_name %} is-sortable{% endif %}"></th>
53
+ {% endif %}
51
54
  {% for field in inline_admin_formset.fields %}
52
55
  {% if not field.widget.is_hidden %}
53
56
  <th class="djn-th
@@ -98,7 +101,7 @@
98
101
  </tr>
99
102
  {% endif %}
100
103
  <tr class="djn-tr form-row{% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}">
101
- <td class="original{% if inline_admin_formset.opts.sortable_field_name %} is-sortable{% endif %}">
104
+ <td class="original{% if inline_admin_formset.opts.sortable_field_name %} is-sortable{% endif %}" style="{% if not context_data.is_sortable_active %}display: none;{% endif %}">
102
105
  {% if inline_admin_formset.opts.sortable_field_name %}
103
106
  <div class="djn-drag-handler">
104
107
  <svg class="w-20 h-20 text-dark-400">
@@ -145,7 +148,9 @@
145
148
  {% if not field.field.is_hidden %}
146
149
  <td class="djn-td field-{{ field.field.name }}">
147
150
  {% if field.is_readonly %}
148
- <div class="px-10 py-8">{{ field.contents }}</div>
151
+ <div class="px-10 py-8">
152
+ {% call_method field "contents" request %}
153
+ </div>
149
154
  {% else %}
150
155
  {{ field.field }}
151
156
  {% if field.field.errors %}
@@ -212,3 +212,11 @@ def get_log_entry_message(log_entry):
212
212
  return get_change_message_legacy(log_entry)
213
213
  except Exception as e:
214
214
  return ""
215
+
216
+
217
+ @register.simple_tag
218
+ def call_method(obj, method_name, *args):
219
+ method = getattr(obj, method_name, None)
220
+ if method:
221
+ return method(*args)
222
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-smartbase-admin
3
- Version: 0.2.75
3
+ Version: 0.2.77
4
4
  Summary:
5
5
  Author: SmartBase
6
6
  Author-email: info@smartbase.sk
@@ -9,6 +9,7 @@ Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.10
10
10
  Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
12
13
  Requires-Dist: django (>=4.1,<6.0)
13
14
  Requires-Dist: django-admin-inline-paginator (>=0.4.0,<0.5.0)
14
15
  Requires-Dist: django-ckeditor (>=6.7.1,<7.0.0)
@@ -1,16 +1,16 @@
1
1
  django_smartbase_admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  django_smartbase_admin/actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- django_smartbase_admin/actions/admin_action_list.py,sha256=EWk0KTXDVVJDT8bh052xazukTpYFFOzrKTp36m1QNzE,19229
3
+ django_smartbase_admin/actions/admin_action_list.py,sha256=2DthQWmdzUh1Bh4d8xZjpFqhwzzaIBk_QJsSXKIrZgA,19297
4
4
  django_smartbase_admin/actions/advanced_filters.py,sha256=Vm8b6TAwNehR8INjolFG7pEYL4ADO7XUiVOWpb0btM0,13481
5
5
  django_smartbase_admin/admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- django_smartbase_admin/admin/admin_base.py,sha256=9fN3c41fNkav7uQiGfEnJaiqYKfDk57VrlYgiN04DE8,40100
6
+ django_smartbase_admin/admin/admin_base.py,sha256=wMAjZW9YpUsO_5nBZOIUAZUuonQHKZwQk06Gd32nQP0,39787
7
7
  django_smartbase_admin/admin/site.py,sha256=VrJBhwgZsLa2GohvjnNL7m4dVR3S4Ou1V1UzfE1qOoQ,6577
8
8
  django_smartbase_admin/admin/widgets.py,sha256=udNG3fqDSTKnbPpvY02qhanPoCm_Kgn7Bg8R3EjWJT4,15035
9
- django_smartbase_admin/apps.py,sha256=3Wm88v1u-ejKVSRsdgsnOrjOfdlsp1qVULskabgH1iY,479
9
+ django_smartbase_admin/apps.py,sha256=C1wT1YUEZNKcUJfpD01nIZEFgYEsuav52WFKvEURRDU,545
10
10
  django_smartbase_admin/compilemessages.py,sha256=-_FEFQlOvE4L8UzSuUxSxZQjgGlwL9IZtmg59fW_kIQ,342
11
11
  django_smartbase_admin/engine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  django_smartbase_admin/engine/actions.py,sha256=n8PiG60Kj1ECHB4lfVH_jvHMyOjOZ-DvQfO9F2CuqW0,1733
13
- django_smartbase_admin/engine/admin_base_view.py,sha256=ni1UV_nc_oeccRhKi9nyy520QB18nhZnmmcdaAIE6Hs,28475
13
+ django_smartbase_admin/engine/admin_base_view.py,sha256=0yMQO-Aa9UtEBcqcgPEJ6VpkaEa30LetmCRfkaH3X2E,28591
14
14
  django_smartbase_admin/engine/admin_entrypoint_view.py,sha256=jfMfcYPfdre2abHfC4KIxaP_epJFuCeTcujGhGd4Tl4,624
15
15
  django_smartbase_admin/engine/admin_view.py,sha256=9wGffahDR3IYmhL9ZbX8uitwGdXdw5DIL5GnWBawmJM,4238
16
16
  django_smartbase_admin/engine/configuration.py,sha256=P3iSiPm9QBl9etTDJIWzo7DzOBCryGwWtbF288PEtus,8120
@@ -19,7 +19,7 @@ django_smartbase_admin/engine/dashboard.py,sha256=wrvX0GI-SII2pG0DX8Kvy4JFaM6h8e
19
19
  django_smartbase_admin/engine/fake_inline.py,sha256=9C2_mltg2P9-xa3vuoo5X_RcFaCRpKGNSy7t1_iiasE,5802
20
20
  django_smartbase_admin/engine/field.py,sha256=BD_W0ekE5tQJxUGpUA8bo4ZyFk0u2F_6UTPH4drj0JU,10286
21
21
  django_smartbase_admin/engine/field_formatter.py,sha256=eHbXEBwG8jBc9zKpDQ4T64N5J4afF0Cbb8RFeFfgQRg,1950
22
- django_smartbase_admin/engine/filter_widgets.py,sha256=lHPMilA2tjtwnwqPw2nUE39BL1Dx_v4HNKhmg6szHUY,23687
22
+ django_smartbase_admin/engine/filter_widgets.py,sha256=dA5tUQ3d6STmMhMWj0pYH_G1tl0eYX3ZuDQ8b7qHLrE,23767
23
23
  django_smartbase_admin/engine/global_filter_form.py,sha256=jlj6e9VYWDPyUNjcgj3gTIkCZXO01NZOWAeU3jFtkoA,249
24
24
  django_smartbase_admin/engine/menu_item.py,sha256=rMJFG5xm1T0wF8lUmiAZHzKtt9C0LdbpRFvaP0PlngM,2936
25
25
  django_smartbase_admin/engine/modal_view.py,sha256=6eAmncDV9UzTWemWFQXZpVCIixWo5gWvNZGnK4mw4FM,2251
@@ -35,6 +35,7 @@ django_smartbase_admin/migrations/0003_auto_20230402_2328.py,sha256=-4fknf4126mz
35
35
  django_smartbase_admin/migrations/0004_alter_sbadminlistviewconfiguration_action_and_more.py,sha256=j3cz6sz5ahxkIuHYlSU0opCt6iLolrnda6u9wRDbUsE,648
36
36
  django_smartbase_admin/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  django_smartbase_admin/models.py,sha256=mvIe4-X8sapMfISZ1KitkEX8ljmVisVbTjxf_hgIjzs,743
38
+ django_smartbase_admin/monkeypatch/admin_readonly_field_monkeypatch.py,sha256=2lDw11br-xPG9cnFk1PTG91d8PfsEzclSEpHe21shjs,3870
38
39
  django_smartbase_admin/monkeypatch/fake_inline_monkeypatch.py,sha256=Wfrz_BmgF4XBKsqncaB3uyZaE13OOdPpQWsZe5_Idls,833
39
40
  django_smartbase_admin/querysets.py,sha256=ST_6qxznqxJiv8Gk1bgYVMpK_gbU74ItKyvMmVmJX04,385
40
41
  django_smartbase_admin/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -579,14 +580,15 @@ django_smartbase_admin/templates/sb_admin/includes/change_form_title.html,sha256
579
580
  django_smartbase_admin/templates/sb_admin/includes/components.html,sha256=ixhN__EcMbCfjXQXdCElda18sqWZYj9scV3nsR9nXts,18822
580
581
  django_smartbase_admin/templates/sb_admin/includes/confirmation.html,sha256=F2lgUPCIF6pveCSyS7L4k8ifZxa_DoJp-NZb4uwmtyI,1844
581
582
  django_smartbase_admin/templates/sb_admin/includes/fieldset.html,sha256=cSaqkdB2OOMXW4UFysoDQP4x-Wm4fgMtClPHqHepv1o,280
582
- django_smartbase_admin/templates/sb_admin/includes/inline_fieldset.html,sha256=A_SDX9ltW3Jb5vOhhYZAS3W5YkIs4k3r8dbDp3z_XF0,1921
583
+ django_smartbase_admin/templates/sb_admin/includes/inline_fieldset.html,sha256=ov6iySp8Oo8VZ8Z54kTHh6PF254OufH2xqmqp0NX_W4,1936
583
584
  django_smartbase_admin/templates/sb_admin/includes/loading.html,sha256=mI-Ya6y0vTOqPVUS9n_GPXmQDvyaDdL9637I5oqcnBE,287
584
585
  django_smartbase_admin/templates/sb_admin/includes/loading_absolute.html,sha256=Eck62NW9ByTw6Mu5QOcYZZJVebhRUNbxTB29XmN5drE,304
585
586
  django_smartbase_admin/templates/sb_admin/includes/notifications.html,sha256=wEEcrcdX3Wrlz-tVCJouiUFSZV_vVEqrc5opV_nifAw,783
586
- django_smartbase_admin/templates/sb_admin/includes/readonly_field.html,sha256=ssFeGHgEVggIsVFLFLFgvBkJQlMPqnykPWheEXRl4jM,873
587
+ django_smartbase_admin/templates/sb_admin/includes/readonly_boolean_field.html,sha256=sCU7DKBeWVD7ILcOAd20APS3gTBLBwXJpDjzTETwp5M,412
588
+ django_smartbase_admin/templates/sb_admin/includes/readonly_field.html,sha256=uGysgcSJJRIwG-AW9Vtfysulz4HOKUNH9Wx0f8sZkuE,291
587
589
  django_smartbase_admin/templates/sb_admin/includes/table_inline_delete_button.html,sha256=pgXePfkFu71wquoKGs-q-TQbuUuLqNEh5-MOINPp39o,1109
588
590
  django_smartbase_admin/templates/sb_admin/inlines/stacked_inline.html,sha256=fB__KCS8tQq8zU_WMKVQcXJC20OLoG-pCyb_CYzMYXU,7592
589
- django_smartbase_admin/templates/sb_admin/inlines/table_inline.html,sha256=jtMX6gS2iCm_jw1QYYB3Cznc7GDKfJzH9qQMxzcc7_4,14388
591
+ django_smartbase_admin/templates/sb_admin/inlines/table_inline.html,sha256=WxdbhtKrJOjbvHTC7vKmm5pILVGqXlJxO_n3sroZMZI,14816
590
592
  django_smartbase_admin/templates/sb_admin/inlines/table_inline_paginated.html,sha256=2I59KWM_7yvchNjlIWHaHklrklnNsJBwaLlp9mZUYfQ,3046
591
593
  django_smartbase_admin/templates/sb_admin/integrations/cms/page_list.html,sha256=juoU5UaqPozIgRX5EJyWpm2-mb1hqM2pfBoePZ1Vs-I,18190
592
594
  django_smartbase_admin/templates/sb_admin/integrations/cms/translations_status_row.html,sha256=Ppm6oEmNZkZC_XV5QNFbgccsBumB6LVy1e0Vk8dPMCM,308
@@ -650,7 +652,7 @@ django_smartbase_admin/templates/sb_admin/widgets/toggle.html,sha256=H9D_SIeS2wn
650
652
  django_smartbase_admin/templates/sb_admin/widgets/url.html,sha256=piw2218tiWPfOwncIvSGAW14Gc5FIzkL_YllirK4yTs,160
651
653
  django_smartbase_admin/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
652
654
  django_smartbase_admin/templatetags/base.py,sha256=XF1wUQxUjULXfdlV-PeHvITfbw65LTHjlDzRikJa_F4,1520
653
- django_smartbase_admin/templatetags/sb_admin_tags.py,sha256=c19x35fXj0YCtHOxRfteTdrUzFJ8oQrb_y7kVy9t6uY,7445
655
+ django_smartbase_admin/templatetags/sb_admin_tags.py,sha256=FozRhcHjPxFFnJUb1zYWVI97eJg7up3b7qBtr14wUX0,7616
654
656
  django_smartbase_admin/urls.py,sha256=Bjdewssljt0LIefjixBem9JN0kkghPvmrIETj3GdXbY,17
655
657
  django_smartbase_admin/utils.py,sha256=0SUIA-n6XiPeDH7PvdekV1xIKwUjvyPxaBPEKsZKXyM,2775
656
658
  django_smartbase_admin/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -658,7 +660,7 @@ django_smartbase_admin/views/dashboard_view.py,sha256=vtz5emYTQ5WDFeLA8HrcmjSOVd
658
660
  django_smartbase_admin/views/global_filter_view.py,sha256=eYo1moJGyi7jc2cPDA5ZBiEgA7Hmc-DxbQvbqUpDkg8,1127
659
661
  django_smartbase_admin/views/media_view.py,sha256=5BLWXuzynF7nM34t-mf2BQSRN5ojY8HxpLIqt7Jiq9g,292
660
662
  django_smartbase_admin/views/translations_view.py,sha256=A02q1t13akLKd2Pg_ej4tVi5qPUKYxB2LqsBbYXM0l8,20267
661
- django_smartbase_admin-0.2.75.dist-info/LICENSE.md,sha256=okRGMBOYvyhprt2eTpX_QXqpzC0MODF-U7zX-4fKPjQ,1078
662
- django_smartbase_admin-0.2.75.dist-info/METADATA,sha256=3LhBbwW73p19qKtw2aJIJsXXsopbCj00ofFL3RF8R0U,945
663
- django_smartbase_admin-0.2.75.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
664
- django_smartbase_admin-0.2.75.dist-info/RECORD,,
663
+ django_smartbase_admin-0.2.77.dist-info/LICENSE.md,sha256=okRGMBOYvyhprt2eTpX_QXqpzC0MODF-U7zX-4fKPjQ,1078
664
+ django_smartbase_admin-0.2.77.dist-info/METADATA,sha256=9e4X45QTy1KclVmnQ4Q5DUAJLx_MTpj008Ix2ibQBjQ,996
665
+ django_smartbase_admin-0.2.77.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
666
+ django_smartbase_admin-0.2.77.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 1.9.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any