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,44 @@
1
+ import django_filters.rest_framework as filters
2
+
3
+
4
+ class ListWithFields(list):
5
+ def __init__(self, fields, lst) -> None:
6
+ super().__init__()
7
+ self.fields = fields
8
+ self.extend(lst)
9
+
10
+
11
+ class FilterBackend(filters.DjangoFilterBackend):
12
+ """
13
+ Our own FilterBackend that also tries to read order from the pagination class's ordering field
14
+ """
15
+
16
+ def get_filterset_class(self, view, queryset=None):
17
+ # here we look for any pagination class and its ordering attribute. if there is none, we sort by id
18
+ __paginator_ordering = getattr(getattr(view, "pagination_class", 0), "ordering", ["id"])
19
+ if isinstance(__paginator_ordering, str):
20
+ __paginator_ordering = [__paginator_ordering]
21
+
22
+ ordering_excluded_fields = getattr(view, "filterset_ordering_exclude", [])
23
+ ordering_default = getattr(view, "filterset_ordering_default", __paginator_ordering)
24
+
25
+ class MyOrderingFilter(filters.OrderingFilter):
26
+ def get_ordering(self, value):
27
+ if not value:
28
+ return ListWithFields(list(self.param_map.keys()), ordering_default)
29
+ return ListWithFields(list(self.param_map.keys()), [self.get_ordering_value(param) for param in value])
30
+
31
+ class FilterSetApplied(filters.FilterSet):
32
+ ordering = MyOrderingFilter(
33
+ fields=[f.name for f in queryset.model._meta.fields if f.name not in ordering_excluded_fields]
34
+ )
35
+
36
+ return FilterSetApplied
37
+
38
+ def get_ordering(self, request, queryset, view):
39
+ filterset = self.get_filterset(request, queryset, view)
40
+ filterset.is_valid()
41
+ ordering_filters = {k: v for k, v in filterset.filters.items() if isinstance(v, filters.OrderingFilter)}
42
+ for name, value in filterset.form.cleaned_data.items():
43
+ if name in ordering_filters:
44
+ return ordering_filters[name].get_ordering(value)
File without changes
@@ -0,0 +1,16 @@
1
+ # flake8: noqa
2
+
3
+ from .action import ActionMixin
4
+ from .allow_tags import AllowTagsMixin
5
+ from .enable_copy import EnableCopyMixin
6
+ from .file_field import FileFieldMixin
7
+ from .help_text import FieldHelpTextMixin
8
+ from .implicit_hidden import HiddenFieldMixin
9
+ from .implicit_natural_date import DateFieldMixin, DateTimeFieldMixin, TimeFieldMixin
10
+ from .null_choice import NullChoiceMixin
11
+ from .null_value import NullValueMixin
12
+ from .password_field import PasswordFieldMixin
13
+ from .related_field_ajax import RelatedFieldAJAXMixin
14
+ from .render import DisplayMode, RenderMixin
15
+ from .rtf_field import RTFFieldMixin
16
+ from .single_choice import SingleChoiceMixin
@@ -0,0 +1,14 @@
1
+ from dynamicforms_legacy.action import Actions
2
+
3
+
4
+ class ActionMixin(object):
5
+ """
6
+ Used in fields allowing declaration of actions that happen when field values change
7
+ """
8
+
9
+ def __init__(self, *args, actions: Actions = None, **kwargs):
10
+ super().__init__(*args, **kwargs)
11
+ act = actions or Actions(None)
12
+ act.actions.extend(getattr(self, "actions", Actions()).actions)
13
+ # Obtain a personalised list of actions
14
+ self.actions = act.get_resolved_copy(self)
@@ -0,0 +1,85 @@
1
+ # TODO: Implementation of this mixin is wrong for ChoiceField (single choice variant):
2
+ # The ChoiceField will return data in an array even for (Single)ChoiceField. Select2 handles this, maybe even requires
3
+ # this, but some other component might not
4
+ import ast
5
+
6
+ from rest_framework.fields import ChoiceField, MultipleChoiceField
7
+
8
+
9
+ class DenormalisedArray(list):
10
+ def __init__(self, lst, field):
11
+ self.field = field
12
+ if lst is not None and isinstance(lst, str) and isinstance(field, MultipleChoiceField):
13
+ for itm in ast.literal_eval(lst):
14
+ self.append(itm)
15
+
16
+ elif lst is not None and isinstance(lst, list):
17
+ for itm in lst:
18
+ self.append(itm)
19
+
20
+ elif lst is not None:
21
+ self.append(lst)
22
+
23
+ def __str__(self):
24
+ choices = self.field.choices
25
+ ret = ""
26
+ count = 0
27
+ for item in self:
28
+ ret += ", " if count else ""
29
+ if item in choices:
30
+ ret += choices[item]
31
+ else:
32
+ ret += item
33
+ count += 1
34
+ return ret
35
+
36
+
37
+ class AllowTagsMixin(object):
38
+ def __init__(self, *args, allow_tags=False, **kwargs):
39
+ super().__init__(*args, **kwargs)
40
+ self.allow_tags = allow_tags or False
41
+
42
+ def to_internal_value(self, data):
43
+ """
44
+ Validator is taken directly from DRF and updated, to check for allow_tags.
45
+ Option allow_tags enables adding value that is not on the options list.
46
+ """
47
+ if not self.allow_tags:
48
+ data = super().to_internal_value(data)
49
+
50
+ if isinstance(self, MultipleChoiceField):
51
+ if isinstance(data, str) or not hasattr(data, "__iter__"):
52
+ self.fail("not_a_list", input_type=type(data).__name__)
53
+ if not self.allow_empty and len(data) == 0:
54
+ self.fail("empty")
55
+
56
+ return data
57
+
58
+ elif isinstance(self, ChoiceField) and not isinstance(self, MultipleChoiceField):
59
+ if data == "" and self.allow_blank:
60
+ return ""
61
+
62
+ try:
63
+ if not self.allow_tags:
64
+ return self.choice_strings_to_values[str(data)]
65
+
66
+ return data
67
+ except KeyError:
68
+ self.fail("invalid_choice", input=data)
69
+
70
+ def to_representation(self, value, row_data=None):
71
+ if isinstance(self, ChoiceField) and self.allow_tags and value:
72
+ return DenormalisedArray(value, self)
73
+
74
+ return super().to_representation(value, row_data)
75
+
76
+ def iter_options_bound(self, value):
77
+ # noinspection PyUnresolvedReferences
78
+ if isinstance(value, list):
79
+ for item in value:
80
+ if item not in self.choices.keys():
81
+ self.grouped_choices[item] = item
82
+ elif isinstance(value, str) and self.allow_tags and value not in self.choices.keys():
83
+ self.grouped_choices[value] = value
84
+
85
+ return super().iter_options_bound(value)
@@ -0,0 +1,4 @@
1
+ class EnableCopyMixin(object):
2
+ def __init__(self, *args, enable_copy=False, **kwargs):
3
+ super().__init__(*args, **kwargs)
4
+ self.enable_copy = enable_copy or False
@@ -0,0 +1,5 @@
1
+ class FileFieldMixin(object):
2
+ def __init__(self, **kwargs) -> None:
3
+ super().__init__(**kwargs)
4
+ # noinspection PyUnresolvedReferences
5
+ self.style.setdefault("no_filter", True)
@@ -0,0 +1,38 @@
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)
@@ -0,0 +1,7 @@
1
+ from .render import DisplayMode, RenderMixin
2
+
3
+
4
+ class HiddenFieldMixin(RenderMixin):
5
+ def __init__(self, *args, **kwargs):
6
+ kwargs.setdefault("display_table", DisplayMode.SUPPRESS)
7
+ super().__init__(*args, **kwargs)
@@ -0,0 +1,86 @@
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 .render import RenderMixin
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 not None:
31
+ output_format = getattr(self, "table_format", None)
32
+ if output_format is not None:
33
+ if re.match(r"%N:\d+", output_format):
34
+ imported = False
35
+ try:
36
+ # This library (will-natural) is used because is the only one we could find that adds text
37
+ # saying when is this datetime ("ago" or "from now") and have the possibility to set max
38
+ # precision
39
+
40
+ # noinspection PyPackageRequirements
41
+ from natural.date import duration
42
+
43
+ imported = True
44
+ except:
45
+ print("Install library for natural presentation of date (pip install will-natural)")
46
+
47
+ if imported:
48
+ if isinstance(self, DateField):
49
+ now = timezone.now().date()
50
+ elif isinstance(self, TimeField):
51
+ now = datetime.now()
52
+ value = datetime.now().replace(
53
+ hour=value.hour, minute=value.minute, second=value.second, microsecond=value.microsecond
54
+ )
55
+ else:
56
+ now = timezone.now()
57
+
58
+ # noinspection PyUnboundLocalVariable
59
+ return duration(value, now=now, precision=int(output_format.split(":")[1]))
60
+ else:
61
+ # Invoke DRF field's to_representation
62
+ global_format = getattr(self, "format", None)
63
+ setattr(self, "format", output_format)
64
+ # noinspection PySuperArguments
65
+ value = super(RenderMixin, self).to_representation(value) # Skip RenderMixin
66
+ setattr(self, "format", global_format)
67
+ return value or ""
68
+
69
+ return localize(value)
70
+
71
+ return super().render_to_table(value, row_data)
72
+
73
+
74
+ class TimeFieldMixin(NaturalDateTimeMixin):
75
+ def __init__(self, *args, table_format: str = None, **kwargs) -> None:
76
+ super().__init__(*args, table_format=table_format, **kwargs)
77
+
78
+
79
+ class DateFieldMixin(NaturalDateTimeMixin):
80
+ def __init__(self, *args, table_format: str = None, **kwargs) -> None:
81
+ super().__init__(*args, table_format=table_format, **kwargs)
82
+
83
+
84
+ class DateTimeFieldMixin(NaturalDateTimeMixin):
85
+ def __init__(self, *args, table_format: str = None, **kwargs) -> None:
86
+ super().__init__(*args, table_format=table_format, **kwargs)
@@ -0,0 +1,26 @@
1
+ from rest_framework.fields import ChoiceField
2
+
3
+
4
+ class NullChoiceMixin(object):
5
+ """
6
+ Declaring ChoiceField's null_choice_text
7
+ (what should be rendered for "no selection"; DRF's default is ---------)
8
+ We also support user declaring a choice with None value and a different text (see hidden fields example)
9
+ """
10
+
11
+ @property
12
+ def null_choice_text(self):
13
+ res = "--------"
14
+ if isinstance(self, ChoiceField) and not getattr(self, "get_queryset", None):
15
+ # Only do this for true ChoiceFields. RelatedFields would run a query here
16
+ # Possibly we will at some point have to enable this for relatedfields too
17
+ for opt in self.iter_options():
18
+ if opt.start_option_group or opt.end_option_group:
19
+ pass
20
+ elif opt.value is None:
21
+ res = opt.display_text
22
+ return res
23
+
24
+ def iter_options_bound(self, value):
25
+ # noinspection PyUnresolvedReferences
26
+ return super().iter_options()
@@ -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,19 @@
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
+ self._password_field = value
8
+ if value:
9
+ self.display_table = "DisplayMode.SUPRESS"
10
+ self.style.update(input_type="password")
11
+ else:
12
+ try:
13
+ self.style["input_type"]
14
+ self.style.update(input_type="text")
15
+ self.display_table = "DisplayMode.FULL"
16
+ except:
17
+ pass
18
+
19
+ password_field = property(lambda self: self._password_field, set_password_field)
@@ -0,0 +1,58 @@
1
+ from typing import Optional
2
+
3
+
4
+ class RelatedFieldAJAXMixin(object):
5
+ def __init__(
6
+ self,
7
+ *args,
8
+ url_reverse: Optional[str] = None,
9
+ placeholder: Optional[str] = None,
10
+ additional_parameters: Optional[dict] = None,
11
+ query_field: str = "query",
12
+ value_field: str = "choice_id",
13
+ text_field: str = "choice_text",
14
+ **kwargs
15
+ ):
16
+ """
17
+ Allows us to use AJAX to populate select2 options instead of pre-populating at render time
18
+
19
+ :param args:
20
+ :param url_reverse: reverse url to ViewSet providing the JSON data
21
+ :param placeholder: select2 placeholder to display until user selects a value
22
+ :param additional_parameters: additional parameters to be sent to ViewSet as part of the query
23
+ :param query_field: field against which user search will be performed
24
+ :param kwargs:
25
+ """
26
+ super().__init__(*args, **kwargs)
27
+ self.url_reverse = url_reverse
28
+ self.placeholder = placeholder
29
+ self.additional_parameters = additional_parameters
30
+ self.query_field = query_field
31
+ self.value_field = value_field
32
+ self.text_field = text_field
33
+
34
+ @property
35
+ def additional_parameters_urlencoded(self):
36
+ from django.utils.http import urlencode
37
+
38
+ return "?" + urlencode(self.additional_parameters)
39
+
40
+ # noinspection PyUnresolvedReferences
41
+ def iter_options_bound(self, value):
42
+ if self.url_reverse:
43
+ if value is None:
44
+ return [dict(value="", display_text=self.placeholder)]
45
+ try:
46
+ if hasattr(self, "child_relation"):
47
+ itm = self.child_relation
48
+ qry = itm.get_queryset()
49
+ qry = qry.filter(pk__in=value)
50
+ else:
51
+ itm = self
52
+ qry = self.get_queryset()
53
+ qry = qry.filter(pk=value)
54
+
55
+ return [dict(value=rec.id, display_text=itm.display_value(rec)) for rec in qry.all()]
56
+ except:
57
+ return []
58
+ return super().iter_options()
@@ -0,0 +1,203 @@
1
+ import uuid as uuid_module
2
+
3
+ from enum import IntEnum
4
+ from typing import Hashable
5
+
6
+ from rest_framework.fields import Field as DrfField
7
+ from rest_framework.relations import ManyRelatedField, PKOnlyObject, RelatedField
8
+ from rest_framework.serializers import ListSerializer
9
+ from rest_framework.templatetags import rest_framework as drftt
10
+
11
+ from dynamicforms_legacy.settings import DYNAMICFORMS
12
+
13
+
14
+ class DisplayMode(IntEnum):
15
+ SUPPRESS = 1 # Field will be entirely suppressed. it will not render (not even to JSON) and will not parse for PUT
16
+ HIDDEN = 5 # Field will render as <input type="hidden"> or <tr data-field_name>
17
+ INVISIBLE = 8 # Field will render completely, but with display: none. Equal to setting its style = {display: none}
18
+ FULL = 10 # Field will render completely
19
+
20
+
21
+ class RenderMixin(object):
22
+ """
23
+ Is used in fields and serializers, so every field and serializer gets its unique id. Also to specify where and how
24
+ fields should render.
25
+
26
+ In form where serializer is used, id is serializers uuid. Table with list of records has id »list-serializer.uuid«,
27
+ in dialog id is »dialog-{serializer.uuid}« and save button's id on dialog is »save-{serializer.uuid}«
28
+
29
+ Similar for fields: All inputs in HTML get id from field.uuid. Div that contains all that belongs to the field has
30
+ »container-{field.uuid}« for id, label has »label-{field.uuid}« and help text (if exists) has »help-{field.uuid}«
31
+ for id.
32
+
33
+ Used for rendering individual field to table view
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ *args,
39
+ uuid: uuid_module.UUID = None,
40
+ display: DisplayMode = None, # None == Leave at default
41
+ display_table: DisplayMode = None, # None == Leave at default
42
+ display_form: DisplayMode = None, # None == Leave at default
43
+ table_classes: str = "",
44
+ **kwargs
45
+ ):
46
+ """
47
+
48
+ :param args: passed on to inherited constructors
49
+ :param uuid: custom specified field UUID. if not specified, it will be assigned automatically
50
+ :param display: see DisplayMode enum. Specifies how field will render. Leave at None for default (FULL)
51
+ display_form and display_table also accepted for better granularity
52
+ :param table_classes: css classes to add to the table column
53
+ :param kwargs: passed on to inherited constructors
54
+ """
55
+ super().__init__(*args, **kwargs)
56
+ self.uuid = uuid or uuid_module.uuid1()
57
+ # noinspection PyUnresolvedReferences
58
+ self.display_table = (
59
+ display_table
60
+ or display
61
+ or (DisplayMode.FULL if not getattr(self, "write_only", False) else DisplayMode.SUPPRESS)
62
+ )
63
+ self.display_form = display_form or display or DisplayMode.FULL
64
+ self.table_classes = table_classes
65
+
66
+ @property
67
+ def is_rendering_to_list(self):
68
+ """
69
+ reports whether we are currently rendering to table or to single record
70
+ :return:
71
+ """
72
+ try:
73
+ # noinspection PyUnresolvedReferences
74
+ base = self.parent
75
+ while base:
76
+ if isinstance(base, ListSerializer):
77
+ # If fields parent's parent is the ListSerializer, we're rendering to list
78
+ return True
79
+ base = base.parent
80
+ except:
81
+ pass
82
+ return False
83
+
84
+ @property
85
+ def is_rendering_to_html(self):
86
+ try:
87
+ # noinspection PyUnresolvedReferences
88
+ return self.context["format"] == "html"
89
+ except:
90
+ pass
91
+ return False
92
+
93
+ # noinspection PyUnresolvedReferences
94
+ def use_pk_only_optimization(self):
95
+ """
96
+ Overrides DRF RelatedField's method. It True is returned then value passed for serialisation will be PK value
97
+ only, not entire relation object. we don't want that because then we can't resolve the value into something more
98
+ human-readable
99
+ :return:
100
+ """
101
+ if self.is_rendering_to_list and self.is_rendering_to_html:
102
+ return False
103
+ return super().use_pk_only_optimization()
104
+
105
+ # noinspection PyUnresolvedReferences
106
+ def to_representation(self, value, row_data=None):
107
+ """
108
+ Overrides DRF Field's to_representation.
109
+ Note that this is also called for the entire record as well as the serializer also is a Field descendant
110
+ :param value: value to serialize
111
+ :param row_data: instance with row data
112
+ :return: serialized value
113
+ """
114
+ if self.is_rendering_to_list and self.is_rendering_to_html and self.display_table != DisplayMode.HIDDEN:
115
+ # if rentering to html table, let's try to resolve any lookups
116
+ # hidden fields will render to tr data-field_name attributes, so we maybe want to have ids, not text there
117
+ # we have discussed alternatives but decided that right now a more complete solution is not needed
118
+ return self.render_to_table(value, row_data)
119
+
120
+ check_for_none = value.pk if isinstance(value, PKOnlyObject) else value
121
+ if check_for_none is None:
122
+ return None
123
+
124
+ return super().to_representation(value)
125
+
126
+ def set_display(self, value):
127
+ if isinstance(value, tuple):
128
+ self.display_form, self.display_table = value
129
+ else:
130
+ self.display_form = self.display_table = value
131
+
132
+ display = property(lambda self: self.display_form, set_display)
133
+
134
+ # noinspection PyUnusedLocal, PyUnresolvedReferences
135
+ def render_to_table(self, value, row_data):
136
+ """
137
+ Renders field value for table view
138
+
139
+ :param value: field value
140
+ :param row_data: data for entire row (for more complex renderers)
141
+ :return: rendered value for table view
142
+ """
143
+ get_queryset = getattr(self, "get_queryset", None)
144
+
145
+ if isinstance(self, ManyRelatedField):
146
+ # Hm, not sure if this is the final thing to do: an example of this field is in
147
+ # ALC plane editor (modes of takeoff). However, value is a queryset here. There seem to still be DB queries
148
+ # However, in the example I have, the problem is solved by doing prefetch_related on the m2m relation
149
+ cr = self.child_relation
150
+ return ", ".join((cr.display_value(item) for item in value))
151
+ # return ', '.join((cr.display_value(item) for item in cr.get_queryset().filter(pk__in=value)))
152
+ elif isinstance(self, RelatedField) or get_queryset:
153
+ return self.display_value(value)
154
+ else:
155
+ choices = getattr(self, "choices", {})
156
+
157
+ # Now that we got our choices for related & choice fields, let's first get the value as it would be by DRF
158
+ check_for_none = value.pk if isinstance(value, PKOnlyObject) else value
159
+ if check_for_none is None:
160
+ value = None
161
+ else:
162
+ value = super().to_representation(value)
163
+
164
+ if isinstance(value, Hashable) and value in choices:
165
+ # choice field: let's render display names, not values
166
+ value = choices[value]
167
+ if value is None:
168
+ return DYNAMICFORMS.null_text_table
169
+
170
+ return drftt.format_value(value)
171
+
172
+ def validate_empty_values(self: DrfField, data):
173
+ # noinspection PyUnresolvedReferences
174
+ res = super().validate_empty_values(data)
175
+
176
+ # This is to fix a problem with calculated fields which was only solved in DRF 3.10.
177
+ # Forces validation and inclusion of the field into validated data. See comment in original function.
178
+ if res == (True, None) and data is None and self.source == "*":
179
+ return False, None
180
+ return res
181
+
182
+ def ordering(self):
183
+ ordering = []
184
+ if hasattr(self, "context") and "view" in getattr(self, "context"):
185
+ ordering = getattr(self.context["view"], "ordering", None)
186
+
187
+ if getattr(self, "field_name") not in getattr(ordering, "fields", []):
188
+ return ""
189
+
190
+ index = -1
191
+ direction_asc = True
192
+ for idx, o in enumerate(ordering):
193
+ if o.startswith("-"):
194
+ direction_asc = False
195
+ o = o[1:]
196
+ if o == getattr(self, "field_name"):
197
+ index = idx
198
+
199
+ if index > -1:
200
+ direction_class = ("asc" if direction_asc else "desc") + " seg-%d" % (index + 1)
201
+ else:
202
+ direction_class = "unsorted"
203
+ return "ordering " + direction_class