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.
Files changed (287) hide show
  1. dynamicforms/__init__.py +8 -0
  2. dynamicforms/action.py +521 -0
  3. dynamicforms/apps.py +5 -0
  4. dynamicforms/fields.py +1570 -0
  5. dynamicforms/filters.py +44 -0
  6. dynamicforms/locale/en/LC_MESSAGES/django.po +67 -0
  7. dynamicforms/locale/en/LC_MESSAGES/djangojs.po +31 -0
  8. dynamicforms/locale/sl/LC_MESSAGES/django.po +71 -0
  9. dynamicforms/locale/sl/LC_MESSAGES/djangojs.po +36 -0
  10. dynamicforms/management/__init__.py +0 -0
  11. dynamicforms/management/commands/__init__.py +0 -0
  12. dynamicforms/management/commands/clean_dfpreuploaded_files.py +26 -0
  13. dynamicforms/migrations/__init__.py +0 -0
  14. dynamicforms/mixins/__init__.py +27 -0
  15. dynamicforms/mixins/action.py +14 -0
  16. dynamicforms/mixins/choice/__init__.py +94 -0
  17. dynamicforms/mixins/choice/allow_tags.py +79 -0
  18. dynamicforms/mixins/choice/null_choice.py +26 -0
  19. dynamicforms/mixins/choice/single_choice.py +21 -0
  20. dynamicforms/mixins/color_field.py +4 -0
  21. dynamicforms/mixins/conditional_visibility.py +193 -0
  22. dynamicforms/mixins/enable_copy.py +4 -0
  23. dynamicforms/mixins/field_render.py +209 -0
  24. dynamicforms/mixins/file_field.py +5 -0
  25. dynamicforms/mixins/help_text.py +46 -0
  26. dynamicforms/mixins/implicit_hidden.py +7 -0
  27. dynamicforms/mixins/implicit_natural_date.py +88 -0
  28. dynamicforms/mixins/null_value.py +15 -0
  29. dynamicforms/mixins/password_field.py +23 -0
  30. dynamicforms/mixins/related_field_ajax.py +142 -0
  31. dynamicforms/mixins/rtf_field.py +58 -0
  32. dynamicforms/models.py +5 -0
  33. dynamicforms/models_utils.py +33 -0
  34. dynamicforms/pagination.py +175 -0
  35. dynamicforms/preupload_files.py +42 -0
  36. dynamicforms/progress.py +71 -0
  37. dynamicforms/renderers.py +1 -0
  38. dynamicforms/routers.py +36 -0
  39. dynamicforms/serializers.py +486 -0
  40. dynamicforms/struct.py +40 -0
  41. dynamicforms/template_render/__init__.py +10 -0
  42. dynamicforms/template_render/layout.py +178 -0
  43. dynamicforms/template_render/mixins/__init__.py +0 -0
  44. dynamicforms/template_render/mixins/base.py +39 -0
  45. dynamicforms/template_render/mixins/field.py +74 -0
  46. dynamicforms/template_render/mixins/listserializer.py +100 -0
  47. dynamicforms/template_render/mixins/render_mode_enum.py +8 -0
  48. dynamicforms/template_render/mixins/serializer.py +301 -0
  49. dynamicforms/template_render/mixins/serializer_filter.py +19 -0
  50. dynamicforms/template_render/mixins/serializer_render_actions.py +21 -0
  51. dynamicforms/template_render/mixins/serializer_render_fields.py +57 -0
  52. dynamicforms/template_render/mixins/util.py +9 -0
  53. dynamicforms/template_render/renderer.py +38 -0
  54. dynamicforms/template_render/responsive_table_layout.py +64 -0
  55. dynamicforms/urls.py +9 -0
  56. dynamicforms/utils.py +32 -0
  57. dynamicforms/views.py +1 -0
  58. dynamicforms/viewsets.py +421 -0
  59. dynamicforms-0.76.14.dist-info/METADATA +129 -0
  60. dynamicforms-0.76.14.dist-info/RECORD +287 -0
  61. dynamicforms-0.76.14.dist-info/WHEEL +4 -0
  62. dynamicforms-0.76.14.dist-info/licenses/LICENSE +21 -0
  63. dynamicforms_legacy/__init__.py +8 -0
  64. dynamicforms_legacy/action.py +561 -0
  65. dynamicforms_legacy/apps.py +5 -0
  66. dynamicforms_legacy/context_processors.py +27 -0
  67. dynamicforms_legacy/dynamicforms_migration_mixin.py +42 -0
  68. dynamicforms_legacy/fields.py +1108 -0
  69. dynamicforms_legacy/filters.py +44 -0
  70. dynamicforms_legacy/migrations/__init__.py +0 -0
  71. dynamicforms_legacy/mixins/__init__.py +16 -0
  72. dynamicforms_legacy/mixins/action.py +14 -0
  73. dynamicforms_legacy/mixins/allow_tags.py +85 -0
  74. dynamicforms_legacy/mixins/enable_copy.py +4 -0
  75. dynamicforms_legacy/mixins/file_field.py +5 -0
  76. dynamicforms_legacy/mixins/help_text.py +38 -0
  77. dynamicforms_legacy/mixins/implicit_hidden.py +7 -0
  78. dynamicforms_legacy/mixins/implicit_natural_date.py +86 -0
  79. dynamicforms_legacy/mixins/null_choice.py +26 -0
  80. dynamicforms_legacy/mixins/null_value.py +15 -0
  81. dynamicforms_legacy/mixins/password_field.py +19 -0
  82. dynamicforms_legacy/mixins/related_field_ajax.py +58 -0
  83. dynamicforms_legacy/mixins/render.py +203 -0
  84. dynamicforms_legacy/mixins/rtf_field.py +52 -0
  85. dynamicforms_legacy/mixins/single_choice.py +21 -0
  86. dynamicforms_legacy/models.py +5 -0
  87. dynamicforms_legacy/pagination.py +168 -0
  88. dynamicforms_legacy/progress.py +71 -0
  89. dynamicforms_legacy/renderers.py +183 -0
  90. dynamicforms_legacy/serializers.py +335 -0
  91. dynamicforms_legacy/settings.py +196 -0
  92. dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap-theme.css +587 -0
  93. dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -0
  94. dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css +6 -0
  95. dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -0
  96. dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap.css +6834 -0
  97. dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -0
  98. dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap.min.css +6 -0
  99. dynamicforms_legacy/static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -0
  100. dynamicforms_legacy/static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.eot +0 -0
  101. dynamicforms_legacy/static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.svg +288 -0
  102. dynamicforms_legacy/static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.ttf +0 -0
  103. dynamicforms_legacy/static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff +0 -0
  104. dynamicforms_legacy/static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
  105. dynamicforms_legacy/static/bootstrap-3.4.1-dist/js/bootstrap.js +2580 -0
  106. dynamicforms_legacy/static/bootstrap-3.4.1-dist/js/bootstrap.min.js +6 -0
  107. dynamicforms_legacy/static/bootstrap-3.4.1-dist/js/npm.js +13 -0
  108. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-grid.css +3719 -0
  109. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-grid.css.map +1 -0
  110. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-grid.min.css +7 -0
  111. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-grid.min.css.map +1 -0
  112. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-reboot.css +331 -0
  113. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-reboot.css.map +1 -0
  114. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-reboot.min.css +8 -0
  115. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap-reboot.min.css.map +1 -0
  116. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap.css +10038 -0
  117. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap.css.map +1 -0
  118. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap.min.css +7 -0
  119. dynamicforms_legacy/static/bootstrap-4.3.1-dist/css/bootstrap.min.css.map +1 -0
  120. dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.bundle.js +7013 -0
  121. dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.bundle.js.map +1 -0
  122. dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.bundle.min.js +7 -0
  123. dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.bundle.min.js.map +1 -0
  124. dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.js +4435 -0
  125. dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.js.map +1 -0
  126. dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.min.js +7 -0
  127. dynamicforms_legacy/static/bootstrap-4.3.1-dist/js/bootstrap.min.js.map +1 -0
  128. dynamicforms_legacy/static/ckeditor-df/ckEditorCustomBuildReadMe.txt +255 -0
  129. dynamicforms_legacy/static/ckeditor-df/ckeditor.js +6 -0
  130. dynamicforms_legacy/static/dynamicforms/dynamicforms.js +1883 -0
  131. dynamicforms_legacy/static/dynamicforms/icons/copy.svg +7 -0
  132. dynamicforms_legacy/static/dynamicforms/icons/eye-off-outline.svg +10 -0
  133. dynamicforms_legacy/static/dynamicforms/icons/eye-outline.svg +7 -0
  134. dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  135. dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  136. dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  137. dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  138. dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  139. dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  140. dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  141. dynamicforms_legacy/static/jquery_ui/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  142. dynamicforms_legacy/static/jquery_ui/css/images/ui-icons_222222_256x240.png +0 -0
  143. dynamicforms_legacy/static/jquery_ui/css/images/ui-icons_228ef1_256x240.png +0 -0
  144. dynamicforms_legacy/static/jquery_ui/css/images/ui-icons_ef8c08_256x240.png +0 -0
  145. dynamicforms_legacy/static/jquery_ui/css/images/ui-icons_ffd27a_256x240.png +0 -0
  146. dynamicforms_legacy/static/jquery_ui/css/images/ui-icons_ffffff_256x240.png +0 -0
  147. dynamicforms_legacy/static/jquery_ui/css/jquery-ui.min.css +7 -0
  148. dynamicforms_legacy/static/jquery_ui/css/jquery-ui.structure.min.css +5 -0
  149. dynamicforms_legacy/static/jquery_ui/css/jquery-ui.theme.min.css +5 -0
  150. dynamicforms_legacy/static/jquery_ui/js/jquery-3.3.1.min.js +2 -0
  151. dynamicforms_legacy/static/jquery_ui/js/jquery-ui.min.js +13 -0
  152. dynamicforms_legacy/static/select2/css/select2-bootstrap3.min.css +7 -0
  153. dynamicforms_legacy/static/select2/css/select2-bootstrap4.min.css +578 -0
  154. dynamicforms_legacy/static/select2/select2-4.0.5/css/select2.min.css +1 -0
  155. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/af.js +3 -0
  156. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ar.js +3 -0
  157. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/az.js +3 -0
  158. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/bg.js +3 -0
  159. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/bs.js +3 -0
  160. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ca.js +3 -0
  161. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/cs.js +3 -0
  162. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/da.js +3 -0
  163. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/de.js +3 -0
  164. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/dsb.js +3 -0
  165. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/el.js +3 -0
  166. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/en.js +3 -0
  167. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/es.js +3 -0
  168. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/et.js +3 -0
  169. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/eu.js +3 -0
  170. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/fa.js +3 -0
  171. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/fi.js +3 -0
  172. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/fr.js +3 -0
  173. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/gl.js +3 -0
  174. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/he.js +3 -0
  175. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/hi.js +3 -0
  176. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/hr.js +3 -0
  177. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/hsb.js +3 -0
  178. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/hu.js +3 -0
  179. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/hy.js +3 -0
  180. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/id.js +3 -0
  181. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/is.js +3 -0
  182. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/it.js +3 -0
  183. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ja.js +3 -0
  184. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/km.js +3 -0
  185. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ko.js +3 -0
  186. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/lt.js +3 -0
  187. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/lv.js +3 -0
  188. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/mk.js +3 -0
  189. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ms.js +3 -0
  190. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/nb.js +3 -0
  191. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/nl.js +3 -0
  192. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/pl.js +3 -0
  193. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ps.js +3 -0
  194. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/pt-BR.js +3 -0
  195. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/pt.js +3 -0
  196. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ro.js +3 -0
  197. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/ru.js +3 -0
  198. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/sk.js +3 -0
  199. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/sl.js +3 -0
  200. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/sr-Cyrl.js +3 -0
  201. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/sr.js +3 -0
  202. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/sv.js +3 -0
  203. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/th.js +3 -0
  204. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/tr.js +3 -0
  205. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/uk.js +3 -0
  206. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/vi.js +3 -0
  207. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/zh-CN.js +3 -0
  208. dynamicforms_legacy/static/select2/select2-4.0.5/js/i18n/zh-TW.js +3 -0
  209. dynamicforms_legacy/static/select2/select2-4.0.5/js/select2.full.js +6457 -0
  210. dynamicforms_legacy/static/select2/select2-4.0.5/js/select2.full.min.js +1 -0
  211. dynamicforms_legacy/static/select2/select2-4.0.5/js/select2.js +5746 -0
  212. dynamicforms_legacy/static/select2/select2-4.0.5/js/select2.min.js +1 -0
  213. dynamicforms_legacy/struct.py +40 -0
  214. dynamicforms_legacy/templates/dynamicforms/base.html +12 -0
  215. dynamicforms_legacy/templates/dynamicforms/base_form.html +44 -0
  216. dynamicforms_legacy/templates/dynamicforms/base_includes.html +51 -0
  217. dynamicforms_legacy/templates/dynamicforms/base_includes_select2.html +6 -0
  218. dynamicforms_legacy/templates/dynamicforms/base_list.html +47 -0
  219. dynamicforms_legacy/templates/dynamicforms/base_table_body.html +46 -0
  220. dynamicforms_legacy/templates/dynamicforms/base_table_filter.html +44 -0
  221. dynamicforms_legacy/templates/dynamicforms/bootstrap/base_form.html +10 -0
  222. dynamicforms_legacy/templates/dynamicforms/bootstrap/base_includes.html +46 -0
  223. dynamicforms_legacy/templates/dynamicforms/bootstrap/base_includes_v3.html +90 -0
  224. dynamicforms_legacy/templates/dynamicforms/bootstrap/base_includes_v4.html +77 -0
  225. dynamicforms_legacy/templates/dynamicforms/bootstrap/base_list.html +9 -0
  226. dynamicforms_legacy/templates/dynamicforms/bootstrap/base_table_body.html +4 -0
  227. dynamicforms_legacy/templates/dynamicforms/bootstrap/base_table_filter.html +5 -0
  228. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/base_field.html +41 -0
  229. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/base_field_v3.html +11 -0
  230. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/base_field_v4.html +11 -0
  231. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/checkbox.html +7 -0
  232. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/checkbox_multiple.html +7 -0
  233. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/fieldset.html +21 -0
  234. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/form.html +4 -0
  235. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/input.html +31 -0
  236. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/list_fieldset.html +18 -0
  237. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/radio.html +8 -0
  238. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/rtf_field.html +4 -0
  239. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/select.html +46 -0
  240. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/select_multiple.html +17 -0
  241. dynamicforms_legacy/templates/dynamicforms/bootstrap/field/textarea.html +4 -0
  242. dynamicforms_legacy/templates/dynamicforms/bootstrap/modal_dialog.html +13 -0
  243. dynamicforms_legacy/templates/dynamicforms/bootstrap/modal_dialog_rest.html +23 -0
  244. dynamicforms_legacy/templates/dynamicforms/bootstrap/page.html +11 -0
  245. dynamicforms_legacy/templates/dynamicforms/field/base_field.html +55 -0
  246. dynamicforms_legacy/templates/dynamicforms/field/checkbox.html +28 -0
  247. dynamicforms_legacy/templates/dynamicforms/field/checkbox_multiple.html +16 -0
  248. dynamicforms_legacy/templates/dynamicforms/field/dict_field.html +4 -0
  249. dynamicforms_legacy/templates/dynamicforms/field/fieldset.html +4 -0
  250. dynamicforms_legacy/templates/dynamicforms/field/form.html +18 -0
  251. dynamicforms_legacy/templates/dynamicforms/field/input.html +31 -0
  252. dynamicforms_legacy/templates/dynamicforms/field/list_field.html +4 -0
  253. dynamicforms_legacy/templates/dynamicforms/field/list_fieldset.html +4 -0
  254. dynamicforms_legacy/templates/dynamicforms/field/radio.html +24 -0
  255. dynamicforms_legacy/templates/dynamicforms/field/rtf_field.html +14 -0
  256. dynamicforms_legacy/templates/dynamicforms/field/select.html +97 -0
  257. dynamicforms_legacy/templates/dynamicforms/field/select_multiple.html +67 -0
  258. dynamicforms_legacy/templates/dynamicforms/field/textarea.html +13 -0
  259. dynamicforms_legacy/templates/dynamicforms/jquery_ui/base_form.html +13 -0
  260. dynamicforms_legacy/templates/dynamicforms/jquery_ui/base_includes.html +225 -0
  261. dynamicforms_legacy/templates/dynamicforms/jquery_ui/base_list.html +9 -0
  262. dynamicforms_legacy/templates/dynamicforms/jquery_ui/base_table_body.html +4 -0
  263. dynamicforms_legacy/templates/dynamicforms/jquery_ui/base_table_filter.html +5 -0
  264. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/base_field.html +16 -0
  265. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/checkbox.html +7 -0
  266. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/checkbox_multiple.html +9 -0
  267. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/fieldset.html +11 -0
  268. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/form.html +4 -0
  269. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/input.html +17 -0
  270. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/list_fieldset.html +6 -0
  271. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/radio.html +8 -0
  272. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/rtf_field.html +4 -0
  273. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/select.html +37 -0
  274. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/select_multiple.html +18 -0
  275. dynamicforms_legacy/templates/dynamicforms/jquery_ui/field/textarea.html +4 -0
  276. dynamicforms_legacy/templates/dynamicforms/jquery_ui/modal_dialog.html +2 -0
  277. dynamicforms_legacy/templates/dynamicforms/jquery_ui/modal_dialog_rest.html +27 -0
  278. dynamicforms_legacy/templates/dynamicforms/jquery_ui/page.html +20 -0
  279. dynamicforms_legacy/templates/dynamicforms/modal_dialog.html +4 -0
  280. dynamicforms_legacy/templates/dynamicforms/modal_dialog_rest.html +18 -0
  281. dynamicforms_legacy/templates/dynamicforms/page.html +25 -0
  282. dynamicforms_legacy/templates/dynamicforms/rtf_field_style.html +41 -0
  283. dynamicforms_legacy/templatetags/__init__.py +0 -0
  284. dynamicforms_legacy/templatetags/dynamicforms.py +475 -0
  285. dynamicforms_legacy/urls.py +8 -0
  286. dynamicforms_legacy/views.py +1 -0
  287. dynamicforms_legacy/viewsets.py +366 -0
@@ -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
+