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,142 @@
1
+ import sys
2
+
3
+ from typing import Optional
4
+
5
+ from django.db.models.manager import BaseManager
6
+ from django.urls import reverse
7
+ from django.utils.http import urlencode
8
+ from rest_framework.relations import RelatedField
9
+
10
+
11
+ class RelatedFieldAJAXMixin(object):
12
+ def __init__(
13
+ self,
14
+ *args,
15
+ url_reverse: Optional[str] = None,
16
+ placeholder: Optional[str] = None,
17
+ additional_parameters: Optional[dict] = None,
18
+ query_field: str = "query",
19
+ value_field: str = "choice_id",
20
+ text_field: str = "choice_text",
21
+ **kwargs,
22
+ ):
23
+ """
24
+ Allows us to use AJAX to populate select2 options instead of pre-populating at render time
25
+
26
+ :param args:
27
+ :param url_reverse: reverse url to ViewSet providing the JSON data
28
+ :param placeholder: select2 placeholder to display until user selects a value
29
+ :param additional_parameters: additional parameters to be sent to ViewSet as part of the query
30
+ :param query_field: field against which user search will be performed
31
+ :param kwargs:
32
+ """
33
+ super().__init__(*args, **kwargs)
34
+ self.url_reverse = url_reverse
35
+ self.placeholder = placeholder
36
+ self.additional_parameters = additional_parameters
37
+ self.query_field = query_field
38
+ self.value_field = value_field
39
+ self.text_field = text_field
40
+
41
+ @property
42
+ def additional_parameters_urlencoded(self):
43
+ return urlencode(self.additional_parameters) if self.additional_parameters else None
44
+
45
+ # noinspection PyUnresolvedReferences
46
+ def iter_options_bound(self, value):
47
+ if self.url_reverse:
48
+ if value is None:
49
+ return [dict(value="", display_text=self.placeholder)]
50
+ try:
51
+ if hasattr(self, "child_relation"):
52
+ itm = self.child_relation
53
+ qry = itm.get_queryset()
54
+ if value != "__all__":
55
+ qry = qry.filter(pk__in=value)
56
+ else:
57
+ itm = self
58
+ qry = self.get_queryset()
59
+ if value != "__all__":
60
+ qry = qry.filter(pk=value)
61
+
62
+ return [dict(value=rec.id, display_text=itm.display_value(rec)) for rec in qry.all()]
63
+ except:
64
+ return []
65
+ return super().iter_options()
66
+
67
+ def as_component_def(self) -> dict:
68
+ try:
69
+ res = super().as_component_def() # noqa
70
+ except AttributeError:
71
+ res = dict()
72
+ if self.url_reverse:
73
+ ajax = dict(
74
+ ajax=dict(
75
+ url_reverse=reverse(self.url_reverse, kwargs=dict(format="json")),
76
+ placeholder=self.placeholder,
77
+ additional_parameters=self.additional_parameters_urlencoded,
78
+ query_field=self.query_field,
79
+ value_field=self.value_field,
80
+ text_field=self.text_field,
81
+ )
82
+ )
83
+ res.update(ajax)
84
+ qs_count = self.get_queryset().count()
85
+
86
+ if qs_count and qs_count <= 10:
87
+ res.update(
88
+ choices=map(
89
+ lambda option: dict(id=option["value"], text=option["display_text"]),
90
+ self.iter_options_bound("__all__"),
91
+ )
92
+ )
93
+ else:
94
+ res.update(
95
+ choices=list(map(
96
+ lambda option: dict(id=option.value, text=option.display_text), self.iter_options_bound(None)
97
+ )
98
+ ))
99
+ if len(res["choices"]) > 100:
100
+ print(
101
+ f"WARNING: {self.parent.__class__.__name__}.{self.field_name} should be converted to AJAX.",
102
+ file=sys.stderr
103
+ )
104
+ return res
105
+
106
+ # noinspection PyUnusedLocal, PyUnresolvedReferences
107
+ def render_to_table(self, value, row_data):
108
+ """
109
+ Renders field value for table view :if rendering to html table, let's try to resolve any lookups
110
+ hidden fields will render to tr data-field_name attributes, so we maybe want to have ids, not text there,
111
+ but that's up to front end to decide
112
+
113
+ :param value: field value
114
+ :param row_data: data for entire row (for more complex renderers)
115
+ :return: rendered value for table view
116
+ """
117
+ from dynamicforms.fields import ManyRelatedField
118
+
119
+ if isinstance(self, ManyRelatedField):
120
+ # Hm, not sure if this is the final thing to do: an example of this field is in
121
+ # ALC plane editor (modes of takeoff). However, value is a queryset here. There seem to still be DB queries
122
+ # However, in the example I have, the problem is solved by doing prefetch_related on the m2m relation
123
+ cr = self.child_relation
124
+ if isinstance(value, BaseManager):
125
+ value = value.all()
126
+ if value is None:
127
+ return ""
128
+ return ", ".join((cr.display_value(item) for item in value))
129
+ # return ', '.join((cr.display_value(item) for item in cr.get_queryset().filter(pk__in=value)))
130
+ elif isinstance(self, RelatedField):
131
+ return self.display_value(value)
132
+
133
+ return value
134
+
135
+ @classmethod
136
+ def many_init(cls, *args, **kwargs):
137
+ list_kwargs = {"child_relation": cls(*args, **kwargs)}
138
+ from dynamicforms.fields import ManyRelatedField
139
+
140
+ return ManyRelatedField(
141
+ display_form=kwargs.get("display_form"), display_table=kwargs.get("display_table"), **list_kwargs
142
+ )
@@ -0,0 +1,58 @@
1
+ from html.parser import HTMLParser
2
+
3
+
4
+ class RTFFieldMixin(object):
5
+ def __init__(self, *args, show_as_plain_text=False, plain_text_lines=3, plain_text_line_length=40, **kwargs):
6
+ super().__init__(*args, **kwargs)
7
+ self.parse = show_as_plain_text or False
8
+ self.max_lines = plain_text_lines
9
+ self.max_line_length = plain_text_line_length
10
+ self.parser = RTFFieldHTMLParser(max_lines=self.max_lines, max_line_length=self.max_line_length)
11
+
12
+ def to_representation(self, instance, row_data=None):
13
+ # TODO: why is this here and not in init?
14
+ self.style.update(input_type="input")
15
+ if not self.parent.is_filter:
16
+ self.render_params["form_component_name"] = "DCKEditor"
17
+
18
+ return super().to_representation(instance, row_data)
19
+
20
+ def render_to_table(self, value, row_data):
21
+ if not self.parent.is_filter and self.parse:
22
+ self.parser.feed(value)
23
+ instance = self.parser.to_string()
24
+ self.parser.reset()
25
+ return instance
26
+ return value
27
+
28
+
29
+ class RTFFieldHTMLParser(HTMLParser):
30
+ def __init__(self, max_lines, max_line_length):
31
+ self.text = []
32
+ self.curr_line_count = 1
33
+ self.curr_line = ""
34
+ self.max_lines = max_lines
35
+ self.max_line_length = max_line_length
36
+ super(RTFFieldHTMLParser, self).__init__()
37
+
38
+ def handle_data(self, data):
39
+ text = data.strip()
40
+ if self.curr_line_count <= self.max_lines:
41
+ if len(text) > 0:
42
+ self.curr_line += " " + text
43
+ line = self.curr_line[0 : self.max_line_length - 3]
44
+ if len(self.curr_line) >= self.max_line_length - 3:
45
+ line += "..."
46
+ line += "<br>"
47
+ self.text.append(line)
48
+ self.curr_line = ""
49
+ self.curr_line_count += 1
50
+
51
+ def reset(self):
52
+ self.text = []
53
+ self.curr_line_count = 1
54
+ self.curr_line = ""
55
+ super(RTFFieldHTMLParser, self).reset()
56
+
57
+ def to_string(self):
58
+ return "".join(self.text).strip()
dynamicforms/models.py ADDED
@@ -0,0 +1,5 @@
1
+ # supposedly required so that tests & stuff works
2
+
3
+ # from django.db import models
4
+
5
+ # Create your models here.
@@ -0,0 +1,33 @@
1
+ from enum import IntEnum
2
+
3
+
4
+ class IntChoiceEnum(IntEnum):
5
+ def __new__(cls, *args, **kwargs):
6
+ obj = int.__new__(cls, args[0])
7
+ obj._value_ = args[0]
8
+ return obj
9
+
10
+ # ignore the first param since it's already set by new
11
+ def __init__(self, _: str, description: str = None, icon: str = None, data: dict = None):
12
+ self._description_ = description
13
+ self._icon_ = icon
14
+ self._data_ = data
15
+
16
+ description = property(lambda self: self._description_)
17
+ icon = property(lambda self: self._icon_)
18
+ data = property(lambda self: self._data_)
19
+
20
+ @classmethod
21
+ def get_choices_tuple(cls):
22
+ return tuple((item.value, item.description) for item in cls)
23
+
24
+ @classmethod
25
+ def get_df_tuple(cls):
26
+ return tuple(
27
+ (item.value, item.description, item.icon) if item.icon is not None else (item.value, item.description)
28
+ for item in cls
29
+ )
30
+
31
+ @classmethod
32
+ def has_value(cls, value):
33
+ return value in cls.__members__.values()
@@ -0,0 +1,175 @@
1
+ import datetime
2
+ import json
3
+
4
+ import rest_framework.pagination as drf_p
5
+
6
+ from django.db.models import DurationField, F, Q
7
+
8
+
9
+ class CursorPagination(drf_p.CursorPagination):
10
+ ordering = "pk"
11
+ NONE_VALUE = "{`None`}"
12
+
13
+ # noinspection PyAttributeOutsideInit
14
+ def paginate_queryset(self, queryset, request, view=None):
15
+ self.page_size = self.get_page_size(request)
16
+ if not self.page_size:
17
+ return None
18
+
19
+ self.base_url = request.build_absolute_uri()
20
+ self.ordering = self.get_ordering(request, queryset, view)
21
+ if "id" not in self.ordering and "-id" not in self.ordering:
22
+ ordering = list(self.ordering)
23
+ ordering.append("id")
24
+ self.ordering = tuple(ordering)
25
+
26
+ self.cursor = self.decode_cursor(request)
27
+ if self.cursor is None:
28
+ (offset, reverse, current_position) = (0, False, None)
29
+ else:
30
+ (offset, reverse, current_position) = self.cursor
31
+ try:
32
+ if current_position:
33
+ current_position = json.loads(current_position)
34
+ except TypeError:
35
+ current_position = None
36
+
37
+ # Cursor pagination always enforces an ordering.
38
+ ordering = drf_p._reverse_ordering(self.ordering) if reverse else self.ordering
39
+ # Currently we force nulls_first in ascending order and nulls_last in descending order
40
+ ordering = [
41
+ getattr(F(field.lstrip("-")), "desc" if field.startswith("-") else "asc")(
42
+ **{"nulls_last" if field.startswith("-") else "nulls_first": True}
43
+ )
44
+ for field in ordering
45
+ ]
46
+ queryset = queryset.order_by(*ordering)
47
+
48
+ # If we have a cursor with a fixed position then filter by that.
49
+ if current_position is not None:
50
+
51
+ def filter_segment(segment_len):
52
+ kwargs = {}
53
+ args = []
54
+ for idx in range(segment_len):
55
+ order = self.ordering[idx]
56
+ is_reversed = order.startswith("-")
57
+ order_attr = order.lstrip("-")
58
+ attr_value = self.field_to_python(queryset, order_attr, current_position[order_attr])
59
+
60
+ # Test for: (cursor reversed) XOR (queryset reversed)
61
+ if idx < segment_len - 1:
62
+ # if this is the last field in this segment
63
+ if attr_value == self.NONE_VALUE:
64
+ kwargs[order_attr + "__isnull"] = True
65
+ else:
66
+ kwargs[order_attr] = attr_value
67
+ elif self.cursor.reverse != is_reversed:
68
+ if attr_value == self.NONE_VALUE:
69
+ # It is impossible to go lower than None... so we just omit this segment
70
+ return None
71
+ else:
72
+ args.append(Q(**{order_attr + "__lt": attr_value}) | Q(**{order_attr + "__isnull": True}))
73
+ else:
74
+ if attr_value == self.NONE_VALUE:
75
+ kwargs[order_attr + "__isnull"] = False
76
+ else:
77
+ kwargs[order_attr + "__gt"] = attr_value
78
+
79
+ return Q(**kwargs) & Q(*args)
80
+
81
+ fltr = Q()
82
+ for seg_len in range(len(self.ordering)):
83
+ new_segment = filter_segment(seg_len + 1)
84
+ if new_segment:
85
+ fltr = fltr | new_segment
86
+ queryset = queryset.filter(fltr)
87
+
88
+ # If we have an offset cursor then offset the entire page by that amount.
89
+ # We also always fetch an extra item in order to determine if there is a
90
+ # page following on from this one.
91
+ results = list(queryset[offset : offset + self.page_size + 1])
92
+ self.page = list(results[: self.page_size])
93
+ if not self.page:
94
+ # In rest_framework/pagination.py/get_previous_link is expected that self.page is not empty
95
+ # if cursor offset != 0
96
+ self.cursor = drf_p.Cursor(offset=0, reverse=False, position=None)
97
+
98
+ # DF: contrary to DRF's implementation we always provide the "prev" and "next" links as there may appear
99
+ # new records before / after the ones we already read. Mechanism for detecting whether they actually appeared
100
+ # is another matter altogether :)
101
+ if current_position is None and results:
102
+ self.cursor = drf_p.Cursor(offset=0, reverse=False, position=None)
103
+ current_position = self._get_position_from_instance(results[0], self.ordering)
104
+ elif isinstance(current_position, dict):
105
+ current_position = json.dumps(current_position)
106
+
107
+ # Determine the position of the final item following the page.
108
+ has_following_position = bool(results) # len(results) > len(self.page)
109
+
110
+ # DF: contrary to DRF's implementation we always provide the "next" link as there may appear new records after
111
+ # the last one read
112
+ following_position = self._get_position_from_instance(results[-1], self.ordering) if results else None
113
+
114
+ if reverse:
115
+ # If we have a reverse queryset, then the query ordering was in reverse
116
+ # so we need to reverse the items again before returning them to the user.
117
+ self.page = list(reversed(self.page))
118
+
119
+ # Determine next and previous positions for reverse cursors.
120
+ self.has_next = (current_position is not None) or (offset > 0)
121
+ self.has_previous = has_following_position
122
+ if self.has_next:
123
+ self.next_position = current_position
124
+ if self.has_previous:
125
+ self.previous_position = following_position
126
+ else:
127
+ # Determine next and previous positions for forward cursors.
128
+ self.has_next = has_following_position
129
+ self.has_previous = (current_position is not None) or (offset > 0)
130
+ if self.has_next:
131
+ self.next_position = following_position
132
+ if self.has_previous:
133
+ self.previous_position = current_position
134
+
135
+ # Display page controls in the browsable API if there is more
136
+ # than one page.
137
+ if (self.has_previous or self.has_next) and self.template is not None:
138
+ self.display_page_controls = True
139
+
140
+ return self.page
141
+
142
+ def _get_position_from_instance(self, instance, ordering):
143
+ def process_field(idx):
144
+ field_name = ordering[idx].lstrip("-")
145
+ attr = None
146
+ if isinstance(instance, dict):
147
+ attr = instance[field_name]
148
+ else:
149
+ field_name_list = field_name.split("__")
150
+ for fn in field_name_list:
151
+ attr = getattr(instance if attr is None else attr, fn)
152
+ return field_name, self.field_to_representation(attr)
153
+
154
+ return json.dumps({k: v for k, v in map(lambda idx: process_field(idx), range(len(ordering)))})
155
+
156
+ def field_to_representation(self, value):
157
+ """
158
+ Converts field value to something that will be later understood by QuerySet.filter()
159
+ Question: why does DRF not do this? is str(value) always enough?
160
+ """
161
+ if isinstance(value, datetime.timedelta):
162
+ return str(value.total_seconds())
163
+ elif value is None:
164
+ return self.NONE_VALUE
165
+ return str(value)
166
+
167
+ def field_to_python(self, queryset, order_attr, value):
168
+ if order_attr in queryset.query.annotations:
169
+ field = queryset.query.annotations.get(order_attr).field
170
+ else:
171
+ # noinspection PyProtectedMember
172
+ field = queryset.model._meta.get_field(order_attr)
173
+ if isinstance(field, DurationField):
174
+ return datetime.timedelta(seconds=float(value))
175
+ return value
@@ -0,0 +1,42 @@
1
+ import pathlib
2
+ import time
3
+
4
+ from typing import Optional
5
+ from uuid import uuid4
6
+
7
+ from django.conf import settings
8
+ from django.core.files.storage import FileSystemStorage
9
+ from django.core.files.uploadedfile import InMemoryUploadedFile
10
+ from django.http import HttpResponse, JsonResponse
11
+ from django.utils.translation import gettext as __
12
+ from rest_framework import status
13
+ from rest_framework.exceptions import ValidationError
14
+
15
+ UPLOADED_FILE_NAME_TIMESTAMP_SEPARATOR = "_timestamp_"
16
+ UPLOADED_FILE_NAME_UUID_SEPARATOR = "_uuid_"
17
+ UPLOADED_FILE_TMP_LOCATION = "df_tmp_files"
18
+
19
+ preuploaded_fs = FileSystemStorage(location=f"{settings.MEDIA_ROOT}/{UPLOADED_FILE_TMP_LOCATION}")
20
+
21
+
22
+ def preupload_file(request):
23
+ # if not DYNAMICFORMS.allow_anonymous_user_to_preupload_files and not request.user.is_authenticated:
24
+ # return HttpResponse(status=status.HTTP_401_UNAUTHORIZED)
25
+ if request.method == "POST":
26
+ uploaded_file: Optional[InMemoryUploadedFile] = request.FILES.get("file", None)
27
+ if not uploaded_file:
28
+ raise ValidationError(dict(file=["required"]))
29
+ file_identifier: str = str(uuid4())
30
+ try:
31
+ file_extension: str = pathlib.Path(uploaded_file.name).suffix
32
+ file_name: str = uploaded_file.name.replace(file_extension, "")
33
+ preuploaded_fs.save(
34
+ f"{file_name}{UPLOADED_FILE_NAME_UUID_SEPARATOR}{file_identifier}"
35
+ f"{UPLOADED_FILE_NAME_TIMESTAMP_SEPARATOR}{int(time.time())}"
36
+ f"{file_extension}",
37
+ uploaded_file,
38
+ )
39
+ return JsonResponse(dict(identifier=file_identifier))
40
+ except Exception:
41
+ return HttpResponse(status=status.HTTP_500_INTERNAL_SERVER_ERROR, content=__("File upload failed").encode())
42
+ return HttpResponse(status=status.HTTP_501_NOT_IMPLEMENTED)
@@ -0,0 +1,71 @@
1
+ from django.core.cache import cache
2
+ from django.http import HttpResponse
3
+ from rest_framework.renderers import JSONRenderer
4
+
5
+
6
+ def get_progress_value(request):
7
+ """
8
+ Returns operations progress to HTTP client
9
+
10
+ :param request:
11
+ :return: Progress in percentages (0 - 100) if progress is set else None
12
+ """
13
+ progress_key = get_progress_key(request)
14
+ value = None
15
+ if progress_key is not None:
16
+ cache_value = cache.get("df_progress.%s" % progress_key)
17
+ if cache_value is not None:
18
+ value = "%.2f" % cache.get("df_progress.%s" % progress_key)
19
+
20
+ return HttpResponse(
21
+ JSONRenderer().render(dict(value=value, comment=cache.get("df_progress_comment.%s" % progress_key))),
22
+ content_type="application/json",
23
+ )
24
+
25
+
26
+ def get_progress_key(request):
27
+ """
28
+ Gets progress/operation key under which operation progress will be stored in servers cache.
29
+ :param request:
30
+ :return: Progress key or None if there is no x_df_timestamp in request header
31
+ """
32
+ timestamp = request.META.get("HTTP_X_DF_TIMESTAMP", request.GET.get("x_df_timestamp", None))
33
+ if timestamp is not None:
34
+ return "%s|%s" % (timestamp, request.session.session_key)
35
+ return None
36
+
37
+
38
+ def set_progress_value(progress_key, value):
39
+ """
40
+ Updates operations progress
41
+
42
+ :param progress_key: Key under which operations progress is stored
43
+ :param value: Progress in percentages (0.00 - 100.00)
44
+ :return:
45
+ """
46
+ if progress_key is not None:
47
+ cache.set("df_progress.%s" % progress_key, value)
48
+
49
+
50
+ def add_progress_value(progress_key, value):
51
+ """
52
+ Updates operations progress by adding to existing progress value
53
+
54
+ :param progress_key: Key under which operations progress is stored
55
+ :param value: Progress in percentages (0.00 - 100.00)
56
+ :return:
57
+ """
58
+ if progress_key is not None:
59
+ cache.set("df_progress.%s" % progress_key, cache.get("df_progress.%s" % progress_key, 0) + value)
60
+
61
+
62
+ def set_progress_comment(progress_key, value):
63
+ """
64
+ Updates operations comment
65
+
66
+ :param progress_key: Key under which operations progress is stored
67
+ :param value: comment
68
+ :return:
69
+ """
70
+ if progress_key is not None:
71
+ cache.set("df_progress_comment.%s" % progress_key, value)
@@ -0,0 +1 @@
1
+ from .template_render import ComponentDefRenderer # noqa: F401
@@ -0,0 +1,36 @@
1
+ from typing import Optional, Set
2
+
3
+ from rest_framework.routers import DefaultRouter, Route
4
+
5
+ from dynamicforms.viewsets import SingleRecordViewSet
6
+
7
+
8
+ class DFRouter(DefaultRouter):
9
+ single_record_registrations: Set[int] = set()
10
+
11
+ def register_single_record(self, prefix: str, viewset: SingleRecordViewSet, basename: Optional[str] = None):
12
+ """
13
+ Register a single-record viewset with the router.
14
+ """
15
+ self.single_record_registrations.add(id(viewset))
16
+ self.register(prefix, viewset, basename)
17
+
18
+ def get_routes(self, viewset):
19
+ if id(viewset) in self.single_record_registrations:
20
+ # these routes are copied from SimpleRouter, but modified so that they don't ask for a parameter
21
+ return [
22
+ Route(
23
+ url=r"^{prefix}{trailing_slash}$",
24
+ mapping={
25
+ "post": "create",
26
+ "get": "retrieve",
27
+ "put": "update",
28
+ "patch": "partial_update",
29
+ "delete": "destroy",
30
+ },
31
+ name="{basename}-detail",
32
+ detail=True,
33
+ initkwargs={"suffix": "Instance"},
34
+ ),
35
+ ]
36
+ return super().get_routes(viewset)