django-smartbase-admin 0.2.47__py3-none-any.whl → 1.0.38__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 (188) hide show
  1. django_smartbase_admin/actions/admin_action_list.py +80 -51
  2. django_smartbase_admin/actions/advanced_filters.py +55 -20
  3. django_smartbase_admin/admin/admin_base.py +477 -89
  4. django_smartbase_admin/admin/site.py +104 -34
  5. django_smartbase_admin/admin/widgets.py +598 -26
  6. django_smartbase_admin/apps.py +2 -0
  7. django_smartbase_admin/engine/actions.py +34 -16
  8. django_smartbase_admin/engine/admin_base_view.py +253 -115
  9. django_smartbase_admin/engine/configuration.py +186 -4
  10. django_smartbase_admin/engine/const.py +7 -0
  11. django_smartbase_admin/engine/dashboard.py +44 -23
  12. django_smartbase_admin/engine/fake_inline.py +44 -7
  13. django_smartbase_admin/engine/field.py +54 -10
  14. django_smartbase_admin/engine/field_formatter.py +32 -9
  15. django_smartbase_admin/engine/filter_widgets.py +356 -21
  16. django_smartbase_admin/engine/menu_item.py +8 -5
  17. django_smartbase_admin/engine/modal_view.py +12 -7
  18. django_smartbase_admin/engine/request.py +2 -0
  19. django_smartbase_admin/integration/__init__.py +0 -0
  20. django_smartbase_admin/integration/django_cms.py +43 -0
  21. django_smartbase_admin/locale/sk/LC_MESSAGES/django.mo +0 -0
  22. django_smartbase_admin/locale/sk/LC_MESSAGES/django.po +268 -37
  23. django_smartbase_admin/migrations/0005_sbadminuserconfiguration.py +26 -0
  24. django_smartbase_admin/migrations/0006_alter_sbadminuserconfiguration_color_scheme.py +18 -0
  25. django_smartbase_admin/models.py +22 -0
  26. django_smartbase_admin/monkeypatch/admin_readonly_field_monkeypatch.py +96 -0
  27. django_smartbase_admin/monkeypatch/fake_inline_monkeypatch.py +1 -1
  28. django_smartbase_admin/querysets.py +3 -0
  29. django_smartbase_admin/services/configuration.py +30 -0
  30. django_smartbase_admin/services/thread_local.py +6 -19
  31. django_smartbase_admin/services/views.py +82 -27
  32. django_smartbase_admin/services/xlsx_export.py +6 -0
  33. django_smartbase_admin/static/sb_admin/build/tailwind.config.js +1 -0
  34. django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/colors.js +4 -0
  35. django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/spacing.js +1 -0
  36. django_smartbase_admin/static/sb_admin/build/webpack.common.js +11 -8
  37. django_smartbase_admin/static/sb_admin/css/ckeditor/ckeditor_content_dark.css +208 -0
  38. django_smartbase_admin/static/sb_admin/css/coloris/coloris.min.css +1 -0
  39. django_smartbase_admin/static/sb_admin/dist/calendar.js +1 -0
  40. django_smartbase_admin/static/sb_admin/dist/calendar_style.css +1 -0
  41. django_smartbase_admin/static/sb_admin/dist/calendar_style.js +0 -0
  42. django_smartbase_admin/static/sb_admin/dist/chart.js +1 -1
  43. django_smartbase_admin/static/sb_admin/dist/main.js +1 -1
  44. django_smartbase_admin/static/sb_admin/dist/main_style.css +1 -1
  45. django_smartbase_admin/static/sb_admin/dist/table.js +1 -1
  46. django_smartbase_admin/static/sb_admin/dist/table.js.LICENSE.txt +9 -0
  47. django_smartbase_admin/static/sb_admin/dist/tree_widget.js +1 -0
  48. django_smartbase_admin/static/sb_admin/dist/tree_widget_style.css +1 -0
  49. django_smartbase_admin/static/sb_admin/dist/tree_widget_style.js +0 -0
  50. django_smartbase_admin/static/sb_admin/fancytree/jquery.fancytree-all-deps.min.js +1 -0
  51. django_smartbase_admin/static/sb_admin/images/file_types/file-csv.svg +11 -0
  52. django_smartbase_admin/static/sb_admin/images/file_types/file-doc.svg +11 -0
  53. django_smartbase_admin/static/sb_admin/images/file_types/file-docx.svg +11 -0
  54. django_smartbase_admin/static/sb_admin/images/file_types/file-other.svg +13 -0
  55. django_smartbase_admin/static/sb_admin/images/file_types/file-pdf.svg +11 -0
  56. django_smartbase_admin/static/sb_admin/images/file_types/file-ppt.svg +11 -0
  57. django_smartbase_admin/static/sb_admin/images/file_types/file-xls.svg +11 -0
  58. django_smartbase_admin/static/sb_admin/images/file_types/file-xlsx.svg +11 -0
  59. django_smartbase_admin/static/sb_admin/images/file_types/file-zip.svg +18 -0
  60. django_smartbase_admin/static/sb_admin/images/flags/de-at.png +0 -0
  61. django_smartbase_admin/static/sb_admin/images/flags/de-ch.png +0 -0
  62. django_smartbase_admin/static/sb_admin/images/logo_light.svg +21 -0
  63. django_smartbase_admin/static/sb_admin/js/coloris/coloris.min.js +6 -0
  64. django_smartbase_admin/static/sb_admin/js/fullcalendar.min.js +14804 -0
  65. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Bolt-one.svg +3 -0
  66. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Calendar.svg +3 -0
  67. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Caution.svg +3 -0
  68. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Electric-drill.svg +3 -0
  69. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Fire-extinguisher.svg +3 -0
  70. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Gas.svg +3 -0
  71. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Lightning-fill.svg +3 -0
  72. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Moon.svg +3 -0
  73. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Phone-telephone.svg +3 -0
  74. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Printer.svg +3 -0
  75. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Pull.svg +3 -0
  76. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Sun-one.svg +3 -0
  77. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Time.svg +3 -0
  78. django_smartbase_admin/static/sb_admin/src/css/_base.css +5 -1
  79. django_smartbase_admin/static/sb_admin/src/css/_colors.css +257 -82
  80. django_smartbase_admin/static/sb_admin/src/css/_components.css +66 -13
  81. django_smartbase_admin/static/sb_admin/src/css/_datepicker.css +8 -1
  82. django_smartbase_admin/static/sb_admin/src/css/_filer.css +60 -0
  83. django_smartbase_admin/static/sb_admin/src/css/_inlines.css +51 -10
  84. django_smartbase_admin/static/sb_admin/src/css/_tabulator.css +8 -2
  85. django_smartbase_admin/static/sb_admin/src/css/calendar.css +162 -0
  86. django_smartbase_admin/static/sb_admin/src/css/components/_button.css +41 -1
  87. django_smartbase_admin/static/sb_admin/src/css/components/_dropdown.css +26 -8
  88. django_smartbase_admin/static/sb_admin/src/css/components/_input.css +62 -20
  89. django_smartbase_admin/static/sb_admin/src/css/components/_modal.css +1 -1
  90. django_smartbase_admin/static/sb_admin/src/css/components/_query-builder.css +21 -2
  91. django_smartbase_admin/static/sb_admin/src/css/components/_toggle.css +12 -1
  92. django_smartbase_admin/static/sb_admin/src/css/components/_tooltip.css +8 -22
  93. django_smartbase_admin/static/sb_admin/src/css/style.css +17 -0
  94. django_smartbase_admin/static/sb_admin/src/css/tree_widget.css +411 -0
  95. django_smartbase_admin/static/sb_admin/src/js/autocomplete.js +63 -5
  96. django_smartbase_admin/static/sb_admin/src/js/calendar.js +56 -0
  97. django_smartbase_admin/static/sb_admin/src/js/chart.js +8 -22
  98. django_smartbase_admin/static/sb_admin/src/js/choices.js +18 -8
  99. django_smartbase_admin/static/sb_admin/src/js/datepicker.js +97 -336
  100. django_smartbase_admin/static/sb_admin/src/js/datepicker_plugins.js +357 -0
  101. django_smartbase_admin/static/sb_admin/src/js/main.js +307 -26
  102. django_smartbase_admin/static/sb_admin/src/js/multiselect.js +50 -41
  103. django_smartbase_admin/static/sb_admin/src/js/range.js +3 -2
  104. django_smartbase_admin/static/sb_admin/src/js/sb_ajax_params_tabulator_modifier.js +21 -0
  105. django_smartbase_admin/static/sb_admin/src/js/table.js +38 -13
  106. django_smartbase_admin/static/sb_admin/src/js/table_modules/advanced_filter_module.js +43 -20
  107. django_smartbase_admin/static/sb_admin/src/js/table_modules/data_edit_module.js +8 -10
  108. django_smartbase_admin/static/sb_admin/src/js/table_modules/filter_module.js +3 -3
  109. django_smartbase_admin/static/sb_admin/src/js/table_modules/header_tabs_module.js +11 -11
  110. django_smartbase_admin/static/sb_admin/src/js/table_modules/selection_module.js +28 -8
  111. django_smartbase_admin/static/sb_admin/src/js/table_modules/table_params_module.js +6 -0
  112. django_smartbase_admin/static/sb_admin/src/js/table_modules/views_module.js +19 -3
  113. django_smartbase_admin/static/sb_admin/src/js/tree_widget.js +406 -0
  114. django_smartbase_admin/static/sb_admin/src/js/utils.js +56 -21
  115. django_smartbase_admin/templates/sb_admin/actions/change_form.html +169 -117
  116. django_smartbase_admin/templates/sb_admin/actions/dashboard.html +2 -2
  117. django_smartbase_admin/templates/sb_admin/actions/delete_selected_confirmation.html +56 -32
  118. django_smartbase_admin/templates/sb_admin/actions/list.html +79 -42
  119. django_smartbase_admin/templates/sb_admin/actions/object_history.html +2 -2
  120. django_smartbase_admin/templates/sb_admin/actions/partials/action_link.html +14 -0
  121. django_smartbase_admin/templates/sb_admin/actions/partials/selected_rows_actions.html +2 -2
  122. django_smartbase_admin/templates/sb_admin/actions/partials/tabulator_header_v2.html +2 -2
  123. django_smartbase_admin/templates/sb_admin/actions/tree_list.html +63 -0
  124. django_smartbase_admin/templates/sb_admin/authentification/login_base.html +5 -1
  125. django_smartbase_admin/templates/sb_admin/components/columns.html +1 -1
  126. django_smartbase_admin/templates/sb_admin/components/filters_v2.html +99 -85
  127. django_smartbase_admin/templates/sb_admin/config/view.html +0 -1
  128. django_smartbase_admin/templates/sb_admin/dashboard/calendar_widget.html +69 -0
  129. django_smartbase_admin/templates/sb_admin/dashboard/chart_widget.html +21 -2
  130. django_smartbase_admin/templates/sb_admin/dashboard/list_widget.html +6 -0
  131. django_smartbase_admin/templates/sb_admin/dashboard/widget_base.html +1 -1
  132. django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/date_field.html +18 -8
  133. django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/multiple_choice_field.html +1 -1
  134. django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/tree_select_filter.html +2 -0
  135. django_smartbase_admin/templates/sb_admin/filter_widgets/date_field.html +18 -4
  136. django_smartbase_admin/templates/sb_admin/filter_widgets/multiple_choice_field.html +14 -0
  137. django_smartbase_admin/templates/sb_admin/filter_widgets/partials/clear.html +10 -5
  138. django_smartbase_admin/templates/sb_admin/filter_widgets/radio_choice_field.html +2 -2
  139. django_smartbase_admin/templates/sb_admin/filter_widgets/tree_select_filter.html +16 -0
  140. django_smartbase_admin/templates/sb_admin/includes/change_form_title.html +3 -1
  141. django_smartbase_admin/templates/sb_admin/includes/components.html +5 -1
  142. django_smartbase_admin/templates/sb_admin/includes/inline_fieldset.html +48 -39
  143. django_smartbase_admin/templates/sb_admin/includes/notifications.html +2 -1
  144. django_smartbase_admin/templates/sb_admin/includes/readonly_boolean_field.html +9 -0
  145. django_smartbase_admin/templates/sb_admin/includes/readonly_field.html +12 -0
  146. django_smartbase_admin/templates/sb_admin/includes/table_inline_delete_button.html +4 -5
  147. django_smartbase_admin/templates/sb_admin/inlines/stacked_inline.html +68 -40
  148. django_smartbase_admin/templates/sb_admin/inlines/table_inline.html +78 -36
  149. django_smartbase_admin/templates/sb_admin/integrations/filer/folder_list.html +18 -0
  150. django_smartbase_admin/templates/sb_admin/navigation.html +166 -158
  151. django_smartbase_admin/templates/sb_admin/partials/modal/modal_content.html +2 -6
  152. django_smartbase_admin/templates/sb_admin/sb_admin_base.html +49 -4
  153. django_smartbase_admin/templates/sb_admin/sb_admin_base_no_sidebar.html +35 -11
  154. django_smartbase_admin/templates/sb_admin/sb_admin_js_trans.html +3 -0
  155. django_smartbase_admin/templates/sb_admin/sprites/sb_admin.svg +1 -1
  156. django_smartbase_admin/templates/sb_admin/tailwind_whitelist.html +6 -3
  157. django_smartbase_admin/templates/sb_admin/widgets/array.html +0 -1
  158. django_smartbase_admin/templates/sb_admin/widgets/attributes.html +68 -0
  159. django_smartbase_admin/templates/sb_admin/widgets/autocomplete.html +13 -2
  160. django_smartbase_admin/templates/sb_admin/widgets/{checkbox_select.html → checkbox_dropdown.html} +2 -2
  161. django_smartbase_admin/templates/sb_admin/widgets/checkbox_group.html +15 -0
  162. django_smartbase_admin/templates/sb_admin/widgets/clearable_file_input.html +2 -2
  163. django_smartbase_admin/templates/sb_admin/widgets/color_field.html +30 -0
  164. django_smartbase_admin/templates/sb_admin/widgets/date.html +8 -1
  165. django_smartbase_admin/templates/sb_admin/widgets/filer_file.html +84 -0
  166. django_smartbase_admin/templates/sb_admin/widgets/html_read_only.html +1 -0
  167. django_smartbase_admin/templates/sb_admin/widgets/includes/related_item_buttons.html +38 -0
  168. django_smartbase_admin/templates/sb_admin/widgets/multiwidget.html +1 -1
  169. django_smartbase_admin/templates/sb_admin/widgets/radio.html +3 -2
  170. django_smartbase_admin/templates/sb_admin/widgets/radio_dropdown.html +30 -0
  171. django_smartbase_admin/templates/sb_admin/widgets/read_only_password_hash.html +3 -0
  172. django_smartbase_admin/templates/sb_admin/widgets/time.html +8 -1
  173. django_smartbase_admin/templates/sb_admin/widgets/toggle.html +1 -1
  174. django_smartbase_admin/templates/sb_admin/widgets/tree_base.html +59 -0
  175. django_smartbase_admin/templates/sb_admin/widgets/tree_select.html +24 -0
  176. django_smartbase_admin/templates/sb_admin/widgets/tree_select_inline.html +12 -0
  177. django_smartbase_admin/templatetags/sb_admin_tags.py +163 -4
  178. django_smartbase_admin/utils.py +22 -3
  179. django_smartbase_admin/views/dashboard_view.py +6 -0
  180. django_smartbase_admin/views/global_filter_view.py +8 -2
  181. django_smartbase_admin/views/translations_view.py +12 -5
  182. django_smartbase_admin/views/user_config_view.py +52 -0
  183. django_smartbase_admin-1.0.38.dist-info/METADATA +166 -0
  184. {django_smartbase_admin-0.2.47.dist-info → django_smartbase_admin-1.0.38.dist-info}/RECORD +186 -121
  185. {django_smartbase_admin-0.2.47.dist-info → django_smartbase_admin-1.0.38.dist-info}/WHEEL +1 -1
  186. django_smartbase_admin/templates/sb_admin/integrations/sorting/change_list.html +0 -401
  187. django_smartbase_admin-0.2.47.dist-info/METADATA +0 -25
  188. {django_smartbase_admin-0.2.47.dist-info → django_smartbase_admin-1.0.38.dist-info}/LICENSE.md +0 -0
