dynamicforms 0.76.14__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.
- dynamicforms/__init__.py +8 -0
- dynamicforms/action.py +521 -0
- dynamicforms/apps.py +5 -0
- dynamicforms/fields.py +1570 -0
- dynamicforms/filters.py +44 -0
- dynamicforms/locale/en/LC_MESSAGES/django.po +67 -0
- dynamicforms/locale/en/LC_MESSAGES/djangojs.po +31 -0
- dynamicforms/locale/sl/LC_MESSAGES/django.po +71 -0
- dynamicforms/locale/sl/LC_MESSAGES/djangojs.po +36 -0
- dynamicforms/management/__init__.py +0 -0
- dynamicforms/management/commands/__init__.py +0 -0
- dynamicforms/management/commands/clean_dfpreuploaded_files.py +26 -0
- dynamicforms/migrations/__init__.py +0 -0
- dynamicforms/mixins/__init__.py +27 -0
- dynamicforms/mixins/action.py +14 -0
- dynamicforms/mixins/choice/__init__.py +94 -0
- dynamicforms/mixins/choice/allow_tags.py +79 -0
- dynamicforms/mixins/choice/null_choice.py +26 -0
- dynamicforms/mixins/choice/single_choice.py +21 -0
- dynamicforms/mixins/color_field.py +4 -0
- dynamicforms/mixins/conditional_visibility.py +193 -0
- dynamicforms/mixins/enable_copy.py +4 -0
- dynamicforms/mixins/field_render.py +209 -0
- dynamicforms/mixins/file_field.py +5 -0
- dynamicforms/mixins/help_text.py +46 -0
- dynamicforms/mixins/implicit_hidden.py +7 -0
- dynamicforms/mixins/implicit_natural_date.py +88 -0
- dynamicforms/mixins/null_value.py +15 -0
- dynamicforms/mixins/password_field.py +23 -0
- dynamicforms/mixins/related_field_ajax.py +142 -0
- dynamicforms/mixins/rtf_field.py +58 -0
- dynamicforms/models.py +5 -0
- dynamicforms/models_utils.py +33 -0
- dynamicforms/pagination.py +175 -0
- dynamicforms/preupload_files.py +42 -0
- dynamicforms/progress.py +71 -0
- dynamicforms/renderers.py +1 -0
- dynamicforms/routers.py +36 -0
- dynamicforms/serializers.py +486 -0
- dynamicforms/struct.py +40 -0
- dynamicforms/template_render/__init__.py +10 -0
- dynamicforms/template_render/layout.py +178 -0
- dynamicforms/template_render/mixins/__init__.py +0 -0
- dynamicforms/template_render/mixins/base.py +39 -0
- dynamicforms/template_render/mixins/field.py +74 -0
- dynamicforms/template_render/mixins/listserializer.py +100 -0
- dynamicforms/template_render/mixins/render_mode_enum.py +8 -0
- dynamicforms/template_render/mixins/serializer.py +301 -0
- dynamicforms/template_render/mixins/serializer_filter.py +19 -0
- dynamicforms/template_render/mixins/serializer_render_actions.py +21 -0
- dynamicforms/template_render/mixins/serializer_render_fields.py +57 -0
- dynamicforms/template_render/mixins/util.py +9 -0
- dynamicforms/template_render/renderer.py +38 -0
- dynamicforms/template_render/responsive_table_layout.py +64 -0
- dynamicforms/urls.py +9 -0
- dynamicforms/utils.py +32 -0
- dynamicforms/views.py +1 -0
- dynamicforms/viewsets.py +421 -0
- dynamicforms-0.76.14.dist-info/METADATA +129 -0
- dynamicforms-0.76.14.dist-info/RECORD +287 -0
- dynamicforms-0.76.14.dist-info/WHEEL +4 -0
- dynamicforms-0.76.14.dist-info/licenses/LICENSE +21 -0
- dynamicforms_legacy/__init__.py +8 -0
- dynamicforms_legacy/action.py +561 -0
- dynamicforms_legacy/apps.py +5 -0
- dynamicforms_legacy/context_processors.py +27 -0
- dynamicforms_legacy/dynamicforms_migration_mixin.py +42 -0
- dynamicforms_legacy/fields.py +1108 -0
- dynamicforms_legacy/filters.py +44 -0
- dynamicforms_legacy/migrations/__init__.py +0 -0
- dynamicforms_legacy/mixins/__init__.py +16 -0
- dynamicforms_legacy/mixins/action.py +14 -0
- dynamicforms_legacy/mixins/allow_tags.py +85 -0
- dynamicforms_legacy/mixins/enable_copy.py +4 -0
- dynamicforms_legacy/mixins/file_field.py +5 -0
- dynamicforms_legacy/mixins/help_text.py +38 -0
- dynamicforms_legacy/mixins/implicit_hidden.py +7 -0
- dynamicforms_legacy/mixins/implicit_natural_date.py +86 -0
- dynamicforms_legacy/mixins/null_choice.py +26 -0
- dynamicforms_legacy/mixins/null_value.py +15 -0
- dynamicforms_legacy/mixins/password_field.py +19 -0
- dynamicforms_legacy/mixins/related_field_ajax.py +58 -0
- dynamicforms_legacy/mixins/render.py +203 -0
- dynamicforms_legacy/mixins/rtf_field.py +52 -0
- dynamicforms_legacy/mixins/single_choice.py +21 -0
- dynamicforms_legacy/models.py +5 -0
- dynamicforms_legacy/pagination.py +168 -0
- dynamicforms_legacy/progress.py +71 -0
- dynamicforms_legacy/renderers.py +183 -0
- dynamicforms_legacy/serializers.py +335 -0
- dynamicforms_legacy/settings.py +196 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap-theme.css +587 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css +6 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap.css +6834 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap.min.css +6 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.eot +0 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.svg +288 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.ttf +0 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff +0 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/js/bootstrap.js +2580 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/js/bootstrap.min.js +6 -0
- dynamicforms_legacy/static/bootstrap-3.4.1-dist/js/npm.js +13 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-grid.css +3719 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-grid.css.map +1 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-grid.min.css +7 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-grid.min.css.map +1 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-reboot.css +331 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-reboot.css.map +1 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-reboot.min.css +8 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-reboot.min.css.map +1 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap.css +10038 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap.css.map +1 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap.min.css +7 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap.min.css.map +1 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.bundle.js +7013 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.bundle.js.map +1 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.bundle.min.js +7 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.bundle.min.js.map +1 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.js +4435 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.js.map +1 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.min.js +7 -0
- dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.min.js.map +1 -0
- dynamicforms_legacy/static/ckeditor-df/ckEditorCustomBuildReadMe.txt +255 -0
- dynamicforms_legacy/static/ckeditor-df/ckeditor.js +6 -0
- dynamicforms_legacy/static/dynamicforms/dynamicforms.js +1883 -0
- dynamicforms_legacy/static/dynamicforms/icons/copy.svg +7 -0
- dynamicforms_legacy/static/dynamicforms/icons/eye-off-outline.svg +10 -0
- dynamicforms_legacy/static/dynamicforms/icons/eye-outline.svg +7 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-icons_222222_256x240.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-icons_228ef1_256x240.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-icons_ef8c08_256x240.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-icons_ffd27a_256x240.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/images/ui-icons_ffffff_256x240.png +0 -0
- dynamicforms_legacy/static/jquery_ui/css/jquery-ui.min.css +7 -0
- dynamicforms_legacy/static/jquery_ui/css/jquery-ui.structure.min.css +5 -0
- dynamicforms_legacy/static/jquery_ui/css/jquery-ui.theme.min.css +5 -0
- dynamicforms_legacy/static/jquery_ui/js/jquery-3.3.1.min.js +2 -0
- dynamicforms_legacy/static/jquery_ui/js/jquery-ui.min.js +13 -0
- dynamicforms_legacy/static/select2/css/select2-bootstrap3.min.css +7 -0
- dynamicforms_legacy/static/select2/css/select2-bootstrap4.min.css +578 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/css/select2.min.css +1 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/af.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ar.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/az.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/bg.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/bs.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ca.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/cs.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/da.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/de.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/dsb.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/el.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/en.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/es.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/et.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/eu.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/fa.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/fi.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/fr.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/gl.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/he.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/hi.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/hr.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/hsb.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/hu.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/hy.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/id.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/is.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/it.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ja.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/km.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ko.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/lt.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/lv.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/mk.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ms.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/nb.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/nl.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/pl.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ps.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/pt-BR.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/pt.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ro.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ru.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/sk.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/sl.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/sr-Cyrl.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/sr.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/sv.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/th.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/tr.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/uk.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/vi.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/zh-CN.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/zh-TW.js +3 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/select2.full.js +6457 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/select2.full.min.js +1 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/select2.js +5746 -0
- dynamicforms_legacy/static/select2/select2-4.0.5/js/select2.min.js +1 -0
- dynamicforms_legacy/struct.py +40 -0
- dynamicforms_legacy/templates/dynamicforms/base.html +12 -0
- dynamicforms_legacy/templates/dynamicforms/base_form.html +44 -0
- dynamicforms_legacy/templates/dynamicforms/base_includes.html +51 -0
- dynamicforms_legacy/templates/dynamicforms/base_includes_select2.html +6 -0
- dynamicforms_legacy/templates/dynamicforms/base_list.html +47 -0
- dynamicforms_legacy/templates/dynamicforms/base_table_body.html +46 -0
- dynamicforms_legacy/templates/dynamicforms/base_table_filter.html +44 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/base_form.html +10 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/base_includes.html +46 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/base_includes_v3.html +90 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/base_includes_v4.html +77 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/base_list.html +9 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/base_table_body.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/base_table_filter.html +5 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/base_field.html +41 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/base_field_v3.html +11 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/base_field_v4.html +11 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/checkbox.html +7 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/checkbox_multiple.html +7 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/fieldset.html +21 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/form.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/input.html +31 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/list_fieldset.html +18 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/radio.html +8 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/rtf_field.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/select.html +46 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/select_multiple.html +17 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/field/textarea.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/modal_dialog.html +13 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/modal_dialog_rest.html +23 -0
- dynamicforms_legacy/templates/dynamicforms/bootstrap/page.html +11 -0
- dynamicforms_legacy/templates/dynamicforms/field/base_field.html +55 -0
- dynamicforms_legacy/templates/dynamicforms/field/checkbox.html +28 -0
- dynamicforms_legacy/templates/dynamicforms/field/checkbox_multiple.html +16 -0
- dynamicforms_legacy/templates/dynamicforms/field/dict_field.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/field/fieldset.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/field/form.html +18 -0
- dynamicforms_legacy/templates/dynamicforms/field/input.html +31 -0
- dynamicforms_legacy/templates/dynamicforms/field/list_field.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/field/list_fieldset.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/field/radio.html +24 -0
- dynamicforms_legacy/templates/dynamicforms/field/rtf_field.html +14 -0
- dynamicforms_legacy/templates/dynamicforms/field/select.html +97 -0
- dynamicforms_legacy/templates/dynamicforms/field/select_multiple.html +67 -0
- dynamicforms_legacy/templates/dynamicforms/field/textarea.html +13 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/base_form.html +13 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/base_includes.html +225 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/base_list.html +9 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/base_table_body.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/base_table_filter.html +5 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/base_field.html +16 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/checkbox.html +7 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/checkbox_multiple.html +9 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/fieldset.html +11 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/form.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/input.html +17 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/list_fieldset.html +6 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/radio.html +8 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/rtf_field.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/select.html +37 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/select_multiple.html +18 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/textarea.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/modal_dialog.html +2 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/modal_dialog_rest.html +27 -0
- dynamicforms_legacy/templates/dynamicforms/jquery_ui/page.html +20 -0
- dynamicforms_legacy/templates/dynamicforms/modal_dialog.html +4 -0
- dynamicforms_legacy/templates/dynamicforms/modal_dialog_rest.html +18 -0
- dynamicforms_legacy/templates/dynamicforms/page.html +25 -0
- dynamicforms_legacy/templates/dynamicforms/rtf_field_style.html +41 -0
- dynamicforms_legacy/templatetags/__init__.py +0 -0
- dynamicforms_legacy/templatetags/dynamicforms.py +475 -0
- dynamicforms_legacy/urls.py +8 -0
- dynamicforms_legacy/views.py +1 -0
- dynamicforms_legacy/viewsets.py +366 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import IntEnum
|
|
4
|
+
from typing import Callable, Iterable, NamedTuple, Union
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ConditionalVisibilityMixin(object):
|
|
8
|
+
def __init__(self, *args, conditional_visibility: Statement = None, **kwargs):
|
|
9
|
+
super().__init__(*args, **kwargs)
|
|
10
|
+
self.conditional_visibility = conditional_visibility
|
|
11
|
+
|
|
12
|
+
def as_component_def(self) -> dict:
|
|
13
|
+
try:
|
|
14
|
+
res = super().as_component_def() # noqa
|
|
15
|
+
except AttributeError:
|
|
16
|
+
res = dict()
|
|
17
|
+
res.update(dict(conditional_visibility=self.conditional_visibility.to_value()))
|
|
18
|
+
return res
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Operators(IntEnum):
|
|
22
|
+
# Logic Operators
|
|
23
|
+
NOT = 0
|
|
24
|
+
OR = 1
|
|
25
|
+
AND = 2
|
|
26
|
+
XOR = 3
|
|
27
|
+
NAND = 4
|
|
28
|
+
NOR = 5
|
|
29
|
+
|
|
30
|
+
# Comparators
|
|
31
|
+
EQUALS = -1
|
|
32
|
+
NOT_EQUALS = -2
|
|
33
|
+
GT = -3
|
|
34
|
+
LT = -4
|
|
35
|
+
GE = -5
|
|
36
|
+
LE = -6
|
|
37
|
+
IN = -7
|
|
38
|
+
NOT_IN = -8
|
|
39
|
+
INCLUDES = -9
|
|
40
|
+
NOT_INCLUDES = -10
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
LogicOperators = {
|
|
44
|
+
Operators.OR,
|
|
45
|
+
Operators.AND,
|
|
46
|
+
Operators.XOR,
|
|
47
|
+
Operators.NAND,
|
|
48
|
+
Operators.NOR,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Comparators = {
|
|
52
|
+
Operators.EQUALS,
|
|
53
|
+
Operators.NOT_EQUALS,
|
|
54
|
+
Operators.GT,
|
|
55
|
+
Operators.LT,
|
|
56
|
+
Operators.GE,
|
|
57
|
+
Operators.LE,
|
|
58
|
+
Operators.IN,
|
|
59
|
+
Operators.NOT_IN,
|
|
60
|
+
Operators.INCLUDES,
|
|
61
|
+
Operators.NOT_INCLUDES,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Types
|
|
66
|
+
FieldType = str
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class StatementType(NamedTuple):
|
|
70
|
+
statement_a: Union[FieldType, StatementType]
|
|
71
|
+
operator: Operators
|
|
72
|
+
statement_b: Union[any, StatementType]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class Field(object):
|
|
76
|
+
"""
|
|
77
|
+
Abstraction of field name. Object is used to make statements inline with comparison operators such as
|
|
78
|
+
equals,
|
|
79
|
+
|
|
80
|
+
Arguments:
|
|
81
|
+
:field_name: Name of a field to use comparison function on
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(self, field_name: str):
|
|
85
|
+
self.field_name = field_name
|
|
86
|
+
|
|
87
|
+
def to_value(self) -> FieldType:
|
|
88
|
+
return self.field_name
|
|
89
|
+
|
|
90
|
+
def __eq__(self, other: any) -> Statement:
|
|
91
|
+
return Statement(self.field_name, Operators.EQUALS, other)
|
|
92
|
+
|
|
93
|
+
def __gt__(self, other: any) -> Statement:
|
|
94
|
+
return Statement(self.field_name, Operators.GT, other)
|
|
95
|
+
|
|
96
|
+
def __ge__(self, other: any) -> Statement:
|
|
97
|
+
return Statement(self.field_name, Operators.GE, other)
|
|
98
|
+
|
|
99
|
+
def __le__(self, other: any) -> Statement:
|
|
100
|
+
return Statement(self.field_name, Operators.LE, other)
|
|
101
|
+
|
|
102
|
+
def __lt__(self, other: any) -> Statement:
|
|
103
|
+
return Statement(self.field_name, Operators.LT, other)
|
|
104
|
+
|
|
105
|
+
def __ne__(self, other: any) -> Statement:
|
|
106
|
+
return Statement(self.field_name, Operators.NOT_EQUALS, other)
|
|
107
|
+
|
|
108
|
+
def is_in(self, other: Union[Iterable, Callable]) -> Statement:
|
|
109
|
+
return Statement(self.field_name, Operators.IN, other)
|
|
110
|
+
|
|
111
|
+
def not_in(self, other: Union[Iterable, Callable]) -> Statement:
|
|
112
|
+
return Statement(self.field_name, Operators.NOT_IN, other)
|
|
113
|
+
|
|
114
|
+
def includes(self, other: Union[Iterable, Callable]) -> Statement:
|
|
115
|
+
return Statement(self.field_name, Operators.INCLUDES, other)
|
|
116
|
+
|
|
117
|
+
def not_includes(self, other: Union[Iterable, Callable]) -> Statement:
|
|
118
|
+
return Statement(self.field_name, Operators.NOT_INCLUDES, other)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class Statement:
|
|
122
|
+
"""
|
|
123
|
+
Statement represents a single statement in boolean algebra. Since boolean algebra statements are recursive,
|
|
124
|
+
one statement can be composed of multiple statements, statement can be of 2 different types. One is a comparison
|
|
125
|
+
on field value and the other is a composition of comparisons.
|
|
126
|
+
|
|
127
|
+
Names of init params can be decieving so please read the following documentation
|
|
128
|
+
|
|
129
|
+
Arguments:
|
|
130
|
+
:statement_a: Is either a nested statement [Statement] or a name of the field [str]
|
|
131
|
+
:operator: Type of common boolean algebra operators [LogicOperators] or comparison operator [Comparator]
|
|
132
|
+
:statement_b: Is either a nested statement [Statement] or a value to compare a field with [any]
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
def __init__(
|
|
136
|
+
self,
|
|
137
|
+
statement_a: Union[str, Statement, Callable],
|
|
138
|
+
operator: Operators,
|
|
139
|
+
statement_b: Union[any, Statement, Callable],
|
|
140
|
+
):
|
|
141
|
+
self.statement_a = statement_a
|
|
142
|
+
self.operator = operator
|
|
143
|
+
self.statement_b = statement_b
|
|
144
|
+
|
|
145
|
+
self.validation()
|
|
146
|
+
|
|
147
|
+
def validation(self) -> None:
|
|
148
|
+
if self.operator in [Operators.IN, Operators.NOT_IN]:
|
|
149
|
+
if not isinstance(self.statement_b, Iterable) and not callable(self.statement_b):
|
|
150
|
+
raise ValueError(f"Operator IN expects an iterable value, got {self.statement_b}")
|
|
151
|
+
|
|
152
|
+
if self.operator == Operators.NOT:
|
|
153
|
+
# Not is a special case, since it only takes 1 argument
|
|
154
|
+
if self.statement_b is not None:
|
|
155
|
+
raise ValueError("Operator NOT expects only first statement, got also second statement")
|
|
156
|
+
|
|
157
|
+
if self.operator in LogicOperators:
|
|
158
|
+
if not (isinstance(self.statement_a, Statement) and isinstance(self.statement_b, Statement)):
|
|
159
|
+
raise ValueError(
|
|
160
|
+
f"Logic operators expect Statement type variables,"
|
|
161
|
+
f"got {type(self.statement_a)}, {type(self.statement_b)}"
|
|
162
|
+
)
|
|
163
|
+
else:
|
|
164
|
+
# We are using comparator
|
|
165
|
+
if isinstance(self.statement_a, Statement) and isinstance(self.statement_b, Statement):
|
|
166
|
+
raise ValueError("Comparators expect non Statement type variables")
|
|
167
|
+
if not isinstance(self.statement_a, str):
|
|
168
|
+
raise ValueError(f"Comparators expects first value to be string type, got {type(self.statement_a)}")
|
|
169
|
+
|
|
170
|
+
def __or__(self, other: Statement) -> Statement:
|
|
171
|
+
return Statement(self, Operators.OR, other)
|
|
172
|
+
|
|
173
|
+
def __and__(self, other: Statement):
|
|
174
|
+
return Statement(self, Operators.AND, other)
|
|
175
|
+
|
|
176
|
+
def __xor__(self, other: Statement):
|
|
177
|
+
return Statement(self, Operators.XOR, other)
|
|
178
|
+
|
|
179
|
+
def __neg__(self):
|
|
180
|
+
return Statement(self, Operators.NOT, None)
|
|
181
|
+
|
|
182
|
+
def to_value(self) -> StatementType:
|
|
183
|
+
statement_a = self.statement_a() if callable(self.statement_a) else self.statement_a
|
|
184
|
+
statement_b = self.statement_b() if callable(self.statement_b) else self.statement_b
|
|
185
|
+
|
|
186
|
+
if isinstance(statement_a, Statement) and isinstance(statement_b, Statement):
|
|
187
|
+
return StatementType(statement_a.to_value(), self.operator, statement_b.to_value())
|
|
188
|
+
return StatementType(statement_a, self.operator, statement_b)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# Aliases for developer friendly experience
|
|
192
|
+
F = Field
|
|
193
|
+
S = Statement
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
import uuid as uuid_module
|
|
3
|
+
|
|
4
|
+
from enum import IntEnum
|
|
5
|
+
from typing import Dict, Optional
|
|
6
|
+
|
|
7
|
+
from rest_framework.fields import Field as DrfField
|
|
8
|
+
from rest_framework.relations import PKOnlyObject
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from dynamicforms.mixins import DFField
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DisplayMode(IntEnum):
|
|
15
|
+
"""
|
|
16
|
+
The ways a field might be rendered (or not).
|
|
17
|
+
|
|
18
|
+
NOTE: This class has a copy in Vue component "df-table", so if you change here, the change needs to be
|
|
19
|
+
implemented there as well
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
SUPPRESS = 1 # Field will be entirely suppressed. it will not render (not even to JSON) and will not parse for PUT
|
|
23
|
+
HIDDEN = 5 # Field will render as <input type="hidden"> or <tr data-field_name>
|
|
24
|
+
INVISIBLE = 8 # Field will render completely, but with display: none. Equal to setting its style = {display: none}
|
|
25
|
+
FULL = 10 # Field will render completely
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FieldAlignment(IntEnum):
|
|
29
|
+
"""
|
|
30
|
+
How field's contents should be aligned in their respective containers (table cell / input)
|
|
31
|
+
|
|
32
|
+
NOTE: This class has a copy in Vue component "df-table", so if you change here, the change needs to be
|
|
33
|
+
implemented there as well
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
LEFT = -1
|
|
37
|
+
CENTER = 0
|
|
38
|
+
RIGHT = 1
|
|
39
|
+
DECIMAL = 2 # Aligned on the decimal separator
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class FieldRenderMixin(object):
|
|
43
|
+
"""
|
|
44
|
+
Is used in fields and serializers, so every field and serializer gets its unique id. Also to specify where and how
|
|
45
|
+
fields should render.
|
|
46
|
+
|
|
47
|
+
In form where serializer is used, id is serializers uuid. Table with list of records has id »list-serializer.uuid«,
|
|
48
|
+
in dialog id is »dialog-{serializer.uuid}« and save button's id on dialog is »save-{serializer.uuid}«
|
|
49
|
+
|
|
50
|
+
Similar for fields: All inputs in HTML get id from field.uuid. Div that contains all that belongs to the field has
|
|
51
|
+
»container-{field.uuid}« for id, label has »label-{field.uuid}« and help text (if exists) has »help-{field.uuid}«
|
|
52
|
+
for id.
|
|
53
|
+
|
|
54
|
+
Used for rendering individual field to table view
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
*args,
|
|
60
|
+
uuid: uuid_module.UUID = None,
|
|
61
|
+
display: DisplayMode = None, # None == Leave at default
|
|
62
|
+
display_table: DisplayMode = None, # None == Leave at default
|
|
63
|
+
display_form: DisplayMode = None, # None == Leave at default
|
|
64
|
+
table_classes: str = "",
|
|
65
|
+
alignment: FieldAlignment = FieldAlignment.LEFT,
|
|
66
|
+
render_params: Optional[Dict] = None,
|
|
67
|
+
**kwargs,
|
|
68
|
+
):
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
:param args: passed on to inherited constructors
|
|
72
|
+
:param uuid: custom specified field UUID. if not specified, it will be assigned automatically
|
|
73
|
+
:param display: see DisplayMode enum. Specifies how field will render. Leave at None for default (FULL)
|
|
74
|
+
display_form and display_table also accepted for better granularity
|
|
75
|
+
:param table_classes: css classes to add to the table column
|
|
76
|
+
:param alignment: How field's contents should be aligned in their respective containers (table cell / input)
|
|
77
|
+
:param render_params: what widget to use for render and its parameters. Whatever you want that affects rendering
|
|
78
|
+
:param kwargs: passed on to inherited constructors
|
|
79
|
+
"""
|
|
80
|
+
super().__init__(*args, **kwargs)
|
|
81
|
+
self.uuid = uuid or uuid_module.uuid1()
|
|
82
|
+
# noinspection PyUnresolvedReferences
|
|
83
|
+
self.display_table = (
|
|
84
|
+
display_table
|
|
85
|
+
or display
|
|
86
|
+
or (DisplayMode.FULL if not getattr(self, "write_only", False) else DisplayMode.SUPPRESS)
|
|
87
|
+
)
|
|
88
|
+
self.display_form = display_form or display or DisplayMode.FULL
|
|
89
|
+
self.table_classes = table_classes
|
|
90
|
+
self.alignment = alignment
|
|
91
|
+
|
|
92
|
+
# render_params need not only include form renderer and table formatting function. add anything you need!
|
|
93
|
+
render_params = render_params or {}
|
|
94
|
+
render_params.setdefault("form_component_name", "DInput")
|
|
95
|
+
render_params.setdefault("table", "df-tablecell-plaintext")
|
|
96
|
+
render_params.setdefault("input_type", "text")
|
|
97
|
+
render_params.setdefault("field_class", "form-control")
|
|
98
|
+
render_params.setdefault("container_class", "form-group")
|
|
99
|
+
self.render_params = render_params
|
|
100
|
+
|
|
101
|
+
# noinspection PyUnresolvedReferences,PyMethodMayBeStatic
|
|
102
|
+
def use_pk_only_optimization(self):
|
|
103
|
+
"""
|
|
104
|
+
Overrides DRF RelatedField's method. If True is returned then value passed for serialisation will be PK value
|
|
105
|
+
only, not entire relation object. we don't want that because then we can't resolve the value into something more
|
|
106
|
+
human-readable
|
|
107
|
+
:return:
|
|
108
|
+
"""
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
# noinspection PyUnresolvedReferences
|
|
112
|
+
def to_representation(self, value, row_data=None):
|
|
113
|
+
"""
|
|
114
|
+
Overrides DRF Field's to_representation.
|
|
115
|
+
Note that this is also called for the entire record as well as the serializer also is a Field descendant
|
|
116
|
+
:param value: value to serialize
|
|
117
|
+
:param row_data: instance with row data
|
|
118
|
+
:return: serialized value
|
|
119
|
+
"""
|
|
120
|
+
check_for_none = value.pk if isinstance(value, PKOnlyObject) else value
|
|
121
|
+
if check_for_none is None:
|
|
122
|
+
value = None
|
|
123
|
+
|
|
124
|
+
if value is None:
|
|
125
|
+
# TODO: DRF excepts if value is None. However, if me have a field that redeclares its
|
|
126
|
+
# to_representation, it now won't be called :(
|
|
127
|
+
return None
|
|
128
|
+
else:
|
|
129
|
+
return super().to_representation(value)
|
|
130
|
+
|
|
131
|
+
def set_display(self, value):
|
|
132
|
+
if isinstance(value, tuple):
|
|
133
|
+
self.display_form, self.display_table = value
|
|
134
|
+
else:
|
|
135
|
+
self.display_form = self.display_table = value
|
|
136
|
+
|
|
137
|
+
display = property(lambda self: self.display_form, set_display)
|
|
138
|
+
|
|
139
|
+
# noinspection PyUnusedLocal, PyUnresolvedReferences
|
|
140
|
+
def render_to_table(self, value, row_data):
|
|
141
|
+
"""
|
|
142
|
+
Renders field value for table view. override for your field
|
|
143
|
+
|
|
144
|
+
:param value: field value
|
|
145
|
+
:param row_data: data for entire row (for more complex renderers)
|
|
146
|
+
:return: rendered value for table view
|
|
147
|
+
"""
|
|
148
|
+
return value
|
|
149
|
+
|
|
150
|
+
def validate_empty_values(self: DrfField, data):
|
|
151
|
+
# noinspection PyUnresolvedReferences
|
|
152
|
+
res = super().validate_empty_values(data)
|
|
153
|
+
|
|
154
|
+
# This is to fix a problem with calculated fields which was only solved in DRF 3.10.
|
|
155
|
+
# Forces validation and inclusion of the field into validated data. See comment in original function.
|
|
156
|
+
if res == (True, None) and data is None and self.source == "*":
|
|
157
|
+
return False, None
|
|
158
|
+
return res
|
|
159
|
+
|
|
160
|
+
def ordering(self):
|
|
161
|
+
ordering = []
|
|
162
|
+
if hasattr(self, "context") and "view" in getattr(self, "context"):
|
|
163
|
+
ordering = getattr(self.context["view"], "ordering", None)
|
|
164
|
+
|
|
165
|
+
if getattr(self, "field_name") not in getattr(ordering, "fields", []):
|
|
166
|
+
return ""
|
|
167
|
+
|
|
168
|
+
index = -1
|
|
169
|
+
direction_asc = True
|
|
170
|
+
for idx, o in enumerate(ordering):
|
|
171
|
+
if o.startswith("-"):
|
|
172
|
+
direction_asc = False
|
|
173
|
+
o = o[1:]
|
|
174
|
+
if o == getattr(self, "field_name"):
|
|
175
|
+
index = idx
|
|
176
|
+
|
|
177
|
+
if index > -1:
|
|
178
|
+
direction_class = ("asc" if direction_asc else "desc") + " seg-%d" % (index + 1)
|
|
179
|
+
else:
|
|
180
|
+
direction_class = "unsorted"
|
|
181
|
+
return "ordering " + direction_class
|
|
182
|
+
|
|
183
|
+
def as_component_def(self: "DFField") -> dict:
|
|
184
|
+
try:
|
|
185
|
+
res = super().as_component_def() # noqa
|
|
186
|
+
except AttributeError:
|
|
187
|
+
res = dict()
|
|
188
|
+
res.update(
|
|
189
|
+
dict(
|
|
190
|
+
uuid=str(self.uuid),
|
|
191
|
+
name=str(self.field_name),
|
|
192
|
+
label=str(self.label),
|
|
193
|
+
read_only=self.read_only,
|
|
194
|
+
table_classes=self.table_classes,
|
|
195
|
+
ordering=self.ordering(),
|
|
196
|
+
alignment=self.alignment.name.lower(),
|
|
197
|
+
visibility=dict(table=self.display_table.value, form=self.display_form.value),
|
|
198
|
+
render_params=self.render_params,
|
|
199
|
+
help_text=res.get("help_text", ""),
|
|
200
|
+
allow_null=self.allow_null,
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
if "base_template" in self.style and self.style["base_template"] == "textarea.html":
|
|
204
|
+
res["render_params"]["form"] = "TextArea"
|
|
205
|
+
if hasattr(self, "max_length"):
|
|
206
|
+
res["render_params"]["max_length"] = self.max_length
|
|
207
|
+
if hasattr(self, "min_length"):
|
|
208
|
+
res["render_params"]["min_length"] = self.min_length
|
|
209
|
+
return res
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import Optional, Union
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class FieldHelpTextMixin(object):
|
|
5
|
+
"""
|
|
6
|
+
Redefines help_text property such that we can set help text for api docs and form help text
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self, *args, help_text: Union[str, dict, None] = None, help_text_form: Optional[str] = None, **kwargs
|
|
11
|
+
) -> None:
|
|
12
|
+
super().__init__(*args, help_text=help_text, **kwargs)
|
|
13
|
+
self.help_text = help_text
|
|
14
|
+
if help_text_form or not isinstance(help_text, dict):
|
|
15
|
+
self._help_text_form = help_text_form or self.help_text
|
|
16
|
+
|
|
17
|
+
def get_help_text(self):
|
|
18
|
+
return self._help_text
|
|
19
|
+
|
|
20
|
+
def set_help_text(self, value):
|
|
21
|
+
if isinstance(value, dict):
|
|
22
|
+
self._help_text = value.get("doc", None)
|
|
23
|
+
self._help_text_form = value.get("form", None)
|
|
24
|
+
else:
|
|
25
|
+
self._help_text = value
|
|
26
|
+
self._help_text_form = getattr(self, "_help_text_form", None) or value
|
|
27
|
+
|
|
28
|
+
def get_help_text_form(self):
|
|
29
|
+
return self._help_text_form
|
|
30
|
+
|
|
31
|
+
def set_help_text_form(self, value):
|
|
32
|
+
if isinstance(value, dict):
|
|
33
|
+
self._help_text = value.get("form", None)
|
|
34
|
+
else:
|
|
35
|
+
self._help_text_form = value
|
|
36
|
+
|
|
37
|
+
help_text = property(get_help_text, set_help_text)
|
|
38
|
+
help_text_form = property(get_help_text_form, set_help_text_form)
|
|
39
|
+
|
|
40
|
+
def as_component_def(self) -> dict:
|
|
41
|
+
try:
|
|
42
|
+
res = super().as_component_def() # noqa
|
|
43
|
+
except AttributeError:
|
|
44
|
+
res = dict()
|
|
45
|
+
res.update(dict(help_text=self.help_text))
|
|
46
|
+
return res
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from django.utils import timezone
|
|
6
|
+
from django.utils.formats import localize
|
|
7
|
+
from rest_framework.fields import DateField, TimeField
|
|
8
|
+
|
|
9
|
+
from .field_render import FieldRenderMixin
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NaturalDateTimeMixin(object):
|
|
13
|
+
"""
|
|
14
|
+
Used for rendering datetime in human natural style (e.g.: 1 hour, 10 minutes ago)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, *args, table_format: str = "", **kwargs) -> None:
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
:param args:
|
|
21
|
+
:param table_format: Format for datetime rendering in table (non editable). If format is %N:{precision},
|
|
22
|
+
datetime will render in natural style with {precision} depth
|
|
23
|
+
:param kwargs:
|
|
24
|
+
"""
|
|
25
|
+
super().__init__(*args, **kwargs)
|
|
26
|
+
self.table_format = table_format
|
|
27
|
+
|
|
28
|
+
# noinspection PyUnresolvedReferences
|
|
29
|
+
def render_to_table(self, value, row_data):
|
|
30
|
+
if value is None:
|
|
31
|
+
return super().render_to_table(value, row_data)
|
|
32
|
+
|
|
33
|
+
if hasattr(self, "enforce_timezone"):
|
|
34
|
+
value = self.enforce_timezone(value)
|
|
35
|
+
|
|
36
|
+
if (output_format := getattr(self, "table_format", None)) is not None:
|
|
37
|
+
if re.match(r"%N:\d+", output_format):
|
|
38
|
+
imported = False
|
|
39
|
+
try:
|
|
40
|
+
# This library (will-natural) is used because is the only one we could find that adds text
|
|
41
|
+
# saying when is this datetime ("ago" or "from now") and have the possibility to set max
|
|
42
|
+
# precision
|
|
43
|
+
|
|
44
|
+
# noinspection PyPackageRequirements
|
|
45
|
+
from natural.date import duration
|
|
46
|
+
|
|
47
|
+
imported = True
|
|
48
|
+
except:
|
|
49
|
+
print("Install library for natural presentation of date (pip install will-natural)")
|
|
50
|
+
|
|
51
|
+
if imported:
|
|
52
|
+
if isinstance(self, DateField):
|
|
53
|
+
now = timezone.now().date()
|
|
54
|
+
elif isinstance(self, TimeField):
|
|
55
|
+
now = datetime.now()
|
|
56
|
+
value = datetime.now().replace(
|
|
57
|
+
hour=value.hour, minute=value.minute, second=value.second, microsecond=value.microsecond
|
|
58
|
+
)
|
|
59
|
+
else:
|
|
60
|
+
now = timezone.now()
|
|
61
|
+
|
|
62
|
+
# noinspection PyUnboundLocalVariable
|
|
63
|
+
return duration(value, now=now, precision=int(output_format.split(":")[1]))
|
|
64
|
+
else:
|
|
65
|
+
# Invoke DRF field's to_representation
|
|
66
|
+
global_format = getattr(self, "format", None)
|
|
67
|
+
setattr(self, "format", output_format)
|
|
68
|
+
# noinspection PySuperArguments
|
|
69
|
+
value = super(FieldRenderMixin, self).to_representation(value) # Skip RenderMixin
|
|
70
|
+
setattr(self, "format", global_format)
|
|
71
|
+
return value or ""
|
|
72
|
+
|
|
73
|
+
return localize(value)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class TimeFieldMixin(NaturalDateTimeMixin):
|
|
77
|
+
def __init__(self, *args, table_format: str = None, **kwargs) -> None:
|
|
78
|
+
super().__init__(*args, table_format=table_format, **kwargs)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class DateFieldMixin(NaturalDateTimeMixin):
|
|
82
|
+
def __init__(self, *args, table_format: str = None, **kwargs) -> None:
|
|
83
|
+
super().__init__(*args, table_format=table_format, **kwargs)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class DateTimeFieldMixin(NaturalDateTimeMixin):
|
|
87
|
+
def __init__(self, *args, table_format: str = None, **kwargs) -> None:
|
|
88
|
+
super().__init__(*args, table_format=table_format, **kwargs)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from rest_framework.fields import IntegerField
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NullValueMixin(object):
|
|
5
|
+
def to_internal_value(self, data):
|
|
6
|
+
if data == "" and self.allow_null:
|
|
7
|
+
self.validators = []
|
|
8
|
+
return None
|
|
9
|
+
return super().to_internal_value(data)
|
|
10
|
+
|
|
11
|
+
def to_representation(self, value, row_data=None):
|
|
12
|
+
if value is None and self.allow_null and not isinstance(self, IntegerField):
|
|
13
|
+
value = ""
|
|
14
|
+
|
|
15
|
+
return super().to_representation(value, row_data)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class PasswordFieldMixin(object):
|
|
2
|
+
def __init__(self, *args, password_field=False, **kwargs):
|
|
3
|
+
super().__init__(*args, **kwargs)
|
|
4
|
+
self.password_field = password_field
|
|
5
|
+
|
|
6
|
+
def set_password_field(self, value):
|
|
7
|
+
from dynamicforms.mixins import DisplayMode
|
|
8
|
+
|
|
9
|
+
self._password_field = value
|
|
10
|
+
if value:
|
|
11
|
+
self.display_table = DisplayMode.SUPPRESS
|
|
12
|
+
self.style.update(input_type="password")
|
|
13
|
+
self.render_params["form_component_name"] = "DPassword"
|
|
14
|
+
self.render_params["input_type"] = "password"
|
|
15
|
+
else:
|
|
16
|
+
try:
|
|
17
|
+
self.style["input_type"]
|
|
18
|
+
self.style.update(input_type="text")
|
|
19
|
+
self.display_table = DisplayMode.FULL
|
|
20
|
+
except:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
password_field = property(lambda self: self._password_field, set_password_field)
|