accrete 0.0.103__py3-none-any.whl → 0.0.105__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.
- accrete/contrib/ui/__init__.py +6 -29
- accrete/contrib/ui/context.py +26 -294
- accrete/contrib/ui/filter.py +316 -324
- accrete/contrib/ui/middleware.py +44 -0
- accrete/contrib/ui/models.py +3 -0
- accrete/contrib/ui/static/bulma/README.md +4 -2
- accrete/contrib/ui/static/bulma/bulma.css +21551 -0
- accrete/contrib/ui/static/bulma/bulma.css.map +1 -0
- accrete/contrib/ui/static/bulma/bulma.scss +1 -1
- accrete/contrib/ui/static/bulma/css/bulma.css +1988 -2874
- accrete/contrib/ui/static/bulma/css/bulma.css.map +1 -1
- accrete/contrib/ui/static/bulma/css/bulma.min.css +2 -2
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.css +19648 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.css.map +1 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.min.css +2 -2
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.css +11136 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.css.map +1 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.min.css +2 -2
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.css +11136 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.css.map +1 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.min.css +2 -2
- accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.min.css +21550 -2
- accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.min.css.map +1 -1
- accrete/contrib/ui/static/bulma/css/versions/bulma-prefixed.min.min.css +3 -0
- accrete/contrib/ui/static/bulma/package.json +12 -11
- accrete/contrib/ui/static/bulma/sass/base/animations.css +15 -0
- accrete/contrib/ui/static/bulma/sass/base/animations.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/base/generic.css +196 -0
- accrete/contrib/ui/static/bulma/sass/base/generic.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/base/minireset.css +82 -0
- accrete/contrib/ui/static/bulma/sass/base/minireset.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/base/skeleton.css +113 -0
- accrete/contrib/ui/static/bulma/sass/base/skeleton.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/base/skeleton.scss +0 -12
- accrete/contrib/ui/static/bulma/sass/components/breadcrumb.css +108 -0
- accrete/contrib/ui/static/bulma/sass/components/breadcrumb.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/card.css +130 -0
- accrete/contrib/ui/static/bulma/sass/components/card.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/dropdown.css +119 -0
- accrete/contrib/ui/static/bulma/sass/components/dropdown.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/menu.css +119 -0
- accrete/contrib/ui/static/bulma/sass/components/menu.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/message.css +191 -0
- accrete/contrib/ui/static/bulma/sass/components/message.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/modal.css +194 -0
- accrete/contrib/ui/static/bulma/sass/components/modal.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/navbar.css +768 -0
- accrete/contrib/ui/static/bulma/sass/components/navbar.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/navbar.scss +41 -30
- accrete/contrib/ui/static/bulma/sass/components/pagination.css +302 -0
- accrete/contrib/ui/static/bulma/sass/components/pagination.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/panel.css +224 -0
- accrete/contrib/ui/static/bulma/sass/components/panel.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/components/panel.scss +2 -2
- accrete/contrib/ui/static/bulma/sass/components/tabs.css +192 -0
- accrete/contrib/ui/static/bulma/sass/components/tabs.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/block.css +17 -0
- accrete/contrib/ui/static/bulma/sass/elements/block.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/box.css +43 -0
- accrete/contrib/ui/static/bulma/sass/elements/box.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/button.css +685 -0
- accrete/contrib/ui/static/bulma/sass/elements/button.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/button.scss +9 -2
- accrete/contrib/ui/static/bulma/sass/elements/content.css +208 -0
- accrete/contrib/ui/static/bulma/sass/elements/content.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/content.scss +8 -2
- accrete/contrib/ui/static/bulma/sass/elements/delete.css +60 -0
- accrete/contrib/ui/static/bulma/sass/elements/delete.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/icon.css +51 -0
- accrete/contrib/ui/static/bulma/sass/elements/icon.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/image.css +253 -0
- accrete/contrib/ui/static/bulma/sass/elements/image.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/loader.css +14 -0
- accrete/contrib/ui/static/bulma/sass/elements/loader.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/notification.css +213 -0
- accrete/contrib/ui/static/bulma/sass/elements/notification.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/progress.css +119 -0
- accrete/contrib/ui/static/bulma/sass/elements/progress.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/table.css +294 -0
- accrete/contrib/ui/static/bulma/sass/elements/table.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/tag.css +252 -0
- accrete/contrib/ui/static/bulma/sass/elements/tag.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/elements/title.css +131 -0
- accrete/contrib/ui/static/bulma/sass/elements/title.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.css +72 -0
- accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/checkbox-radio.scss +7 -3
- accrete/contrib/ui/static/bulma/sass/form/file.css +374 -0
- accrete/contrib/ui/static/bulma/sass/form/file.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/input-textarea.css +284 -0
- accrete/contrib/ui/static/bulma/sass/form/input-textarea.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/input-textarea.scss +0 -10
- accrete/contrib/ui/static/bulma/sass/form/select.css +347 -0
- accrete/contrib/ui/static/bulma/sass/form/select.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/select.scss +1 -0
- accrete/contrib/ui/static/bulma/sass/form/shared.css +48 -0
- accrete/contrib/ui/static/bulma/sass/form/shared.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/shared.scss +5 -1
- accrete/contrib/ui/static/bulma/sass/form/tools.css +356 -0
- accrete/contrib/ui/static/bulma/sass/form/tools.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/form/tools.scss +23 -12
- accrete/contrib/ui/static/bulma/sass/grid/columns-v2.css +1635 -0
- accrete/contrib/ui/static/bulma/sass/grid/columns-v2.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/grid/columns.css +1652 -0
- accrete/contrib/ui/static/bulma/sass/grid/columns.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/grid/columns.scss +109 -25
- accrete/contrib/ui/static/bulma/sass/grid/grid.css +3011 -0
- accrete/contrib/ui/static/bulma/sass/grid/grid.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/grid/grid.scss +3 -3
- accrete/contrib/ui/static/bulma/sass/helpers/aspect-ratio.css +61 -0
- accrete/contrib/ui/static/bulma/sass/helpers/aspect-ratio.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/border.css +17 -0
- accrete/contrib/ui/static/bulma/sass/helpers/border.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/color.css +5582 -0
- accrete/contrib/ui/static/bulma/sass/helpers/color.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/color.scss +166 -186
- accrete/contrib/ui/static/bulma/sass/helpers/flexbox.css +217 -0
- accrete/contrib/ui/static/bulma/sass/helpers/flexbox.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/float.css +37 -0
- accrete/contrib/ui/static/bulma/sass/helpers/float.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/gap.css +209 -0
- accrete/contrib/ui/static/bulma/sass/helpers/gap.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/other.css +34 -0
- accrete/contrib/ui/static/bulma/sass/helpers/other.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/overflow.css +65 -0
- accrete/contrib/ui/static/bulma/sass/helpers/overflow.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/position.css +45 -0
- accrete/contrib/ui/static/bulma/sass/helpers/position.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/spacing.css +489 -0
- accrete/contrib/ui/static/bulma/sass/helpers/spacing.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/typography.css +423 -0
- accrete/contrib/ui/static/bulma/sass/helpers/typography.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/helpers/visibility.css +485 -0
- accrete/contrib/ui/static/bulma/sass/helpers/visibility.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/container.scss +16 -8
- accrete/contrib/ui/static/bulma/sass/layout/footer.css +9 -0
- accrete/contrib/ui/static/bulma/sass/layout/footer.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/hero.css +534 -0
- accrete/contrib/ui/static/bulma/sass/layout/hero.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/level.css +102 -0
- accrete/contrib/ui/static/bulma/sass/layout/level.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/media.css +90 -0
- accrete/contrib/ui/static/bulma/sass/layout/media.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/section.css +23 -0
- accrete/contrib/ui/static/bulma/sass/layout/section.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/layout/section.scss +4 -0
- accrete/contrib/ui/static/bulma/sass/themes/dark.css +3 -0
- accrete/contrib/ui/static/bulma/sass/themes/dark.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/themes/light.css +3 -0
- accrete/contrib/ui/static/bulma/sass/themes/light.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/themes/light.scss +1 -0
- accrete/contrib/ui/static/bulma/sass/themes/setup.css +3 -0
- accrete/contrib/ui/static/bulma/sass/themes/setup.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/controls.css +13 -0
- accrete/contrib/ui/static/bulma/sass/utilities/controls.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/css-variables.css +3 -0
- accrete/contrib/ui/static/bulma/sass/utilities/css-variables.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/css-variables.scss +3 -2
- accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.css +3 -0
- accrete/contrib/ui/static/bulma/sass/utilities/derived-variables.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/extends.css +13 -0
- accrete/contrib/ui/static/bulma/sass/utilities/extends.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/functions.scss +2 -2
- accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.css +3 -0
- accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/initial-variables.scss +4 -4
- accrete/contrib/ui/static/bulma/sass/utilities/mixins.css +3 -0
- accrete/contrib/ui/static/bulma/sass/utilities/mixins.css.map +1 -0
- accrete/contrib/ui/static/bulma/sass/utilities/mixins.scss +1 -1
- accrete/contrib/ui/static/bulma/versions/bulma-no-dark-mode.css +19648 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-dark-mode.css.map +1 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-dark-mode.scss +2 -1
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers-prefixed.css +11136 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers-prefixed.css.map +1 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers-prefixed.scss +1 -1
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers.css +11136 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers.css.map +1 -0
- accrete/contrib/ui/static/bulma/versions/bulma-no-helpers.scss +1 -1
- accrete/contrib/ui/static/bulma/versions/bulma-prefixed.css +21551 -0
- accrete/contrib/ui/static/bulma/versions/bulma-prefixed.css.map +1 -0
- accrete/contrib/ui/static/bulma/versions/bulma-prefixed.scss +1 -1
- accrete/contrib/ui/static/css/accrete.css +20757 -19997
- accrete/contrib/ui/static/css/accrete.css.map +1 -1
- accrete/contrib/ui/static/css/accrete.scss +185 -462
- accrete/contrib/ui/static/js/filter.js +97 -679
- accrete/contrib/ui/static/js/htmx.min.js +1 -1
- accrete/contrib/ui/templates/django/forms/widgets/date.html +5 -9
- accrete/contrib/ui/templates/django/forms/widgets/select.html +7 -5
- accrete/contrib/ui/templates/ui/content_right.html +7 -0
- accrete/contrib/ui/templates/ui/filter/filter.html +27 -0
- accrete/contrib/ui/templates/ui/filter/query_input.html +31 -0
- accrete/contrib/ui/templates/ui/filter/query_operator.html +11 -0
- accrete/contrib/ui/templates/ui/filter/query_params.html +106 -0
- accrete/contrib/ui/templates/ui/filter/query_tags.html +26 -0
- accrete/contrib/ui/templates/ui/layout.html +162 -233
- accrete/contrib/ui/templates/ui/list.html +39 -28
- accrete/contrib/ui/templates/ui/list_update.html +3 -0
- accrete/contrib/ui/templates/ui/message.html +13 -0
- accrete/contrib/ui/templates/ui/{partials/modal.html → modal.html} +9 -5
- accrete/contrib/ui/templates/ui/oob.html +3 -0
- accrete/contrib/ui/templates/ui/table.html +67 -71
- accrete/contrib/ui/templates/ui/table_row_update.html +14 -0
- accrete/contrib/ui/templates/ui/widgets/model_search_select.html +2 -2
- accrete/contrib/ui/templates/ui/widgets/model_search_select_multi.html +25 -14
- accrete/contrib/ui/templates/ui/widgets/model_search_select_options.html +1 -1
- accrete/contrib/ui/templatetags/{accrete_ui.py → ui.py} +68 -56
- accrete/contrib/ui/urls.py +3 -1
- accrete/contrib/ui/views.py +33 -3
- accrete/contrib/ui/widgets/__init__.py +1 -0
- accrete/contrib/ui/{forms/widgets.py → widgets/search_select.py} +7 -2
- accrete/contrib/user/templates/user/login.html +71 -23
- accrete/forms.py +0 -2
- accrete/managers.py +4 -4
- accrete/middleware.py +43 -66
- accrete/models.py +7 -1
- accrete/storage.py +4 -1
- accrete/tenant.py +9 -4
- accrete/utils/__init__.py +2 -0
- accrete/utils/models.py +9 -1
- accrete/utils/views.py +36 -20
- accrete/views.py +9 -5
- {accrete-0.0.103.dist-info → accrete-0.0.105.dist-info}/METADATA +2 -2
- accrete-0.0.105.dist-info/RECORD +370 -0
- {accrete-0.0.103.dist-info → accrete-0.0.105.dist-info}/WHEEL +1 -1
- accrete/contrib/ui/components/__init__.py +0 -1
- accrete/contrib/ui/components/search_select.py +0 -18
- accrete/contrib/ui/elements.py +0 -95
- accrete/contrib/ui/forms/__init__.py +0 -0
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-dark-mode.min.css.map +0 -1
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers-prefixed.min.css.map +0 -1
- accrete/contrib/ui/static/bulma/css/versions/bulma-no-helpers.min.css.map +0 -1
- accrete/contrib/ui/static/bulma/sass/grid/columns-v2.scss +0 -957
- accrete/contrib/ui/static/js/ui.js +0 -30
- accrete/contrib/ui/templates/ui/dashboard.html +0 -7
- accrete/contrib/ui/templates/ui/detail.html +0 -19
- accrete/contrib/ui/templates/ui/form.html +0 -21
- accrete/contrib/ui/templates/ui/partials/filter.html +0 -146
- accrete/contrib/ui/templates/ui/partials/form_errors.html +0 -34
- accrete/contrib/ui/templates/ui/partials/header.html +0 -123
- accrete/contrib/ui/templates/ui/partials/modal_form.html +0 -46
- accrete/contrib/ui/templates/ui/partials/onchange_form.html +0 -1
- accrete/contrib/ui/templates/ui/partials/pagination_detail.html +0 -23
- accrete/contrib/ui/templates/ui/partials/pagination_list.html +0 -28
- accrete/contrib/ui/templates/ui/partials/table_field.html +0 -16
- accrete/contrib/ui/templates/ui/partials/table_field_float.html +0 -1
- accrete/contrib/ui/templates/ui/partials/table_field_monetary.html +0 -4
- accrete/contrib/ui/templates/ui/partials/table_field_string.html +0 -1
- accrete/contrib/ui/templates/ui/widgets/model_search_select_multi_selected_options.html +0 -3
- accrete/contrib/ui/templates/ui/widgets/model_search_select_multi_tags.html +0 -6
- accrete-0.0.103.dist-info/RECORD +0 -241
- {accrete-0.0.103.dist-info → accrete-0.0.105.dist-info}/licenses/LICENSE +0 -0
accrete/contrib/ui/filter.py
CHANGED
@@ -1,19 +1,24 @@
|
|
1
|
+
import json
|
1
2
|
import logging
|
2
|
-
|
3
|
-
from
|
4
|
-
|
5
|
-
from django.
|
3
|
+
import datetime
|
4
|
+
from decimal import Decimal
|
5
|
+
|
6
|
+
from django.db.models import QuerySet
|
7
|
+
from django.http.request import QueryDict
|
8
|
+
from django.db.models.fields import Field
|
9
|
+
from django.template.loader import render_to_string
|
6
10
|
from django.utils.translation import gettext_lazy as _
|
7
11
|
from django.utils.safestring import mark_safe
|
8
|
-
from django.
|
12
|
+
from django.apps import apps
|
13
|
+
from django.core import paginator
|
14
|
+
from accrete.utils.models import get_related_model, get_related_field
|
15
|
+
from accrete.utils import page_from_querystring, filter_from_querystring
|
9
16
|
|
10
17
|
_logger = logging.getLogger(__name__)
|
11
18
|
|
12
19
|
|
13
20
|
class Filter:
|
14
21
|
|
15
|
-
query_relation_depth = 4
|
16
|
-
|
17
22
|
LABEL_EXACT = _('Equals')
|
18
23
|
LABEL_EXACT_NOT = _('Equals Not')
|
19
24
|
LABEL_ICONTAINS = _('Contains')
|
@@ -24,337 +29,324 @@ class Filter:
|
|
24
29
|
LABEL_FALSE = _('False')
|
25
30
|
LABEL_SET = _('Is Set')
|
26
31
|
LABEL_NOT_SET = _('Is Not Set')
|
32
|
+
LABEL_AND = _('And')
|
33
|
+
LABEL_OR = _('Or')
|
34
|
+
LABEL_XOR = _('Not Or')
|
35
|
+
|
36
|
+
# EXCLUDE = ['tenant']
|
37
|
+
|
38
|
+
DATE_FORMAT = '%Y-%m-%d'
|
39
|
+
|
40
|
+
TYPES_INTEGER = [
|
41
|
+
'AutoField', 'BigAutoField', 'IntegerField', 'PositiveSmallIntegerField'
|
42
|
+
]
|
43
|
+
TYPES_NUMBER = ['DecimalField', 'FloatField']
|
44
|
+
TYPES_CHAR = ['CharField', 'TextField']
|
45
|
+
TYPES_BOOL = ['BooleanField']
|
46
|
+
TYPES_DATETIME = ['DateTimeField']
|
47
|
+
TYPES_DATE = ['DateField']
|
48
|
+
TYPES_TIME = ['TimeField']
|
27
49
|
|
28
50
|
def __init__(
|
29
|
-
|
30
|
-
default_exclude: list[str] = None, default_filter_term: str = None
|
51
|
+
self, model, query: QueryDict, default_lookup: str = None
|
31
52
|
):
|
32
53
|
self.model = model
|
33
|
-
self.
|
34
|
-
|
35
|
-
|
36
|
-
self.
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
return
|
54
|
+
self.query = query
|
55
|
+
self.model_name = f'{self.model._meta.app_label}.{self.model._meta.model_name}'
|
56
|
+
seen_models = query.get('models', '').split(',')
|
57
|
+
self.seen_models = [
|
58
|
+
model for model in seen_models
|
59
|
+
if model and model != self.model_name
|
60
|
+
] + [self.model_name]
|
61
|
+
self.default_lookup = default_lookup
|
62
|
+
self.exclude = []
|
63
|
+
if hasattr(self.model, 'exclude_from_filter'):
|
64
|
+
self.exclude.extend(self.model.exclude_from_filter())
|
65
|
+
|
66
|
+
def get_page(self, select_related: list = None, prefetch_related: list = None) -> paginator.Page:
|
67
|
+
return page_from_querystring(
|
68
|
+
self.model,
|
69
|
+
self.query,
|
70
|
+
select_related=select_related,
|
71
|
+
prefetch_related=prefetch_related
|
72
|
+
)
|
47
73
|
|
48
|
-
def
|
49
|
-
|
50
|
-
if len(model_path) <= self.query_relation_depth:
|
51
|
-
fields.extend(self.get_relation_fields(model_path, field_path))
|
52
|
-
return sorted(fields, key=lambda x: x['label'].lower())
|
74
|
+
def get_queryset(self) -> QuerySet:
|
75
|
+
return filter_from_querystring(self.model, self.query)
|
53
76
|
|
54
|
-
def
|
55
|
-
filter_exclude = getattr(model_path[-1], 'filter_exclude', [])
|
56
|
-
filter_exclude.extend(self.default_exclude)
|
77
|
+
def query_params(self):
|
57
78
|
fields = filter(
|
58
|
-
lambda x: x.
|
59
|
-
|
79
|
+
lambda x: x.name not in self.exclude,
|
80
|
+
self.model._meta.get_fields()
|
60
81
|
)
|
61
|
-
|
62
|
-
|
63
|
-
occurrences = Counter([
|
64
|
-
f.related_model for f in filter(
|
65
|
-
lambda x: isinstance(
|
66
|
-
x, db_fields.reverse_related.ManyToOneRel
|
67
|
-
), fields_counter
|
68
|
-
)
|
69
|
-
])
|
70
|
-
multi_related_models = set([
|
71
|
-
key for key, val in occurrences.items() if val > 1
|
72
|
-
])
|
82
|
+
params = []
|
83
|
+
path = self.query.get('path', '')
|
73
84
|
for field in fields:
|
74
|
-
|
75
|
-
if field.
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
85
|
+
field_params = self._get_field_params(field)
|
86
|
+
if not field.is_relation or field_params['model_name'] not in self.seen_models:
|
87
|
+
params.append(self._get_field_params(field))
|
88
|
+
has_previous = len(self.seen_models) > 1
|
89
|
+
ctx = dict(
|
90
|
+
params=sorted(params, key=lambda x: x['label'].lower()),
|
91
|
+
model_name=self.model_name,
|
92
|
+
seen_models=','.join(self.seen_models),
|
93
|
+
verbose_model_name=self.model._meta.verbose_name,
|
94
|
+
has_previous=has_previous,
|
95
|
+
)
|
96
|
+
if has_previous:
|
97
|
+
previous_model_name = self.seen_models[-2]
|
98
|
+
previous_path = '__'.join(path.split('__')[:-1])
|
99
|
+
ctx.update(
|
100
|
+
previous_model_name=previous_model_name,
|
101
|
+
previous_seen_models=','.join(
|
102
|
+
self.seen_models[:-1] if has_previous else []
|
103
|
+
),
|
104
|
+
previous_verbose_model_name=apps.get_model(
|
105
|
+
*previous_model_name.split('.')
|
106
|
+
)._meta.verbose_name,
|
107
|
+
previous_path=previous_path
|
108
|
+
)
|
109
|
+
return render_to_string('ui/filter/query_params.html', ctx)
|
110
|
+
|
111
|
+
def query_input(self, lookup: str = None):
|
112
|
+
lookup = lookup or self.default_lookup
|
113
|
+
if not lookup:
|
114
|
+
ctx = {
|
115
|
+
'verbose_lookup': str(_('Select an attribute')),
|
116
|
+
'query_dict': self.query,
|
117
|
+
'model_name': self.model_name
|
118
|
+
}
|
119
|
+
return render_to_string('ui/filter/query_input.html', ctx)
|
120
|
+
prefix = lookup.startswith('~') and '~' or ''
|
121
|
+
lookup = lookup.removeprefix('~')
|
122
|
+
lookup_parts = lookup.split('__')
|
123
|
+
lookup_operator = lookup_parts[-1]
|
124
|
+
rel_path = '__'.join(lookup_parts[:-2])
|
125
|
+
model, names = get_related_model(self.model, rel_path)
|
126
|
+
field = model._meta.get_field(lookup_parts[-2])
|
127
|
+
input_params = self._get_input_params(field, lookup_operator)
|
128
|
+
ctx = {
|
129
|
+
'verbose_lookup': ' > '.join(self._get_query_tag_lhs(prefix + lookup)),
|
130
|
+
'field': field,
|
131
|
+
'lookup': prefix + lookup,
|
132
|
+
'input': input_params,
|
133
|
+
'lookup_operator': lookup_operator,
|
134
|
+
'query_dict': self.query,
|
135
|
+
'model_name': self.model_name
|
136
|
+
}
|
137
|
+
return render_to_string('ui/filter/query_input.html', ctx)
|
138
|
+
|
139
|
+
def query_tags(self, query: list | dict = None, operator: str = None):
|
140
|
+
if not query:
|
141
|
+
query = json.loads(self.query.get('q', '[]'))
|
142
|
+
if isinstance(query, dict):
|
143
|
+
query = [query]
|
144
|
+
operator = operator or '&'
|
145
|
+
html = '<div class="query-group-container">'
|
146
|
+
html += render_to_string(
|
147
|
+
'ui/filter/query_operator.html', {'operator': operator}
|
148
|
+
)
|
149
|
+
html += (
|
150
|
+
'<div class="query-group" '
|
151
|
+
'x-sort="applyQuery();" x-sort:group="query" '
|
152
|
+
'x-sort:config="{preventOnFilter: false}">'
|
153
|
+
)
|
154
|
+
for idx, item in enumerate(query):
|
155
|
+
if isinstance(item, str):
|
81
156
|
continue
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
157
|
+
operator = '&'
|
158
|
+
if idx > 0 and isinstance(query[idx - 1], str):
|
159
|
+
operator = query[idx - 1]
|
160
|
+
if isinstance(item, list):
|
161
|
+
html += self.query_tags(item, operator)
|
162
|
+
if isinstance(item, dict):
|
163
|
+
item_ctx = self._get_query_tag_context(item, operator)
|
164
|
+
html += render_to_string('ui/filter/query_tags.html', item_ctx)
|
165
|
+
html += '</div></div>'
|
166
|
+
return mark_safe(html)
|
167
|
+
|
168
|
+
def _get_input_params(self, field: Field, lookup_operator: str):
|
169
|
+
field_type = field.get_internal_type()
|
170
|
+
params = {
|
171
|
+
'field_type': field_type,
|
172
|
+
'data_type': self._internal_type_to_data_type(field_type, lookup_operator)
|
173
|
+
}
|
174
|
+
if lookup_operator == 'isnull':
|
175
|
+
params.update(
|
176
|
+
input_type='select',
|
177
|
+
choices=[('false', str(_('True'))), ('true', str(_('False')))],
|
178
|
+
data_type='bool'
|
179
|
+
)
|
180
|
+
return params
|
181
|
+
if field_type in self.TYPES_BOOL:
|
182
|
+
params.update(
|
183
|
+
input_type='select',
|
184
|
+
choices=[('true', str(_('True'))), ('false', str(_('False')))],
|
185
|
+
data_type='bool'
|
186
|
+
)
|
187
|
+
return params
|
188
|
+
elif field_type in self.TYPES_INTEGER:
|
189
|
+
if field.choices:
|
190
|
+
params.update(input_type='select', choices=field.choices)
|
191
|
+
else:
|
192
|
+
params.update(input_type='number', step=1)
|
193
|
+
elif field_type in self.TYPES_NUMBER:
|
194
|
+
step = (
|
195
|
+
hasattr(field, 'decimal_places')
|
196
|
+
and self._cast_decimal_places_to_step(field.decimal_places)
|
197
|
+
or 1
|
198
|
+
)
|
199
|
+
params.update(input_type='number', step=step)
|
200
|
+
elif field_type in self.TYPES_CHAR:
|
201
|
+
if field.choices:
|
202
|
+
params.update(input_type='select', choices=field.choices)
|
86
203
|
else:
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
204
|
+
params.update(input_type='text')
|
205
|
+
elif field_type in self.TYPES_DATETIME:
|
206
|
+
params.update(input_type='datetime-local', format=self.DATE_FORMAT)
|
207
|
+
elif field_type in self.TYPES_DATE:
|
208
|
+
params.update(input_type='date', format=self.DATE_FORMAT)
|
209
|
+
elif field_type in self.TYPES_TIME:
|
210
|
+
params.update(input_type='time')
|
211
|
+
return params
|
212
|
+
|
213
|
+
def _internal_type_to_data_type(self, internal_type, lookup: str):
|
214
|
+
if lookup == 'isnull' or internal_type in self.TYPES_BOOL:
|
215
|
+
return 'bool'
|
216
|
+
if internal_type in self.TYPES_INTEGER + self.TYPES_NUMBER:
|
217
|
+
return 'number'
|
218
|
+
return 'text'
|
219
|
+
|
220
|
+
def _get_query_tag_context(self, data: dict, operator: str):
|
221
|
+
ctx = {'tags': []}
|
222
|
+
first_operator = operator
|
223
|
+
operator = '&'
|
224
|
+
for key, value in data.items():
|
225
|
+
|
226
|
+
field_path = key.split('__')
|
227
|
+
field = get_related_field(
|
228
|
+
self.model, '__'.join(field_path[:-1]).removeprefix('~')
|
229
|
+
)
|
230
|
+
lookup = field_path[-1]
|
231
|
+
internal_type = field.get_internal_type()
|
232
|
+
data_type = self._internal_type_to_data_type(internal_type, lookup)
|
233
|
+
ctx['tags'].append({
|
234
|
+
'lhs': self._get_query_tag_lhs(key),
|
235
|
+
'rhs': self._get_query_tag_rhs(value, internal_type, lookup),
|
236
|
+
'lookup': key,
|
237
|
+
'value': value,
|
238
|
+
'operator': operator,
|
239
|
+
'data_type': data_type,
|
240
|
+
'model_name': self.model_name
|
95
241
|
})
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
242
|
+
if ctx['tags']:
|
243
|
+
ctx['tags'][0]['operator'] = first_operator
|
244
|
+
return ctx
|
245
|
+
|
246
|
+
def _get_query_tag_lhs(self, lookup: str):
|
247
|
+
prefix = lookup.startswith('~') and '~' or ''
|
248
|
+
lookup = lookup.removeprefix('~')
|
249
|
+
parts = lookup.split('__')
|
250
|
+
assert len(parts) >= 2
|
251
|
+
related_parts = parts[:-2]
|
252
|
+
rel_model, names = get_related_model(
|
253
|
+
self.model, '__'.join(related_parts)
|
104
254
|
)
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
'
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
if
|
121
|
-
return
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
if html:
|
139
|
-
return html
|
140
|
-
if not self.fields:
|
141
|
-
self.fields = self.get_fields([self.model], '')
|
142
|
-
html = ''
|
143
|
-
for f in self.fields:
|
144
|
-
html += self.field_params(f)
|
145
|
-
html = {
|
146
|
-
'params': mark_safe(html.strip().replace('\n', '')),
|
147
|
-
'field_paths': mark_safe(
|
148
|
-
self.field_path_selection().strip().replace('\n', '')
|
149
|
-
)
|
255
|
+
try:
|
256
|
+
names.extend([
|
257
|
+
str(rel_model._meta.get_field(parts[-2]).verbose_name),
|
258
|
+
str(self._get_lookup_label(f'{prefix}{parts[-1]}'))
|
259
|
+
])
|
260
|
+
except AttributeError:
|
261
|
+
names.extend([
|
262
|
+
str(rel_model._meta.get_field(parts[-2]).field.verbose_name),
|
263
|
+
str(self._get_lookup_label(f'{prefix}{parts[-1]}'))
|
264
|
+
])
|
265
|
+
return names[1:]
|
266
|
+
|
267
|
+
def _get_query_tag_rhs(self, value, internal_type, lookup):
|
268
|
+
if lookup == 'isnull':
|
269
|
+
return not value
|
270
|
+
if internal_type in self.TYPES_DATE:
|
271
|
+
return datetime.date.fromisoformat(value)
|
272
|
+
if internal_type in self.TYPES_DATETIME:
|
273
|
+
return datetime.datetime.fromisoformat(value)
|
274
|
+
if internal_type in self.TYPES_NUMBER:
|
275
|
+
return Decimal(value)
|
276
|
+
return value
|
277
|
+
|
278
|
+
def _get_lookup_label(self, lookup: str):
|
279
|
+
labels = {
|
280
|
+
'exact': self.LABEL_EXACT,
|
281
|
+
'~exact': self.LABEL_EXACT_NOT,
|
282
|
+
'icontains': self.LABEL_ICONTAINS,
|
283
|
+
'~icontains': self.LABEL_ICONTAINS_NOT,
|
284
|
+
'gte': self.LABEL_GTE,
|
285
|
+
'lte': self.LABEL_LTE,
|
286
|
+
'isnull': self.LABEL_SET,
|
287
|
+
'~isnull': self.LABEL_NOT_SET
|
150
288
|
}
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
289
|
+
return str(labels[lookup])
|
290
|
+
|
291
|
+
def _get_field_params(self, field):
|
292
|
+
path = self.query.get('path', '')
|
293
|
+
label = ''
|
294
|
+
name = field.name
|
295
|
+
if path:
|
296
|
+
field_path = f'{path}__{name}'
|
297
|
+
else:
|
298
|
+
field_path = name
|
299
|
+
field_type = field.get_internal_type()
|
300
|
+
if field.concrete or not field.is_relation:
|
301
|
+
label = str(field.verbose_name)
|
302
|
+
if field.is_relation:
|
303
|
+
choices = None
|
304
|
+
step = None
|
305
|
+
model = ''
|
306
|
+
if field_type in ['ForeignKey', 'OneToOneField']:
|
307
|
+
model = (
|
308
|
+
f'{field.related_model._meta.app_label}.'
|
309
|
+
f'{field.related_model._meta.model_name}'
|
310
|
+
)
|
311
|
+
if not field.concrete:
|
312
|
+
label = str(
|
313
|
+
field.related_model._meta.verbose_name_plural
|
314
|
+
)
|
315
|
+
elif field_type == 'ManyToManyField':
|
316
|
+
model = (
|
317
|
+
f'{field.remote_field.model._meta.app_label}.'
|
318
|
+
f'{field.remote_field.model._meta.model_name}'
|
319
|
+
)
|
320
|
+
if not field.concrete:
|
321
|
+
label = (
|
322
|
+
f'{field.remote_field.model._meta.verbose_name} / '
|
323
|
+
f'{field.remote_field.verbose_name}'
|
324
|
+
)
|
325
|
+
else:
|
326
|
+
step = (
|
327
|
+
hasattr(field, 'decimal_places')
|
328
|
+
and self._cast_decimal_places_to_step(field.decimal_places)
|
329
|
+
or 1
|
330
|
+
)
|
331
|
+
choices = field.choices or None
|
332
|
+
model = self.model_name
|
171
333
|
return {
|
172
|
-
'
|
173
|
-
'
|
174
|
-
'
|
175
|
-
'
|
176
|
-
'
|
177
|
-
'
|
178
|
-
'
|
179
|
-
'
|
180
|
-
'
|
181
|
-
'
|
182
|
-
'
|
183
|
-
'TimeField': self.time_param,
|
184
|
-
'ForeignKey': self.foreign_key_param,
|
185
|
-
'ManyToManyField': self.many_to_many_param,
|
186
|
-
'FileField': self.file_param,
|
187
|
-
'ImageField': self.file_param
|
334
|
+
'field': field,
|
335
|
+
'name': name,
|
336
|
+
'field_path': field_path,
|
337
|
+
'label': label,
|
338
|
+
'type': field_type,
|
339
|
+
'param_type': '',
|
340
|
+
'choices': choices,
|
341
|
+
'null': field.null,
|
342
|
+
'step': step,
|
343
|
+
'is_relation': field.is_relation,
|
344
|
+
'model_name': model,
|
188
345
|
}
|
189
346
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
def params(self, field):
|
197
|
-
return self.field_map().get(field['type'], self.no_param)(field['name'], field)
|
198
|
-
|
199
|
-
def param(
|
200
|
-
self, key: str, value: dict, param: str, data_type: str,
|
201
|
-
options: str, invert: bool = True
|
202
|
-
):
|
203
|
-
|
204
|
-
def get_label(inverted=False):
|
205
|
-
if param == 'exact':
|
206
|
-
return self.LABEL_EXACT_NOT if inverted else self.LABEL_EXACT
|
207
|
-
if param == 'icontains':
|
208
|
-
return self.LABEL_ICONTAINS_NOT if inverted else self.LABEL_ICONTAINS
|
209
|
-
if param == 'gte':
|
210
|
-
return self.LABEL_GTE
|
211
|
-
if param == 'lte':
|
212
|
-
return self.LABEL_LTE
|
213
|
-
|
214
|
-
def param_div(inverted=False):
|
215
|
-
return f"""
|
216
|
-
<div id="filter-id-{'~' if inverted else ''}{key}__{param}"
|
217
|
-
class="query-param" tabindex="-1" data-type="{data_type}"
|
218
|
-
data-step="{value.get('step', 1)}"
|
219
|
-
data-param="{param}" data-param-label="{value.get("label")}"
|
220
|
-
>
|
221
|
-
<p class="px-1 arrowless">{get_label(inverted)}</p>
|
222
|
-
<div class="param-options is-hidden">
|
223
|
-
{options if options else ''}
|
224
|
-
</div>
|
225
|
-
</div>
|
226
|
-
"""
|
227
|
-
|
228
|
-
html = param_div()
|
229
|
-
if invert:
|
230
|
-
html += param_div(inverted=True)
|
231
|
-
return html
|
232
|
-
|
233
|
-
def char_param(self, key, value):
|
234
|
-
if value.get('choices'):
|
235
|
-
return self.char_choice_param(key, value)
|
236
|
-
options = self.parse_choices(value.get('choices', ''))
|
237
|
-
html = self.param(key, value, 'icontains', 'text', options)
|
238
|
-
html += self.param(key, value, 'exact', 'text', options)
|
239
|
-
if value.get('null'):
|
240
|
-
html += self.null_param(key, value)
|
241
|
-
return html
|
242
|
-
|
243
|
-
def char_choice_param(self, key, value):
|
244
|
-
options = self.parse_choices(value.get('choices', []))
|
245
|
-
html = self.param(key, value, 'exact', 'selection', options)
|
246
|
-
if value.get('null'):
|
247
|
-
html += self.null_param(key, value)
|
248
|
-
return html
|
249
|
-
|
250
|
-
def float_param(self, key, value):
|
251
|
-
options = self.parse_choices(value.get('choices', ''))
|
252
|
-
html = self.param(key, value, 'exact', 'number', options)
|
253
|
-
html += self.param(key, value, 'gte', 'number', options, False)
|
254
|
-
html += self.param(key, value, 'lte', 'number', options, False)
|
255
|
-
if value.get('null'):
|
256
|
-
html += self.null_param(key, value)
|
257
|
-
return html
|
258
|
-
|
259
|
-
def bool_param(self, key, value):
|
260
|
-
options = self.parse_choices([('true', _('True')), ('false', _('False'))])
|
261
|
-
html = self.param(key, value, 'exact', 'selection', options)
|
262
|
-
if value.get('null'):
|
263
|
-
html += self.null_param(key, value)
|
264
|
-
return html
|
265
|
-
|
266
|
-
def int_param(self, key, value):
|
267
|
-
options = self.parse_choices(value.get('choices', ''))
|
268
|
-
html = self.param(key, value, 'exact', 'number', options)
|
269
|
-
html += self.param(key, value, 'gte', 'number', options, False)
|
270
|
-
html += self.param(key, value, 'lte', 'number', options, False)
|
271
|
-
if value.get('null'):
|
272
|
-
html += self.null_param(key, value)
|
273
|
-
return html
|
274
|
-
|
275
|
-
def date_time_param(self, key, value):
|
276
|
-
options = self.parse_choices(value.get('choices', ''))
|
277
|
-
html = self.param(key, value, 'exact', 'datetime-local', options)
|
278
|
-
html += self.param(key, value, 'gte', 'datetime-local', options, False)
|
279
|
-
html += self.param(key, value, 'lte', 'datetime-local', options, False)
|
280
|
-
if value.get('null'):
|
281
|
-
html += self.null_param(key, value)
|
282
|
-
return html
|
283
|
-
|
284
|
-
def date_param(self, key, value):
|
285
|
-
options = self.parse_choices(value.get('choices', ''))
|
286
|
-
html = self.param(key, value, 'exact', 'date', options)
|
287
|
-
html += self.param(key, value, 'gte', 'date', options, False)
|
288
|
-
html += self.param(key, value, 'lte', 'date', options, False)
|
289
|
-
if value.get('null'):
|
290
|
-
html += self.null_param(key, value)
|
291
|
-
return html
|
292
|
-
|
293
|
-
def time_param(self, key, value):
|
294
|
-
options = self.parse_choices(value.get('choices', ''))
|
295
|
-
html = self.param(key, value, 'exact', 'time', options)
|
296
|
-
html += self.param(key, value, 'gte', 'time', options, False)
|
297
|
-
html += self.param(key, value, 'lte', 'time', options, False)
|
298
|
-
if value.get('null'):
|
299
|
-
html += self.null_param(key, value)
|
300
|
-
return html
|
301
|
-
|
302
|
-
def foreign_key_param(self, key, value):
|
303
|
-
if value.get('null'):
|
304
|
-
return self.null_param(key, value)
|
305
|
-
return ''
|
306
|
-
|
307
|
-
def many_to_many_param(self, key, value):
|
308
|
-
return self.null_param(key, value)
|
309
|
-
|
310
|
-
def file_param(self, key, value):
|
311
|
-
return self.null_param(key, value)
|
312
|
-
|
313
|
-
def null_param(self, key, value):
|
314
|
-
options = self.parse_choices([
|
315
|
-
('false', _('True')),
|
316
|
-
('true', _('False'))
|
317
|
-
])
|
318
|
-
return f"""
|
319
|
-
<div id="filter-id-{key}__isnull"
|
320
|
-
class="query-param" tabindex="-1" data-type="selection"
|
321
|
-
data-param-invert="false"
|
322
|
-
data-param="isnull" data-param-label="{value.get("label")}"
|
323
|
-
>
|
324
|
-
<p class="px-1 arrowless">{self.LABEL_SET}</p>
|
325
|
-
<div class="param-options is-hidden">
|
326
|
-
{options}
|
327
|
-
</div>
|
328
|
-
</div>
|
329
|
-
"""
|
330
|
-
|
331
|
-
def no_param(self, key, value):
|
332
|
-
return ''
|
333
|
-
|
334
|
-
def field_path_selection(self):
|
335
|
-
html = ''
|
336
|
-
filter_exclude = getattr(self.model, 'filter_exclude', [])
|
337
|
-
filter_exclude.extend(self.default_exclude)
|
338
|
-
fields = [(x.verbose_name, x.name) for x in filter(
|
339
|
-
lambda x:
|
340
|
-
x.name not in filter_exclude
|
341
|
-
and not isinstance(x, (
|
342
|
-
db_fields.related.ManyToOneRel,
|
343
|
-
db_fields.related.ManyToManyRel
|
344
|
-
)),
|
345
|
-
self.model._meta.get_fields()
|
346
|
-
)]
|
347
|
-
if hasattr(self.model, 'get_annotations'):
|
348
|
-
fields.extend([
|
349
|
-
(x['annotation'].verbose_name, x['name'])
|
350
|
-
for x in self.model.get_annotations()
|
351
|
-
])
|
352
|
-
sorted_fields = sorted(fields, key=lambda x: x[0].lower())
|
353
|
-
for field in sorted_fields:
|
354
|
-
html += f"""
|
355
|
-
<label class="checkbox is-unselectable my-1" style="width: 100%">
|
356
|
-
<input type="checkbox" name="{field[1]}" data-label="{field[0].lower()}">
|
357
|
-
<span>{field[0]}</span>
|
358
|
-
</label>
|
359
|
-
"""
|
360
|
-
return html
|
347
|
+
@staticmethod
|
348
|
+
def _cast_decimal_places_to_step(decimal_places):
|
349
|
+
if not decimal_places or decimal_places < 1:
|
350
|
+
return '1'
|
351
|
+
zero_count = decimal_places - 1
|
352
|
+
return f'0.{"0" * zero_count}1'
|