@@ -1,12 +1,14 @@
1
+ from __future__ import annotations
1
2
  import json
2
3
  import math
4
+ from typing import Any, TYPE_CHECKING
3
5
 
4
- from django.core.paginator import Paginator
5
6
  from django.db.models import Q
6
7
  from django.utils import timezone
8
+ from django.utils.html import escape
9
+ from django.utils.safestring import SafeString
7
10
  from django.utils.text import smart_split, unescape_string_literal
8
11
 
9
- from django_smartbase_admin.engine.actions import SBAdminAction
10
12
  from django_smartbase_admin.engine.const import (
11
13
  XLSX_PAGE_CHUNK_SIZE,
12
14
  SELECTED_ROWS_KWARG_NAME,
@@ -32,8 +34,8 @@ from django_smartbase_admin.engine.const import (
32
34
  CONFIG_NAME,
33
35
  TABLE_PARAMS_FULL_TEXT_SEARCH,
34
36
  TABLE_PARAMS_SELECTED_FILTER_TYPE,
35
- FilterVersions,
36
37
  ADVANCED_FILTER_DATA_NAME,
38
+ IGNORE_LIST_SELECTION,
37
39
  )
38
40
  from django_smartbase_admin.services.views import SBAdminViewService
39
41
  from django_smartbase_admin.utils import import_with_injection
@@ -42,6 +44,19 @@ QueryBuilderService = import_with_injection(
42
44
  "django_smartbase_admin.actions.advanced_filters", "QueryBuilderService"
43
45
  )
44
46
 
47
+ if TYPE_CHECKING:
48
+ from django_smartbase_admin.engine.field import SBAdminField
49
+
50
+
51
+ class SBAdminAction(object):
52
+ view = None
53
+ threadsafe_request = None
54
+
55
+ def __init__(self, view, request) -> None:
56
+ super().__init__()
57
+ self.view = view
58
+ self.threadsafe_request = request
59
+
45
60
 
46
61
  class SBAdminListAction(SBAdminAction):
47
62
  def __init__(
@@ -58,19 +73,12 @@ class SBAdminListAction(SBAdminAction):
58
73
  self.all_params = json.loads(
59
74
  request.request_data.request_get.get(BASE_PARAMS_NAME, "{}")
60
75
  )
61
- if request.request_data.request_method == "GET":
62
- source_data = request.request_data.request_get.get(
63
- BASE_PARAMS_NAME, "{}"
64
- )
65
- elif request.request_data.request_method == "POST":
66
- if request.headers.get("X-TabulatorRequest", None) == "true":
67
- source_data = request.body
68
- else:
69
- source_data = request.request_data.request_post.get(
70
- BASE_PARAMS_NAME, "{}"
71
- )
72
- else:
73
- source_data = "{}"
76
+ source_data = request.request_data.request_get.get(BASE_PARAMS_NAME, "{}")
77
+ if (
78
+ request.request_data.request_method == "POST"
79
+ and request.headers.get("X-TabulatorRequest", None) == "true"
80
+ ):
81
+ source_data = request.body
74
82
  try:
75
83
  self.all_params = json.loads(source_data)
76
84
  except json.JSONDecodeError:
@@ -90,7 +98,7 @@ class SBAdminListAction(SBAdminAction):
90
98
  )
91
99
  self.deselected_rows = self.selection_data.get(DESELECTED_ROWS_KWARG_NAME, [])
92
100
  self.page_size = page_size or self.table_params.get(
93
- TABLE_PARAMS_SIZE_NAME, self.view.get_list_per_page()
101
+ TABLE_PARAMS_SIZE_NAME, self.view.get_list_per_page(request)
94
102
  )
95
103
  self.init_column_fields()
96
104
  self.tabulator_definition = tabulator_definition
@@ -139,11 +147,6 @@ class SBAdminListAction(SBAdminAction):
139
147
  if field.field in values
140
148
  ]
