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
dynamicforms/viewsets.py
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
from datetime import datetime, timedelta
|
|
2
|
+
from typing import List, Union
|
|
3
|
+
|
|
4
|
+
import pytz
|
|
5
|
+
|
|
6
|
+
from django.conf import settings
|
|
7
|
+
from django.db import models
|
|
8
|
+
from django.http import Http404
|
|
9
|
+
from django.utils.dateparse import (
|
|
10
|
+
datetime_re,
|
|
11
|
+
iso8601_duration_re,
|
|
12
|
+
parse_datetime,
|
|
13
|
+
parse_duration,
|
|
14
|
+
parse_time,
|
|
15
|
+
standard_duration_re,
|
|
16
|
+
time_re,
|
|
17
|
+
)
|
|
18
|
+
from rest_framework import viewsets
|
|
19
|
+
from rest_framework.exceptions import ValidationError
|
|
20
|
+
from rest_framework.renderers import JSONRenderer
|
|
21
|
+
from rest_framework.response import Response
|
|
22
|
+
from rest_framework.serializers import ListSerializer
|
|
23
|
+
|
|
24
|
+
from dynamicforms.fields import BooleanField
|
|
25
|
+
from dynamicforms.utils import get_pk_name
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class NewMixin:
|
|
29
|
+
"""
|
|
30
|
+
Provides support for retrieving default values for a new record.
|
|
31
|
+
|
|
32
|
+
Caution: Do not use directly. This is only a mixin and is used in final ViewSet derivatives.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def get_object(self: viewsets.ModelViewSet):
|
|
36
|
+
"""
|
|
37
|
+
Returns the new_object if "new" is requested, otherwise from database
|
|
38
|
+
"""
|
|
39
|
+
# Perform the lookup filtering.
|
|
40
|
+
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
|
41
|
+
if (
|
|
42
|
+
# "new" was requested directly
|
|
43
|
+
(lookup_url_kwarg in self.kwargs and self.kwargs[lookup_url_kwarg] == "new") or
|
|
44
|
+
# if this is a SingleRecordViewSet, our router may have created routes where pk won't even be there
|
|
45
|
+
(lookup_url_kwarg not in self.kwargs and isinstance(self, SingleRecordViewSet))
|
|
46
|
+
):
|
|
47
|
+
return self.new_object()
|
|
48
|
+
|
|
49
|
+
return super().get_object()
|
|
50
|
+
|
|
51
|
+
def new_object(self: viewsets.ModelViewSet):
|
|
52
|
+
"""
|
|
53
|
+
Returns a new model instance. If you need it pre-populated with default values, this is the method to override.
|
|
54
|
+
|
|
55
|
+
:return: model instance
|
|
56
|
+
"""
|
|
57
|
+
# TODO: This function must return an object that has its field values correctly / realistically filled out
|
|
58
|
+
# Example: if a certain field's value hides (sets another field to None), then that second field can't be
|
|
59
|
+
# set to 42, can it?
|
|
60
|
+
# can we run some kind of validation to enforce this?
|
|
61
|
+
# Not that easy: the returned record may not validate for its (correctly) empty fields
|
|
62
|
+
# Maybe we will have to run JavaScript onchange for all fields displayed to ensure at least some consistency?
|
|
63
|
+
# If we do not, subsequent validation may fail because a hidden field has a value
|
|
64
|
+
field_names = [
|
|
65
|
+
(f.name + "_id") if isinstance(f, models.ForeignKey) else f.name
|
|
66
|
+
for f in self.get_queryset().model._meta.fields
|
|
67
|
+
]
|
|
68
|
+
model = self.get_queryset().model
|
|
69
|
+
fld = model._meta.get_field
|
|
70
|
+
instantiation_params = {k: fld(k).to_python(v) for k, v in self.request.GET.items() if k in field_names}
|
|
71
|
+
return self.get_queryset().model(**instantiation_params)
|
|
72
|
+
|
|
73
|
+
# noinspection PyUnresolvedReferences
|
|
74
|
+
def retrieve(self: viewsets.ModelViewSet, request, *args, **kwargs):
|
|
75
|
+
try:
|
|
76
|
+
if not hasattr(super(), "retrieve"):
|
|
77
|
+
raise Http404() # This is not a ModelViewSet, so we don't have a retrieval mechanism
|
|
78
|
+
return super().retrieve(request, *args, **kwargs)
|
|
79
|
+
except Http404:
|
|
80
|
+
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
|
81
|
+
filter_kwargs = {
|
|
82
|
+
self.lookup_field: self.kwargs.get(
|
|
83
|
+
# if this is a SingleRecordViewSet, our router may have created routes where pk won't even be there
|
|
84
|
+
lookup_url_kwarg,
|
|
85
|
+
"new" if isinstance(self, SingleRecordViewSet) else None,
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
if filter_kwargs.get(self.lookup_field, None) == "new":
|
|
89
|
+
instance = self.new_object()
|
|
90
|
+
serializer = self.get_serializer(instance)
|
|
91
|
+
return Response(serializer.data)
|
|
92
|
+
else:
|
|
93
|
+
raise
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class PutPostMixin(object):
|
|
97
|
+
"""
|
|
98
|
+
Provides support for when there is no record id in URL when calling PUT
|
|
99
|
+
(First empty form is loaded. Then user loads existing data to this form and updates it... - Perform PUT action)
|
|
100
|
+
|
|
101
|
+
or
|
|
102
|
+
|
|
103
|
+
Provides support for when there is record id in URL when calling POST
|
|
104
|
+
(First form for some existing record is loaded. Then user wants to create new record for it.
|
|
105
|
+
- deletes record ID and perform POST action)
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
# When there is no record id in URL when calling PUT, this function will be called
|
|
109
|
+
# noinspection PyUnresolvedReferences
|
|
110
|
+
def put(self: viewsets.ModelViewSet, request, *args, **kwargs):
|
|
111
|
+
self.kwargs["pk"] = request.data[get_pk_name(self.get_queryset())]
|
|
112
|
+
return self.update(request, *args, **kwargs)
|
|
113
|
+
|
|
114
|
+
# When there is record id in URL when calling POST, this function will be called
|
|
115
|
+
# noinspection PyUnresolvedReferences
|
|
116
|
+
def post(self: viewsets.ModelViewSet, request, *args, **kwargs):
|
|
117
|
+
return self.create(request, *args, **kwargs)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class TemplateRendererMixin:
|
|
121
|
+
template_context = {}
|
|
122
|
+
"""
|
|
123
|
+
template_context provides configuration to templates being rendered
|
|
124
|
+
|
|
125
|
+
Note that when adding data from database, it is advised to make this definition a callable so that it is evaluated
|
|
126
|
+
on each render of the ViewSet: that way you can ensure data is always loaded fresh from database.
|
|
127
|
+
This member can also be defined in the serializer class. If that is the case, it will be copied to ViewSet.
|
|
128
|
+
|
|
129
|
+
e.g.
|
|
130
|
+
|
|
131
|
+
.. code-block:: python
|
|
132
|
+
|
|
133
|
+
template_context = lambda self: dict(items=MyModel.objects.all())
|
|
134
|
+
|
|
135
|
+
or
|
|
136
|
+
|
|
137
|
+
.. code-block:: python
|
|
138
|
+
|
|
139
|
+
def template_context(self):
|
|
140
|
+
return dict(items=MyModel.objects.all())
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
def __init__(self, *args, **kwds):
|
|
144
|
+
if not self.template_context and getattr(self, "serializer_class", None) is not None:
|
|
145
|
+
self.template_context = getattr(self.serializer_class, "template_context", {})
|
|
146
|
+
super().__init__(*args, **kwds)
|
|
147
|
+
|
|
148
|
+
def get_serializer(self, *args, **kwargs):
|
|
149
|
+
serializer = super().get_serializer(*args, **kwargs)
|
|
150
|
+
if self.format_kwarg == "component":
|
|
151
|
+
# view_mode support: set viewmode when DRF renderer is the component renderer
|
|
152
|
+
if isinstance(serializer, ListSerializer):
|
|
153
|
+
serializer.apply_component_context(self.request, self.paginator)
|
|
154
|
+
else:
|
|
155
|
+
serializer.apply_component_context(self.request, None)
|
|
156
|
+
return serializer
|
|
157
|
+
|
|
158
|
+
# noinspection PyAttributeOutsideInit
|
|
159
|
+
def initialize_request(self, request, *args, **kwargs):
|
|
160
|
+
# Caution: just to be sure for any future debugging: the request parameter to this function is a WSGIRequest
|
|
161
|
+
# while the return Request is actually DRF Request
|
|
162
|
+
# As a consequence, form values don't get parsed until you actually call super().initialize_request
|
|
163
|
+
# There's no "request.data", etc. Just saying. So you don't debug for two hours next time. By "you" I mean me
|
|
164
|
+
|
|
165
|
+
# Force render using a given render path (full page, table, table rows, form, dialog with form)
|
|
166
|
+
self.render_type = request.META.get("HTTP_X_DF_RENDER_TYPE", request.GET.get("df_render_type", "page"))
|
|
167
|
+
|
|
168
|
+
if request.method.lower() == "post" and request.POST.get("data-dynamicforms-method", None):
|
|
169
|
+
# This is a hack because HTML forms can only do POST & GET. This way we also get PUT & PATCH
|
|
170
|
+
request.method = request.POST.get("data-dynamicforms-method")
|
|
171
|
+
# If we don't set this META, django won't recognise our CSRF token
|
|
172
|
+
request.META["HTTP_X_CSRFTOKEN"] = request.POST["csrfmiddlewaretoken"]
|
|
173
|
+
return super().initialize_request(request, *args, **kwargs)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class ModelViewSet(NewMixin, PutPostMixin, TemplateRendererMixin, viewsets.ModelViewSet):
|
|
177
|
+
"""
|
|
178
|
+
In addition to all the functionality, provided by DRF, DynamicForms ViewSet has some extra features:
|
|
179
|
+
|
|
180
|
+
* Separate templates for rendering list or single record
|
|
181
|
+
* You can request a "new" record and even have it pre-populated with values
|
|
182
|
+
* To render viewset as API or JSON use the same method as in DRF: To render it in HTML just add ".html" to the URL.
|
|
183
|
+
* Standard DRF router URL patterns apply:
|
|
184
|
+
|
|
185
|
+
* To render a new record use pk=new.
|
|
186
|
+
* To render an existing record (for editing) use pk={record_id}.
|
|
187
|
+
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
ordering_parameter = "ordering"
|
|
191
|
+
ordering_style = None
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def paginator(self):
|
|
195
|
+
if self.request:
|
|
196
|
+
request = self.request
|
|
197
|
+
pagination_enabled = request.META.get("HTTP_X_PAGINATION", request.GET.get("x_df_pagination", False))
|
|
198
|
+
pagination_enabled = BooleanField().to_internal_value(pagination_enabled)
|
|
199
|
+
if isinstance(request.accepted_renderer, JSONRenderer) and not pagination_enabled:
|
|
200
|
+
# if pagination is disabled, we need to hide the paginator so that nothing breaks looking for it
|
|
201
|
+
return None
|
|
202
|
+
return super().paginator
|
|
203
|
+
|
|
204
|
+
def paginate_queryset(self, queryset):
|
|
205
|
+
"""
|
|
206
|
+
Return a single page of results, or `None` if pagination is disabled.
|
|
207
|
+
"""
|
|
208
|
+
if self.paginator is None:
|
|
209
|
+
return None
|
|
210
|
+
return self.paginator.paginate_queryset(queryset, self.request, view=self)
|
|
211
|
+
|
|
212
|
+
def get_queryset(self):
|
|
213
|
+
"""
|
|
214
|
+
Returns records from queryset with filters applied
|
|
215
|
+
|
|
216
|
+
:return: filtered records
|
|
217
|
+
"""
|
|
218
|
+
return super().get_queryset()
|
|
219
|
+
|
|
220
|
+
def filter_queryset(self, queryset, query_params=None):
|
|
221
|
+
"""
|
|
222
|
+
Applies filters for all fields
|
|
223
|
+
|
|
224
|
+
:param queryset: Queryset
|
|
225
|
+
:param query_params: Custom query_params if needed
|
|
226
|
+
:return: queryset with filters applied
|
|
227
|
+
"""
|
|
228
|
+
res = super().filter_queryset(queryset)
|
|
229
|
+
if self.request:
|
|
230
|
+
if query_params is None:
|
|
231
|
+
query_params = self.request.query_params
|
|
232
|
+
for fld, val in query_params.items():
|
|
233
|
+
res = self.filter_queryset_field(res, fld, val)
|
|
234
|
+
for backend in self.filter_backends:
|
|
235
|
+
if hasattr(backend, "get_ordering"):
|
|
236
|
+
ordering = backend().get_ordering(self.request, queryset, self)
|
|
237
|
+
if ordering:
|
|
238
|
+
self.ordering = ordering
|
|
239
|
+
break
|
|
240
|
+
return res
|
|
241
|
+
|
|
242
|
+
def __get_time_resolution(self, value: str) -> dict:
|
|
243
|
+
try:
|
|
244
|
+
value = value.replace("Z", "")
|
|
245
|
+
resolution = (
|
|
246
|
+
getattr(time_re.match(value), "lastgroup", "")
|
|
247
|
+
or getattr(datetime_re.match(value), "lastgroup", "")
|
|
248
|
+
or getattr(standard_duration_re.match(value), "lastgroup", "")
|
|
249
|
+
or getattr(iso8601_duration_re.match(value), "lastgroup", "")
|
|
250
|
+
)
|
|
251
|
+
if resolution in ("second", "microsecond", "seconds", "microseconds"):
|
|
252
|
+
return dict(seconds=1)
|
|
253
|
+
if resolution in ("hour", "hours"):
|
|
254
|
+
return dict(hours=1)
|
|
255
|
+
if resolution in ("minute", "minutes"):
|
|
256
|
+
return dict(minutes=1)
|
|
257
|
+
except:
|
|
258
|
+
pass
|
|
259
|
+
return {}
|
|
260
|
+
|
|
261
|
+
# noinspection PyMethodMayBeStatic
|
|
262
|
+
def filter_queryset_field(self, queryset, field, value):
|
|
263
|
+
"""
|
|
264
|
+
Applies filter to individual field
|
|
265
|
+
|
|
266
|
+
:param queryset: Queryset
|
|
267
|
+
:param field: Field name
|
|
268
|
+
:param value: Field value
|
|
269
|
+
:return: queryset with applied filter for the field
|
|
270
|
+
"""
|
|
271
|
+
if value is None or value == "":
|
|
272
|
+
return queryset
|
|
273
|
+
|
|
274
|
+
model_meta = queryset.model._meta
|
|
275
|
+
|
|
276
|
+
if field not in (fld.name for fld in model_meta.get_fields()):
|
|
277
|
+
return queryset
|
|
278
|
+
|
|
279
|
+
try:
|
|
280
|
+
# TODO: this would probably be better moved into the fields themselves
|
|
281
|
+
if isinstance(model_meta.get_field(field), (models.CharField, models.TextField)):
|
|
282
|
+
return queryset.filter(**{field + "__icontains": value})
|
|
283
|
+
if isinstance(model_meta.get_field(field), (models.DateTimeField,)):
|
|
284
|
+
date_time: datetime = parse_datetime(value.replace("Z", ""))
|
|
285
|
+
date_time.replace(microsecond=0)
|
|
286
|
+
date_time = pytz.timezone(settings.TIME_ZONE).localize(date_time).astimezone(pytz.utc)
|
|
287
|
+
return queryset.filter(
|
|
288
|
+
**{
|
|
289
|
+
field + "__gte": date_time,
|
|
290
|
+
field + "__lt": date_time + timedelta(**self.__get_time_resolution(value)),
|
|
291
|
+
}
|
|
292
|
+
)
|
|
293
|
+
if isinstance(model_meta.get_field(field), (models.TimeField,)):
|
|
294
|
+
start = datetime.combine(datetime.now().date(), parse_time(value).replace(microsecond=0))
|
|
295
|
+
end = (start + timedelta(**self.__get_time_resolution(value))).time()
|
|
296
|
+
return queryset.filter(**{field + "__gte": start, field + "__lt": end})
|
|
297
|
+
if isinstance(model_meta.get_field(field), (models.DurationField,)):
|
|
298
|
+
duration = parse_duration(value)
|
|
299
|
+
duration = duration - timedelta(microseconds=duration.microseconds)
|
|
300
|
+
return queryset.filter(
|
|
301
|
+
**{
|
|
302
|
+
field + "__gte": duration,
|
|
303
|
+
field + "__lt": duration + timedelta(**self.__get_time_resolution(value)),
|
|
304
|
+
}
|
|
305
|
+
)
|
|
306
|
+
if isinstance(model_meta.get_field(field), (models.DateField,)):
|
|
307
|
+
date_time = None
|
|
308
|
+
for date_time_fmt in [settings.DATE_FORMAT, "%Y-%m-%d"]:
|
|
309
|
+
try:
|
|
310
|
+
date_time = datetime.strptime(value, date_time_fmt)
|
|
311
|
+
break
|
|
312
|
+
except:
|
|
313
|
+
pass
|
|
314
|
+
date_time = pytz.timezone(settings.TIME_ZONE).localize(date_time).astimezone(pytz.utc)
|
|
315
|
+
return queryset.filter(**{field + "__gte": date_time, field + "__lt": date_time + timedelta(days=1)})
|
|
316
|
+
else:
|
|
317
|
+
if isinstance(model_meta.get_field(field), models.BooleanField):
|
|
318
|
+
value = value == "true"
|
|
319
|
+
return queryset.filter(**{field: value})
|
|
320
|
+
except:
|
|
321
|
+
return queryset
|
|
322
|
+
|
|
323
|
+
@staticmethod
|
|
324
|
+
def generate_paged_loader(page_size: int = 30, ordering: Union[str, List[str]] = "id"):
|
|
325
|
+
"""
|
|
326
|
+
Generates a Pagination class that will handle dynamic data loading for ViewSets with a lot of data.
|
|
327
|
+
Use by declaring `pagination_class = ModelViewSet.generate_paged_loader()` in class variables
|
|
328
|
+
|
|
329
|
+
:param page_size: how many records should be fetched at a time
|
|
330
|
+
:param ordering: This should be a string, or list of strings, indicating the field against which the cursor
|
|
331
|
+
based pagination will be applied. For example: ordering = 'slug'
|
|
332
|
+
:return: a Pagination class
|
|
333
|
+
"""
|
|
334
|
+
from dynamicforms.pagination import CursorPagination
|
|
335
|
+
|
|
336
|
+
ps = page_size
|
|
337
|
+
ordr = ordering
|
|
338
|
+
|
|
339
|
+
class MyCursorPagination(CursorPagination):
|
|
340
|
+
ordering = ordr
|
|
341
|
+
page_size = ps
|
|
342
|
+
df_request = None
|
|
343
|
+
|
|
344
|
+
def paginate_queryset(self, queryset, request, view=None):
|
|
345
|
+
self.df_request = request
|
|
346
|
+
return super().paginate_queryset(queryset, request, view)
|
|
347
|
+
|
|
348
|
+
def encode_cursor(self, cursor):
|
|
349
|
+
# Following code is needed when we have https proxy server that redirects requests to http servers.
|
|
350
|
+
# In that case original code generates cursor links that have http scheme.
|
|
351
|
+
# So here I check REFERER header to find out which scheme is originally declared.
|
|
352
|
+
# And use that one in cursor link.
|
|
353
|
+
request = getattr(self, "df_request", None)
|
|
354
|
+
cursor_url = super().encode_cursor(cursor).split(":", 1)
|
|
355
|
+
req_url = self.df_request.META.get("HTTP_REFERER", None)
|
|
356
|
+
if req_url:
|
|
357
|
+
req_url = req_url.split(":", 1)
|
|
358
|
+
if cursor_url[0] != req_url[0] and req_url[0].lower() in ("http", "https"):
|
|
359
|
+
cursor_url[0] = req_url[0]
|
|
360
|
+
cursor_url = ":".join(cursor_url)
|
|
361
|
+
if (
|
|
362
|
+
request
|
|
363
|
+
and isinstance(request.accepted_renderer, JSONRenderer)
|
|
364
|
+
and "x_df_pagination" not in cursor_url
|
|
365
|
+
):
|
|
366
|
+
cursor_url += "&x_df_pagination=1"
|
|
367
|
+
return cursor_url
|
|
368
|
+
|
|
369
|
+
def get_paginated_response(self, data):
|
|
370
|
+
# TODO: this is to be removed - we're getting component definitions through
|
|
371
|
+
# ComponentHTMLRenderer.render_type=component-def
|
|
372
|
+
serializer = data.serializer
|
|
373
|
+
request = serializer.request
|
|
374
|
+
if (
|
|
375
|
+
request
|
|
376
|
+
and isinstance(request.accepted_renderer, JSONRenderer)
|
|
377
|
+
and BooleanField().to_internal_value(request.META.get("HTTP_X_DF_COMPONENT_DEF", False))
|
|
378
|
+
):
|
|
379
|
+
# if component definition was requested, let's return that and not just the data
|
|
380
|
+
serializer.apply_component_context(request, self)
|
|
381
|
+
return Response(
|
|
382
|
+
serializer.component_params(
|
|
383
|
+
output_json=False,
|
|
384
|
+
data=dict(next=self.get_next_link(), previous=self.get_previous_link(), results=data),
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
return super().get_paginated_response(data)
|
|
388
|
+
|
|
389
|
+
return MyCursorPagination
|
|
390
|
+
|
|
391
|
+
def handle_create_validation_exception(self, e, request, *args, **kwargs):
|
|
392
|
+
instance = self.new_object()
|
|
393
|
+
ser = self.get_serializer(instance, data=request.data, partial=False)
|
|
394
|
+
ser.is_valid(raise_exception=False)
|
|
395
|
+
e.detail.serializer = ser
|
|
396
|
+
raise e
|
|
397
|
+
|
|
398
|
+
def create(self, request, *args, **kwargs):
|
|
399
|
+
try:
|
|
400
|
+
return super().create(request, *args, **kwargs)
|
|
401
|
+
except ValidationError as e:
|
|
402
|
+
self.handle_create_validation_exception(e, request, *args, **kwargs)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
class ViewSet(NewMixin, viewsets.ViewSet):
|
|
406
|
+
# When using this class make sure you do a serializer.apply_component_context before returning Response(serializer)
|
|
407
|
+
pass
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class SingleRecordViewSet(NewMixin, TemplateRendererMixin, viewsets.GenericViewSet):
|
|
411
|
+
def new_object(self):
|
|
412
|
+
raise NotImplementedError()
|
|
413
|
+
|
|
414
|
+
def create(self, request, *args, **kwargs):
|
|
415
|
+
raise NotImplementedError()
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
# noinspection PyUnresolvedReferences
|
|
419
|
+
class GenericViewSet(NewMixin, PutPostMixin, TemplateRendererMixin, viewsets.GenericViewSet):
|
|
420
|
+
def __init__(self, *args, **kwargs):
|
|
421
|
+
super().__init__(*args, **kwargs)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: dynamicforms
|
|
3
|
+
Version: 0.76.14
|
|
4
|
+
Summary: DynamicForms performs all the visualisation & data entry of your DRF Serializers & ViewSets and adds some candy of its own
|
|
5
|
+
Project-URL: Homepage, https://github.com/velis74/DynamicForms
|
|
6
|
+
Project-URL: Repository, https://github.com/velis74/DynamicForms
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/velis74/DynamicForms/issues
|
|
8
|
+
Author-email: Jure Erznožnik <jure@velis.si>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Framework :: Django
|
|
13
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Requires-Dist: coreapi>=1.32
|
|
18
|
+
Requires-Dist: django-cors-headers
|
|
19
|
+
Requires-Dist: django-enumfields>=2.1.1
|
|
20
|
+
Requires-Dist: django-filter
|
|
21
|
+
Requires-Dist: django>=2
|
|
22
|
+
Requires-Dist: djangorestframework>=3.14
|
|
23
|
+
Requires-Dist: jsonschema
|
|
24
|
+
Requires-Dist: pillow
|
|
25
|
+
Requires-Dist: pytz
|
|
26
|
+
Requires-Dist: ruff
|
|
27
|
+
Requires-Dist: ruff-lsp
|
|
28
|
+
Requires-Dist: versio
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Notice to users of this library
|
|
32
|
+
|
|
33
|
+
Releases 0.50.x and 0.70.x are interim releases with which we prepare the stage for 1.x release.
|
|
34
|
+
|
|
35
|
+
0.50.x moves all "existing" code to `dynamicforms_legacy` module. If you weren't following vue
|
|
36
|
+
development branches, you will need to change all imports in python to this "new" module.
|
|
37
|
+
|
|
38
|
+
This is done to keep legacy code still running as existing code is transitioned to vue-based approach. Unless there is
|
|
39
|
+
significant interest, we will remove this module with 1.x release.
|
|
40
|
+
|
|
41
|
+
0.70.x re-introduces `dynamicforms` module, but this time refactored to only provide .componentdef OPTIONS +
|
|
42
|
+
payload data responses needed by the vue front-end library. This will hopefully be refactored to be more OpenAPI
|
|
43
|
+
compatible. The new primary branch is now `main`.
|
|
44
|
+
|
|
45
|
+
HTML renderers will no longer be supported and have been removed from the "new" dynamicforms module. It was too slow
|
|
46
|
+
and required too many hacks to remain viable. So we moved to Vue. The components in 0.70 will be vue3, vuetify3, vite
|
|
47
|
+
and typescript-compatible. We're in final stages of adapting to the new stack. Some inputs and some table functionality
|
|
48
|
+
isn't working yet.
|
|
49
|
+
|
|
50
|
+
We're keeping the Bootstrap stubs too, but not actively developing to support seamless selection of the two frameworks.
|
|
51
|
+
If there is interest to support CSS frameworks other than Vuetify, pull requests welcome. Hopefully the stubs should
|
|
52
|
+
point the way on how to do it.
|
|
53
|
+
|
|
54
|
+
Migration path is thus:
|
|
55
|
+
|
|
56
|
+
- Upgrade to dynamicforms \>= 0.50.3
|
|
57
|
+
- replace all dynamicforms imports with dynamicforms_legacy
|
|
58
|
+
- because of the rename, there is a bit of work required in settings.py so that Django can find the templates and
|
|
59
|
+
filters:
|
|
60
|
+
|
|
61
|
+
``` python
|
|
62
|
+
from dynamicforms_legacy import __file__ as DYNAMICFORMS_BASEDIR_FILE
|
|
63
|
+
DYNAMICFORMS_BASEDIR = os.path.dirname(DYNAMICFORMS_BASEDIR_FILE)
|
|
64
|
+
...
|
|
65
|
+
INSTALLED_APPS = [
|
|
66
|
+
...
|
|
67
|
+
'dynamicforms_legacy'
|
|
68
|
+
|
|
69
|
+
TEMPLATES = [
|
|
70
|
+
...
|
|
71
|
+
'DIRS': [
|
|
72
|
+
os.path.join(DYNAMICFORMS_BASEDIR, 'templates'),
|
|
73
|
+
...
|
|
74
|
+
'OPTIONS': {
|
|
75
|
+
...
|
|
76
|
+
'libraries': {
|
|
77
|
+
'dynamicforms': 'dynamicforms_legacy.templatetags.dynamicforms',
|
|
78
|
+
}
|
|
79
|
+
STATICFILES_DIRS = [
|
|
80
|
+
...
|
|
81
|
+
os.path.join(DYNAMICFORMS_BASEDIR, 'static'),
|
|
82
|
+
]
|
|
83
|
+
```
|
|
84
|
+
- replace any javascript dynamicforms progress calls with progress-legacy
|
|
85
|
+
- all other javascript code remains the same (including the dynamicforms object with support functions)
|
|
86
|
+
- check that everything still works
|
|
87
|
+
- Upgrade dynamicforms to \>= 0.70.1
|
|
88
|
+
- Start migration to Vue front-end and the new backend
|
|
89
|
+
|
|
90
|
+
# What is DynamicForms?
|
|
91
|
+
|
|
92
|
+
DynamicForms wants to eliminate HTML form boilerplate for generic tables & forms. Specifying a single DRF Serializer /
|
|
93
|
+
ViewSet and possibly desired form layout instantly provides both HTML renders and JSON renders (and anything else DRF
|
|
94
|
+
supports) keeping you free to implement your project.
|
|
95
|
+
|
|
96
|
+
There are two parts to DanymicForms:
|
|
97
|
+
|
|
98
|
+
- Django / DRF extensions providing the JSON definitions for on-screen objects
|
|
99
|
+
- HTML / Javascript component library providing the visualisation
|
|
100
|
+
|
|
101
|
+
It performs all the visualisation & data entry of your DRF Serializers & ViewSets and adds some candy of its own: It is
|
|
102
|
+
a [django](https://www.djangoproject.com/) library that gives you the power of dynamically-shown form fields,
|
|
103
|
+
autofilled default values, dynamic record loading and similar candy with little effort. To put it differently: once
|
|
104
|
+
defined, a particular ViewSet / Serializer can be rendered in multiple ways allowing you to perform viewing and
|
|
105
|
+
authoring operations on the data in question.
|
|
106
|
+
|
|
107
|
+
It is based on
|
|
108
|
+
[django-rest-framework](http://www.django-rest-framework.org/)
|
|
109
|
+
|
|
110
|
+
Documentation [on readthedocs](https://dynamicforms.readthedocs.io/)
|
|
111
|
+
|
|
112
|
+
## Why DynamicForms
|
|
113
|
+
|
|
114
|
+
- Turn your rest-framework ViewSets into HTML tables & forms
|
|
115
|
+
- Powerful HTML based CRUD
|
|
116
|
+
- Support for fetching "new" records
|
|
117
|
+
- Render to table, form or dialog, with plenty of customisation options
|
|
118
|
+
- Full nested data support
|
|
119
|
+
- Dynamically display & hide fields based on other fields' values
|
|
120
|
+
- Easily add actions and place the buttons to execute them anywhere you like
|
|
121
|
+
- Clear separation of list & dialog templates
|
|
122
|
+
- Dynamic loading of additional records for table views supported by infinite scroll
|
|
123
|
+
- Responsive tables (multiple table layouts for various screen widths)
|
|
124
|
+
- Easy implementation of filtering
|
|
125
|
+
- Action items, declared globally, placed where you need them
|
|
126
|
+
- Custom templates whenever & wherever you want them
|
|
127
|
+
- Support for form validation, will show errors even if they are not tied to a field
|
|
128
|
+
- Progress dialog for long lasting ajax operations
|
|
129
|
+
|