141
149
 
142
- def is_jquery_required(self, request):
143
- return (
144
- self.view.get_filters_version(request) == FilterVersions.FILTERS_VERSION_2
145
- )
146
-
147
150
  def get_template_data(self):
148
151
  context_data = self.view.get_context_data(self.threadsafe_request)
149
152
  constants = {
@@ -182,7 +185,9 @@ class SBAdminListAction(SBAdminAction):
182
185
  tabulator_definition["tableIdColumnName"] = id_column_name
183
186
  tabulator_definition["constants"] = constants
184
187
 
185
- list_actions = self.list_actions or self.view._get_sbadmin_list_actions()
188
+ list_actions = self.list_actions or self.view._get_sbadmin_list_actions(
189
+ self.threadsafe_request
190
+ )
186
191
 
187
192
  context_data.update(
188
193
  {
@@ -190,7 +195,6 @@ class SBAdminListAction(SBAdminAction):
190
195
  "tabulator_definition": tabulator_definition,
191
196
  "id_column_name": id_column_name,
192
197
  "filters": self.get_filters(),
193
- "is_jquery_required": self.is_jquery_required(self.threadsafe_request),
194
198
  "advanced_filters_data": QueryBuilderService.get_advanced_filters_context_data(
195
199
  self
196
200
  ),
@@ -201,16 +205,18 @@ class SBAdminListAction(SBAdminAction):
201
205
  self.threadsafe_request
202
206
  ),
203
207
  "search_fields": self.view.get_search_fields(self.threadsafe_request),
204
- "search_field_placeholder": self.view.get_search_field_placeholder(),
205
- "list_actions": self.view.process_actions_permissions(
208
+ "search_field_placeholder": self.view.get_search_field_placeholder(
209
+ self.threadsafe_request
210
+ ),
211
+ "list_actions": self.view.process_actions(
206
212
  self.threadsafe_request, list_actions
207
213
  ),
208
214
  "list_selection_actions": self.view.get_sbadmin_list_selection_actions_grouped(
209
215
  self.threadsafe_request
210
216
  ),
211
- "config_url": self.view.get_config_url(),
217
+ "config_url": self.view.get_config_url(self.threadsafe_request),
212
218
  "new_url": (
213
- self.view.get_new_url()
219
+ self.view.get_new_url(self.threadsafe_request)
214
220
  if self.view.has_add_permission(self.threadsafe_request)
215
221
  else None
216
222
  ),
@@ -224,9 +230,23 @@ class SBAdminListAction(SBAdminAction):
224
230
  for sort in self.table_params.get("sort", []):
225
231
  order_by.append(f"{'-' if sort['dir'] == 'desc' else ''}{sort['field']}")
226
232
  if len(order_by) == 0:
227
- order_by = self.view.get_list_ordering() or [self.get_pk_field().name]
233
+ order_by = self.view.get_list_ordering(self.threadsafe_request) or [
234
+ self.get_pk_field().name
235
+ ]
228
236
  return order_by
229
237
 
238
+ def get_order_by_fields_from_request(self):
239
+ order_by = self.get_order_by_from_request()
240
+ order_by_fields = []
241
+ order_by_fields_names = set()
242
+ for field in order_by:
243
+ field_name = field[1:] if field.startswith("-") else field
244
+ order_by_fields_names.add(field_name)
245
+ for field in self.column_fields:
246
+ if field.name in order_by_fields_names:
247
+ order_by_fields.append(field)
248
+ return order_by_fields
249
+
230
250
  def get_filter_from_request(self):
231
251
  base_filters = SBAdminViewService.get_filter_from_request(
232
252
  self.threadsafe_request, self.column_fields, self.filter_data
@@ -253,6 +273,7 @@ class SBAdminListAction(SBAdminAction):
253
273
  filter_fields.extend(
254
274
  QueryBuilderService.get_filters_fields_for_list_action(self)
255
275
  )
276
+ filter_fields.extend(self.get_order_by_fields_from_request())
256
277
  if self.is_search_query():
257
278
  search_fields = self.get_search_fields(self.threadsafe_request)
258
279
  filter_fields.extend(search_fields)
@@ -271,7 +292,7 @@ class SBAdminListAction(SBAdminAction):
271
292
  **self.get_annotates(visible_fields)
272
293
  )
273
294
 
274
- def get_visible_column_fields(self):
295
+ def get_visible_column_fields(self) -> list[SBAdminField]:
275
296
  columns_data_dict = self.columns_data.get(COLUMNS_DATA_COLUMNS_NAME, {})
276
297
  return [
277
298
  field
@@ -288,8 +309,15 @@ class SBAdminListAction(SBAdminAction):
288
309
  values = [self.get_pk_field().name]
289
310
  visible_column_fields = self.get_visible_column_fields()
290
311
  values.extend([field.field for field in visible_column_fields])
312
+ # Include supporting_annotates keys for visible columns
313
+ for field in visible_column_fields:
314
+ if field.supporting_annotates:
315
+ values.extend(field.supporting_annotates.keys())
291
316
  if self.view.sbadmin_list_display_data:
292
317
  values.extend(self.view.sbadmin_list_display_data)
318
+ values.extend(
319
+ [field.field for field in self.get_order_by_fields_from_request()]
320
+ )
293
321
  return values
294
322
 
295
323
  def get_search_results(self, request, queryset, search_term):
@@ -386,32 +414,28 @@ class SBAdminListAction(SBAdminAction):
386
414
  "last_row": total_count,
387
415
  }
388
416
 
389
- def process_final_data(self, final_data):
417
+ def process_final_data(self, final_data: list[dict[str, Any]]) -> None:
390
418
  visible_columns = self.get_visible_column_fields()
391
- fields_with_methods_to_call = [
392
- field
393
- for field in visible_columns
394
- if field.view_method or field.python_formatter
395
- ]
419
+ field_key_field_map: dict[str, SBAdminField] = {
420
+ field.field: field for field in visible_columns
421
+ }
396
422
  for row in final_data:
423
+ obj_id = row.get(self.get_pk_field().name, None)
397
424
  additional_data = {}
398
425
  if self.view.sbadmin_list_display_data:
399
426
  additional_data = {
400
427
  data: row.get(data, None)
401
428
  for data in self.view.sbadmin_list_display_data
402
429
  }
403
- for field in fields_with_methods_to_call:
404
- object_id = row.get(self.get_pk_field().name, None)
405
- value = row.get(field.field, None)
406
- processed_value = value
407
- if field.view_method:
408
- processed_value = field.view_method(
409
- object_id, value, **additional_data
430
+ for field_key, value in row.items():
431
+ if field_key in field_key_field_map:
432
+ field = field_key_field_map[field_key]
433
+ value = self.view.process_field_data(
434
+ self.threadsafe_request, field, obj_id, value, additional_data
410
435
  )
411
- formatted_value = processed_value
412
- if field.python_formatter:
413
- formatted_value = field.python_formatter(object_id, processed_value)
414
- row[field.field] = formatted_value
436
+ if isinstance(value, str) and not isinstance(value, SafeString):
437
+ value = escape(value)
438
+ row[field_key] = value
415
439
 
416
440
  def get_json_data(self):
417
441
  return self.get_data()
@@ -426,6 +450,9 @@ class SBAdminListAction(SBAdminAction):
426
450
  return final_data
427
451
 
428
452
  def get_selection_queryset(self):
453
+ if not self.selection_data:
454
+ # don't run with no selection data as it will result in querying all records
455
+ return Q(id__in=[])
429
456
  additional_filter = None
430
457
  if self.selected_rows and self.selected_rows != SELECT_ALL_KEYWORD:
431
458
  additional_filter = Q(id__in=self.selected_rows)
@@ -434,13 +461,15 @@ class SBAdminListAction(SBAdminAction):
434
461
  additional_filter = ~Q(id__in=self.deselected_rows)
435
462
  return additional_filter
436
463
 
437
- def get_xlsx_data(self):
464
+ def get_xlsx_data(self, request):
438
465
  page_size = XLSX_PAGE_CHUNK_SIZE
439
466
  file_name = (
440
467
  f'{self.view.get_menu_label()}__{timezone.now().strftime("%Y-%m-%d")}.xlsx'
441
468
  )
442
469
  columns = self.get_excel_columns()
443
- additional_filter = self.get_selection_queryset()
470
+ additional_filter = Q()
471
+ if request.request_data.modifier != IGNORE_LIST_SELECTION:
472
+ additional_filter = self.get_selection_queryset()
444
473
  data_list = []
445
474
  report_data = self.get_data(
446
475
  page_size=page_size,
@@ -457,8 +486,8 @@ class SBAdminListAction(SBAdminAction):
457
486
  )["data"]
458
487
  )
459
488
  options = (
460
- self.view.get_sbadmin_xlsx_options().to_json()
461
- if self.view.get_sbadmin_xlsx_options()
489
+ self.view.get_sbadmin_xlsx_options(request).to_json()
490
+ if self.view.get_sbadmin_xlsx_options(request)
462
491
  else {}
463
492
  )
464
493
  return [file_name, data_list, columns, options]
@@ -5,7 +5,7 @@ from dataclasses import field as dataclass_field
5
5
  from typing import List, Optional, TYPE_CHECKING
6
6
 
7
7
  from django.db import models
8
- from django.db.models import Q
8
+ from django.db.models import Q, Count
9
9
  from django.template.loader import render_to_string
10
10
  from django.utils.translation import gettext_lazy as _
11
11
 
@@ -45,8 +45,15 @@ class AllOperators(models.TextChoices):
45
45
  IS_NOT_NULL = "is_not_null", _("Is not null")
46
46
  BEFORE = "before", _("Before")
47
47
  AFTER = "after", _("After")
48
+ IN_THE_LAST = "in_the_last", _("In the last")
49
+ IN_THE_NEXT = "in_the_next", _("In the next")
48
50
 
49
51
 
52
+ NULL_ATTRIBUTES = [
53
+ AllOperators.IS_NULL,
54
+ AllOperators.IS_NOT_NULL,
55
+ ]
56
+
50
57
  DATE_ATTRIBUTES = [
51
58
  AllOperators.BETWEEN,
52
59
  AllOperators.NOT_BETWEEN,
@@ -54,6 +61,8 @@ DATE_ATTRIBUTES = [
54
61
  AllOperators.AFTER,
55
62
  AllOperators.IS_NULL,
56
63
  AllOperators.IS_NOT_NULL,
64
+ AllOperators.IN_THE_LAST,
65
+ AllOperators.IN_THE_NEXT,
57
66
  ]
58
67
 
59
68
  NUMBER_ATTRIBUTES = [
@@ -96,12 +105,16 @@ class QueryBuilderFilter:
96
105
  filter_widget_for_context.input_id = QB_JS_PREFIX
97
106
  filter_widget_for_context.input_name = QB_JS_PREFIX
98
107
  operators = filter_widget.get_advanced_filter_operators()
99
- operators = [operator.value for operator in operators]
108
+ operators_values = []
109
+ for operator in operators:
110
+ if filter_widget.exclude_null_operators and operator in NULL_ATTRIBUTES:
111
+ continue
112
+ operators_values.append(operator.value)
100
113
  return cls(
101
114
  id=filter_widget.input_id,
102
115
  field=filter_widget.field.field,
103
116
  label=filter_widget.field.title,
104
- operators=operators,
117
+ operators=operators_values,
105
118
  input=render_to_string(
106
119
  template_name=filter_widget.template_name.replace(
107
120
  "filter_widgets/", "filter_widgets/advanced_filters/"
@@ -148,6 +161,8 @@ class QueryBuilderService:
148
161
  AllOperators.IS_NOT_NULL.value: "__isnull",
149
162
  AllOperators.BEFORE.value: "__lt",
150
163
  AllOperators.AFTER.value: "__gte",
164
+ AllOperators.IN_THE_LAST.value: "__range",
165
+ AllOperators.IN_THE_NEXT.value: "__range",
151
166
  }
152
167
 
153
168
  ZERO_INPUTS_OPERATORS = {
@@ -236,36 +251,56 @@ class QueryBuilderService:
236
251
  if field is None:
237
252
  continue
238
253
 
239
- value = field.filter_widget.parse_value_from_input(
240
- request, rule["value"]
241
- )
242
- if operator not in cls.LIST_OPERATORS and isinstance(value, list):
243
- value = value[0]
244
-
245
254
  if operator in cls.ZERO_INPUTS_OPERATORS:
255
+ value = None
246
256
  filter_value = (
247
257
  True
248
258
  if operator in [AllOperators.IS_NULL, AllOperators.IS_NOT_NULL]
249
259
  else ""
250
260
  )
261
+ if field.annotate and isinstance(field.annotate, Count):
262
+ filter_value = 0
251
263
  q = Q(
252
264
  **{
253
265
  f"{field.filter_field}{cls.OPERATOR_MAP[operator]}": filter_value,
254
266
  }
255
267
  )
256
- elif operator in [
257
- AllOperators.BETWEEN.value,
258
- AllOperators.NOT_BETWEEN.value,
259
- ]:
260
- q = Q()
261
- if value[0] is not None:
262
- q &= Q(**{f"{field.filter_field}__gte": value[0]})
263
- if value[1] is not None:
264
- q &= Q(**{f"{field.filter_field}__lte": value[1]})
268
+ from django_smartbase_admin.engine.filter_widgets import (
269
+ StringFilterWidget,
270
+ )
271
+
272
+ if isinstance(
273
+ field.filter_widget, StringFilterWidget
274
+ ) and operator in [
275
+ AllOperators.IS_EMPTY,
276
+ AllOperators.IS_NOT_EMPTY,
277
+ ]:
278
+ q = q | Q(
279
+ **{
280
+ f"{field.filter_field}{cls.OPERATOR_MAP[AllOperators.IS_NULL]}": True
281
+ }
282
+ )
265
283
  else:
266
- q = Q(
267
- **{f"{field.filter_field}{cls.OPERATOR_MAP[operator]}": value}
284
+ value = field.filter_widget.parse_value_from_input(
285
+ request, rule["value"]
268
286
  )
287
+ if operator not in cls.LIST_OPERATORS and isinstance(value, list):
288
+ value = value[0]
289
+ if operator in [
290
+ AllOperators.BETWEEN.value,
291
+ AllOperators.NOT_BETWEEN.value,
292
+ ]:
293
+ q = Q()
294
+ if value[0] is not None:
295
+ q &= Q(**{f"{field.filter_field}__gte": value[0]})
296
+ if value[1] is not None:
297
+ q &= Q(**{f"{field.filter_field}__lte": value[1]})
298
+ else:
299
+ q = Q(
300
+ **{
301
+ f"{field.filter_field}{cls.OPERATOR_MAP[operator]}": value
302
+ }
303
+ )
269
304
 
270
305
  if operator in cls.NEGATIVE_OPERATORS:
271
306
  q = ~q