django-advanced-modals 1.0.0__tar.gz

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 (179) hide show
  1. django_advanced_modals-1.0.0/LICENSE +21 -0
  2. django_advanced_modals-1.0.0/MANIFEST.in +3 -0
  3. django_advanced_modals-1.0.0/PKG-INFO +531 -0
  4. django_advanced_modals-1.0.0/django_advanced_modals.egg-info/PKG-INFO +531 -0
  5. django_advanced_modals-1.0.0/django_advanced_modals.egg-info/SOURCES.txt +177 -0
  6. django_advanced_modals-1.0.0/django_advanced_modals.egg-info/dependency_links.txt +1 -0
  7. django_advanced_modals-1.0.0/django_advanced_modals.egg-info/requires.txt +4 -0
  8. django_advanced_modals-1.0.0/django_advanced_modals.egg-info/top_level.txt +1 -0
  9. django_advanced_modals-1.0.0/django_modals/__init__.py +0 -0
  10. django_advanced_modals-1.0.0/django_modals/apps.py +5 -0
  11. django_advanced_modals-1.0.0/django_modals/datatables.py +62 -0
  12. django_advanced_modals-1.0.0/django_modals/decorators.py +76 -0
  13. django_advanced_modals-1.0.0/django_modals/fields.py +161 -0
  14. django_advanced_modals-1.0.0/django_modals/form_helpers.py +76 -0
  15. django_advanced_modals-1.0.0/django_modals/forms.py +340 -0
  16. django_advanced_modals-1.0.0/django_modals/helper.py +170 -0
  17. django_advanced_modals-1.0.0/django_modals/includes.py +64 -0
  18. django_advanced_modals-1.0.0/django_modals/messages.py +44 -0
  19. django_advanced_modals-1.0.0/django_modals/mixins.py +24 -0
  20. django_advanced_modals-1.0.0/django_modals/modal_upload.py +37 -0
  21. django_advanced_modals-1.0.0/django_modals/modals/__init__.py +6 -0
  22. django_advanced_modals-1.0.0/django_modals/modals/datatables.py +42 -0
  23. django_advanced_modals-1.0.0/django_modals/modals/form_modals.py +273 -0
  24. django_advanced_modals-1.0.0/django_modals/modals/formset.py +112 -0
  25. django_advanced_modals-1.0.0/django_modals/modals/json_mixin.py +108 -0
  26. django_advanced_modals-1.0.0/django_modals/modals/modals.py +234 -0
  27. django_advanced_modals-1.0.0/django_modals/modals/multi_form.py +239 -0
  28. django_advanced_modals-1.0.0/django_modals/modals/multi_form_formsets.py +121 -0
  29. django_advanced_modals-1.0.0/django_modals/model_fields/__init__.py +0 -0
  30. django_advanced_modals-1.0.0/django_modals/model_fields/colour.py +18 -0
  31. django_advanced_modals-1.0.0/django_modals/processes.py +40 -0
  32. django_advanced_modals-1.0.0/django_modals/static/django_modals/css/bootstrap4-toggle.min.css +10 -0
  33. django_advanced_modals-1.0.0/django_modals/static/django_modals/css/modals.css +486 -0
  34. django_advanced_modals-1.0.0/django_modals/static/django_modals/css/select2.min.css +1 -0
  35. django_advanced_modals-1.0.0/django_modals/static/django_modals/jquery-month-picker/css/MonthPicker.min.css +1 -0
  36. django_advanced_modals-1.0.0/django_modals/static/django_modals/jquery-month-picker/js/MonthPicker.min.js +1 -0
  37. django_advanced_modals-1.0.0/django_modals/static/django_modals/jquery_ui_limited/css/images/ui-icons_444444_256x240.png +0 -0
  38. django_advanced_modals-1.0.0/django_modals/static/django_modals/jquery_ui_limited/css/images/ui-icons_555555_256x240.png +0 -0
  39. django_advanced_modals-1.0.0/django_modals/static/django_modals/jquery_ui_limited/css/images/ui-icons_777620_256x240.png +0 -0
  40. django_advanced_modals-1.0.0/django_modals/static/django_modals/jquery_ui_limited/css/images/ui-icons_777777_256x240.png +0 -0
  41. django_advanced_modals-1.0.0/django_modals/static/django_modals/jquery_ui_limited/css/images/ui-icons_cc0000_256x240.png +0 -0
  42. django_advanced_modals-1.0.0/django_modals/static/django_modals/jquery_ui_limited/css/images/ui-icons_ffffff_256x240.png +0 -0
  43. django_advanced_modals-1.0.0/django_modals/static/django_modals/jquery_ui_limited/css/jquery-ui.min.css +7 -0
  44. django_advanced_modals-1.0.0/django_modals/static/django_modals/jquery_ui_limited/js/jquery-ui.min.js +6 -0
  45. django_advanced_modals-1.0.0/django_modals/static/django_modals/js/bootstrap4-toggle.min.js +11 -0
  46. django_advanced_modals-1.0.0/django_modals/static/django_modals/js/bootstrap4-toggle.min.js.map +1 -0
  47. django_advanced_modals-1.0.0/django_modals/static/django_modals/js/jquery.formset.js +289 -0
  48. django_advanced_modals-1.0.0/django_modals/static/django_modals/js/modals.js +464 -0
  49. django_advanced_modals-1.0.0/django_modals/static/django_modals/js/modals.min.js +1 -0
  50. django_advanced_modals-1.0.0/django_modals/static/django_modals/js/modals_legacy.js +552 -0
  51. django_advanced_modals-1.0.0/django_modals/static/django_modals/js/select2.min.js +2 -0
  52. django_advanced_modals-1.0.0/django_modals/static/django_modals/js/widget_select2.js +149 -0
  53. django_advanced_modals-1.0.0/django_modals/static/django_modals/pickr/css/classic.min.css +3 -0
  54. django_advanced_modals-1.0.0/django_modals/static/django_modals/pickr/css/monolith.min.css +3 -0
  55. django_advanced_modals-1.0.0/django_modals/static/django_modals/pickr/css/nano.min.css +3 -0
  56. django_advanced_modals-1.0.0/django_modals/static/django_modals/pickr/js/pickr.es5.min.js +4 -0
  57. django_advanced_modals-1.0.0/django_modals/static/django_modals/pickr/js/pickr.es5.min.js.map +1 -0
  58. django_advanced_modals-1.0.0/django_modals/static/django_modals/pickr/js/pickr.min.js +3 -0
  59. django_advanced_modals-1.0.0/django_modals/static/django_modals/pickr/js/pickr.min.js.map +1 -0
  60. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/jquery.tinymce.min.js +1 -0
  61. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/langs/readme.md +3 -0
  62. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/license.txt +504 -0
  63. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/advlist/plugin.min.js +1 -0
  64. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/anchor/plugin.min.js +1 -0
  65. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/autolink/plugin.min.js +1 -0
  66. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/autoresize/plugin.min.js +1 -0
  67. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/autosave/plugin.min.js +1 -0
  68. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/bbcode/plugin.min.js +1 -0
  69. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/charmap/plugin.min.js +1 -0
  70. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/code/plugin.min.js +1 -0
  71. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/codesample/css/prism.css +138 -0
  72. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/codesample/plugin.min.js +1 -0
  73. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/colorpicker/plugin.min.js +1 -0
  74. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/contextmenu/plugin.min.js +1 -0
  75. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/directionality/plugin.min.js +1 -0
  76. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-cool.gif +0 -0
  77. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-cry.gif +0 -0
  78. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-embarassed.gif +0 -0
  79. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif +0 -0
  80. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-frown.gif +0 -0
  81. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-innocent.gif +0 -0
  82. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-kiss.gif +0 -0
  83. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-laughing.gif +0 -0
  84. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-money-mouth.gif +0 -0
  85. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-sealed.gif +0 -0
  86. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-smile.gif +0 -0
  87. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-surprised.gif +0 -0
  88. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-tongue-out.gif +0 -0
  89. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-undecided.gif +0 -0
  90. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-wink.gif +0 -0
  91. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/img/smiley-yell.gif +0 -0
  92. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/emoticons/plugin.min.js +1 -0
  93. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/fullpage/plugin.min.js +1 -0
  94. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/fullscreen/plugin.min.js +1 -0
  95. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/help/img/logo.png +0 -0
  96. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/help/plugin.min.js +1 -0
  97. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/hr/plugin.min.js +1 -0
  98. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/image/plugin.min.js +1 -0
  99. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/imagetools/plugin.min.js +1 -0
  100. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/importcss/plugin.min.js +1 -0
  101. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/insertdatetime/plugin.min.js +1 -0
  102. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/legacyoutput/plugin.min.js +1 -0
  103. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/link/plugin.min.js +1 -0
  104. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/lists/plugin.min.js +1 -0
  105. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/media/plugin.min.js +1 -0
  106. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/nonbreaking/plugin.min.js +1 -0
  107. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/noneditable/plugin.min.js +1 -0
  108. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/pagebreak/plugin.min.js +1 -0
  109. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/paste/plugin.min.js +1 -0
  110. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/preview/plugin.min.js +1 -0
  111. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/print/plugin.min.js +1 -0
  112. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/save/plugin.min.js +1 -0
  113. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/searchreplace/plugin.min.js +1 -0
  114. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/spellchecker/plugin.min.js +1 -0
  115. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/tabfocus/plugin.min.js +1 -0
  116. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/table/plugin.min.js +1 -0
  117. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/template/plugin.min.js +1 -0
  118. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/textcolor/plugin.min.js +1 -0
  119. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/textpattern/plugin.min.js +1 -0
  120. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/toc/plugin.min.js +1 -0
  121. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/visualblocks/css/visualblocks.css +154 -0
  122. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/visualblocks/plugin.min.js +1 -0
  123. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/visualchars/plugin.min.js +1 -0
  124. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/plugins/wordcount/plugin.min.js +1 -0
  125. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/content.inline.min.css +1 -0
  126. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/content.min.css +1 -0
  127. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/content.mobile.min.css +1 -0
  128. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/fonts/tinymce-mobile.woff +0 -0
  129. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/fonts/tinymce-small.eot +0 -0
  130. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/fonts/tinymce-small.svg +63 -0
  131. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/fonts/tinymce-small.ttf +0 -0
  132. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/fonts/tinymce-small.woff +0 -0
  133. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/fonts/tinymce.eot +0 -0
  134. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/fonts/tinymce.svg +132 -0
  135. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/fonts/tinymce.ttf +0 -0
  136. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/fonts/tinymce.woff +0 -0
  137. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/img/anchor.gif +0 -0
  138. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/img/loader.gif +0 -0
  139. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/img/object.gif +0 -0
  140. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/img/trans.gif +0 -0
  141. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/skin.min.css +1 -0
  142. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/skins/lightgray/skin.mobile.min.css +2 -0
  143. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/themes/inlite/theme.min.js +1 -0
  144. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/themes/mobile/theme.min.js +1 -0
  145. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/themes/modern/theme.min.js +1 -0
  146. django_advanced_modals-1.0.0/django_modals/static/django_modals/tinymce/tinymce.min.js +2 -0
  147. django_advanced_modals-1.0.0/django_modals/task_modals.py +65 -0
  148. django_advanced_modals-1.0.0/django_modals/templates/django_modals/blank_page_form.html +27 -0
  149. django_advanced_modals-1.0.0/django_modals/templates/django_modals/datatables/datatable.html +24 -0
  150. django_advanced_modals-1.0.0/django_modals/templates/django_modals/fields/label_checkbox.html +32 -0
  151. django_advanced_modals-1.0.0/django_modals/templates/django_modals/formset/prepended_appended_text.html +51 -0
  152. django_advanced_modals-1.0.0/django_modals/templates/django_modals/formset/table_inline_formset.html +56 -0
  153. django_advanced_modals-1.0.0/django_modals/templates/django_modals/message.html +8 -0
  154. django_advanced_modals-1.0.0/django_modals/templates/django_modals/modal_base.html +48 -0
  155. django_advanced_modals-1.0.0/django_modals/templates/django_modals/modal_formset_base.html +23 -0
  156. django_advanced_modals-1.0.0/django_modals/templates/django_modals/modal_lazy_body.html +4 -0
  157. django_advanced_modals-1.0.0/django_modals/templates/django_modals/modal_menu.html +13 -0
  158. django_advanced_modals-1.0.0/django_modals/templates/django_modals/multi_form/table_formset.html +30 -0
  159. django_advanced_modals-1.0.0/django_modals/templates/django_modals/multi_form.html +7 -0
  160. django_advanced_modals-1.0.0/django_modals/templates/django_modals/scratchpad.html +26 -0
  161. django_advanced_modals-1.0.0/django_modals/templates/django_modals/widgets/colour_picker.html +69 -0
  162. django_advanced_modals-1.0.0/django_modals/templates/django_modals/widgets/colour_picker_append.html +61 -0
  163. django_advanced_modals-1.0.0/django_modals/templates/django_modals/widgets/datepicker.html +18 -0
  164. django_advanced_modals-1.0.0/django_modals/templates/django_modals/widgets/month_picker.html +14 -0
  165. django_advanced_modals-1.0.0/django_modals/templates/django_modals/widgets/select2.html +48 -0
  166. django_advanced_modals-1.0.0/django_modals/templates/django_modals/widgets/tinymce.html +19 -0
  167. django_advanced_modals-1.0.0/django_modals/templates/django_modals/widgets/toggle.html +8 -0
  168. django_advanced_modals-1.0.0/django_modals/templates/example_views/datatables.html +15 -0
  169. django_advanced_modals-1.0.0/django_modals/templatetags/__init__.py +0 -0
  170. django_advanced_modals-1.0.0/django_modals/templatetags/modal_tags.py +30 -0
  171. django_advanced_modals-1.0.0/django_modals/url_helper.py +20 -0
  172. django_advanced_modals-1.0.0/django_modals/widgets/colour_picker.py +30 -0
  173. django_advanced_modals-1.0.0/django_modals/widgets/jquery_datepicker.py +8 -0
  174. django_advanced_modals-1.0.0/django_modals/widgets/month_picker.py +8 -0
  175. django_advanced_modals-1.0.0/django_modals/widgets/select2.py +168 -0
  176. django_advanced_modals-1.0.0/django_modals/widgets/widgets.py +11 -0
  177. django_advanced_modals-1.0.0/pyproject.toml +37 -0
  178. django_advanced_modals-1.0.0/readme.md +510 -0
  179. django_advanced_modals-1.0.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Ian Jones
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ recursive-exclude * *.pyc
2
+ recursive-exclude * *.pyo
3
+ recursive-exclude * __pycache__
@@ -0,0 +1,531 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-advanced-modals
3
+ Version: 1.0.0
4
+ Summary: Django app to implement Bootstrap nested modals
5
+ Author-email: Ian Jones <ian@cobi.co.uk>
6
+ Maintainer: Thomas Turner
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/django-advance-utils/django-advanced-modals
9
+ Project-URL: Upstream, https://github.com/jonesim/django-modals
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.6
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: ajax-advanced-helpers>=1.0.0
17
+ Requires-Dist: django-advanced-menus>=1.0.0
18
+ Requires-Dist: django-crispy-forms
19
+ Requires-Dist: make-html
20
+ Dynamic: license-file
21
+
22
+ [![PyPI version](https://img.shields.io/pypi/v/django-advanced-modals)](https://pypi.org/project/django-advanced-modals/)
23
+
24
+ # django-advanced-modals
25
+
26
+ Bootstrap nested modals with AJAX form handling for Django. Modals are class-based views — they handle GET (render the modal) and POST (process buttons/forms) via AJAX, with responses returned as JSON command arrays.
27
+
28
+ The [django-advance-utils](https://github.com/django-advance-utils) line of Ian Jones's
29
+ [django-nested-modals](https://github.com/jonesim/django-modals), forked so that releases can be cut as
30
+ the downstream libraries and django-advanced-report-builder need them. The Python package is still
31
+ `django_modals`, so existing imports and `INSTALLED_APPS` entries do not change; only the pip name
32
+ does. It depends on [ajax-advanced-helpers](https://github.com/django-advance-utils/ajax-advanced-helpers)
33
+ and [django-advanced-menus](https://github.com/django-advance-utils/django-advanced-menus).
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install django-advanced-modals
39
+ ```
40
+
41
+ Add to `INSTALLED_APPS`:
42
+
43
+ ```python
44
+ INSTALLED_APPS = [
45
+ ...
46
+ 'bootstrap_modals',
47
+ ]
48
+ ```
49
+
50
+ Add to your base template:
51
+
52
+ ```html
53
+ <script src="{% static 'django_modals/js/modals.js' %}"></script>
54
+ <link rel="stylesheet" type="text/css" href="{% static 'django_modals/css/modals.css' %}"/>
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Quick Start
60
+
61
+ ### 1. Define a modal view
62
+
63
+ ```python
64
+ # views.py
65
+ from django_modals.modals import ModelFormModal
66
+
67
+ class CompanyModal(ModelFormModal):
68
+ model = Company
69
+ form_fields = ['name', 'active']
70
+ modal_title = ['New Company', 'Edit Company']
71
+ ```
72
+
73
+ ### 2. Wire up a URL
74
+
75
+ ```python
76
+ # urls.py
77
+ path('modal/company/<slug:slug>/', CompanyModal.as_view(), name='company_modal'),
78
+ ```
79
+
80
+ ### 3. Trigger it from a template
81
+
82
+ ```django
83
+ {% load modal_tags %}
84
+ {% show_modal 'company_modal' 'pk-1' %} {# edit pk=1 #}
85
+ {% show_modal 'company_modal' 'pk-0' %} {# create new #}
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Modal Types
91
+
92
+ ### `Modal` — simple content modal
93
+
94
+ Use for messages, confirmations, or any custom HTML content.
95
+
96
+ ```python
97
+ from django_modals.modals import Modal
98
+ from django_modals.helper import modal_button
99
+
100
+ class InfoModal(Modal):
101
+ modal_title = 'Information'
102
+
103
+ def modal_content(self):
104
+ return '<p>This action cannot be undone.</p>'
105
+
106
+ def get_modal_buttons(self):
107
+ return [modal_button('OK', 'close', 'btn-success')]
108
+ ```
109
+
110
+ ### `TemplateModal` — modal from a template file
111
+
112
+ ```python
113
+ from django_modals.modals import TemplateModal
114
+
115
+ class HelpModal(TemplateModal):
116
+ modal_title = 'Help'
117
+ modal_template = 'myapp/help_modal.html'
118
+
119
+ def modal_context(self):
120
+ return {'section': self.slug.get('section', 'general')}
121
+ ```
122
+
123
+ ### `FormModal` — non-model form
124
+
125
+ ```python
126
+ from django_modals.modals import FormModal
127
+
128
+ class ContactModal(FormModal):
129
+ form_class = ContactForm
130
+ modal_title = 'Contact Us'
131
+
132
+ def form_valid(self, form):
133
+ send_email(form.cleaned_data)
134
+ return self.command_response('reload')
135
+ ```
136
+
137
+ ### `ModelFormModal` — CRUD modal for a model
138
+
139
+ The most common modal type. Automatically handles create, edit, view, and delete.
140
+
141
+ ```python
142
+ from django_modals.modals import ModelFormModal
143
+ from django_modals.processes import PERMISSION_AUTHENTICATED, PERMISSION_STAFF
144
+
145
+ class PersonModal(ModelFormModal):
146
+ model = Person
147
+ form_fields = ['first_name', 'surname', 'email', 'company']
148
+ modal_title = ['New Person', 'Edit Person', 'View Person']
149
+ permission_create = PERMISSION_AUTHENTICATED
150
+ permission_edit = PERMISSION_AUTHENTICATED
151
+ permission_delete = PERMISSION_STAFF
152
+ ```
153
+
154
+ ### `MultiFormModal` — multiple independent forms in one modal
155
+
156
+ ```python
157
+ from django_modals.modals import MultiFormModal, MultiForm
158
+
159
+ class NewAccountModal(MultiFormModal):
160
+ modal_title = 'Create Account'
161
+ forms = [
162
+ MultiForm(Company, ['name', 'active']),
163
+ MultiForm(Person, ['first_name', 'surname', 'email']),
164
+ ]
165
+
166
+ def forms_valid(self, valid_forms):
167
+ company = valid_forms['CompanyForm'].save()
168
+ person = valid_forms['PersonForm']
169
+ person.instance.company = company
170
+ person.save()
171
+ return self.command_response('reload')
172
+ ```
173
+
174
+ ---
175
+
176
+ ## URL Slug System
177
+
178
+ State is passed via a single URL slug encoded as alternating key-value pairs. The slug is parsed into `self.slug` dict inside the modal.
179
+
180
+ ```
181
+ pk-1 → {'pk': '1'} edit pk=1
182
+ pk-0 → {} create new
183
+ pk-1-modal-view → {'pk': '1', ...} view (read-only)
184
+ pk-1-modal-viewedit → {'pk': '1', ...} view with edit button
185
+ pk-1-modal-delete → {'pk': '1', ...} delete confirmation
186
+ section-help → {'section': 'help'} arbitrary key-value
187
+ ```
188
+
189
+ In the URL conf, use `<slug:slug>` for standard slugs or `<str:slug>` when values contain uppercase or special characters.
190
+
191
+ ```python
192
+ urlpatterns = [
193
+ path('modal/person/<slug:slug>/', PersonModal.as_view(), name='person_modal'),
194
+ ]
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Processes and Permissions
200
+
201
+ Each modal operation is a *process*. Permissions control who can invoke each process.
202
+
203
+ ### Process constants (`django_modals.processes`)
204
+
205
+ | Constant | Value | Description |
206
+ |---|---|---|
207
+ | `PROCESS_CREATE` | 0 | Create new object |
208
+ | `PROCESS_EDIT` | 1 | Edit existing object |
209
+ | `PROCESS_VIEW` | 2 | Read-only view |
210
+ | `PROCESS_DELETE` | 3 | Delete object |
211
+ | `PROCESS_EDIT_DELETE` | 4 | Edit with delete button |
212
+ | `PROCESS_VIEW_EDIT` | 5 | Read-only with edit button |
213
+
214
+ ### Permission constants
215
+
216
+ | Constant | Description |
217
+ |---|---|
218
+ | `PERMISSION_OFF` | No restriction |
219
+ | `PERMISSION_DISABLE` | Always disabled |
220
+ | `PERMISSION_AUTHENTICATED` | Logged-in users only |
221
+ | `PERMISSION_STAFF` | Staff/superusers only |
222
+ | `PERMISSION_METHOD` | Delegate to `permission(user, process)` method |
223
+
224
+ ```python
225
+ from django_modals.processes import (
226
+ PERMISSION_OFF, PERMISSION_AUTHENTICATED,
227
+ PERMISSION_STAFF, PERMISSION_METHOD
228
+ )
229
+
230
+ class CompanyModal(ModelFormModal):
231
+ model = Company
232
+ form_fields = ['name', 'active']
233
+ permission_create = PERMISSION_AUTHENTICATED
234
+ permission_edit = PERMISSION_AUTHENTICATED
235
+ permission_delete = PERMISSION_STAFF
236
+ permission_view = PERMISSION_OFF
237
+
238
+ # Used when permission_X = PERMISSION_METHOD
239
+ @staticmethod
240
+ def permission(user, process):
241
+ return user.groups.filter(name='editors').exists()
242
+ ```
243
+
244
+ ---
245
+
246
+ ## ModelFormModal Reference
247
+
248
+ ### Class attributes
249
+
250
+ | Attribute | Type | Description |
251
+ |---|---|---|
252
+ | `model` | Model class | Required |
253
+ | `form_fields` | list | Field names to include |
254
+ | `form_class` | Form class | Override auto-generated form |
255
+ | `modal_title` | str or list | Single title, or `[create, edit, view]` |
256
+ | `size` | `'sm'` / `'md'` / `'lg'` | Modal dialog size (default `'lg'`) |
257
+ | `labels` | dict | Field label overrides |
258
+ | `help_texts` | dict | Field help text overrides |
259
+ | `widgets` | dict | Widget overrides |
260
+ | `field_classes` | dict | Form field class overrides |
261
+ | `error_messages` | dict | Error message overrides |
262
+ | `permission_create` | constant | Create permission |
263
+ | `permission_edit` | constant | Edit permission |
264
+ | `permission_view` | constant | View permission |
265
+ | `permission_delete` | constant | Delete permission |
266
+ | `delete_message` | str | Confirmation message for delete |
267
+ | `delete_title` | str | Title for delete confirmation |
268
+ | `focus` | bool | Auto-focus first field (default `True`) |
269
+ | `lazy` | bool | Load modal content via AJAX (default `False`) |
270
+
271
+ ### Hook methods
272
+
273
+ ```python
274
+ class PersonModal(ModelFormModal):
275
+ model = Person
276
+ form_fields = ['first_name', 'surname', 'company']
277
+
278
+ @staticmethod
279
+ def form_setup(form, **kwargs):
280
+ """Customise form fields before rendering — runs on GET and invalid POST."""
281
+ form.fields['company'].queryset = Company.objects.filter(active=True)
282
+
283
+ def form_valid(self, form):
284
+ """Override save logic. Must return a command response."""
285
+ instance = form.save()
286
+ return self.command_response('reload')
287
+
288
+ def post_save(self, created, form):
289
+ """Runs after save, before the response is built."""
290
+ if created:
291
+ notify_team(form.instance)
292
+
293
+ def object_delete(self):
294
+ """Runs after the object is deleted."""
295
+ audit_log(self.object)
296
+ ```
297
+
298
+ ---
299
+
300
+ ## MultiFormModal Reference
301
+
302
+ Forms are defined with `MultiForm` instances. Each `MultiForm` accepts the same field-configuration arguments as `ModelFormModal`.
303
+
304
+ ```python
305
+ MultiForm(
306
+ model,
307
+ fields,
308
+ form_id=None, # Defaults to '<ModelName>Form'
309
+ labels={},
310
+ help_texts={},
311
+ widgets={},
312
+ field_classes={},
313
+ error_messages={},
314
+ )
315
+ ```
316
+
317
+ Override methods on the modal:
318
+
319
+ ```python
320
+ class AccountModal(MultiFormModal):
321
+ forms = [
322
+ MultiForm(Company, ['name']),
323
+ MultiForm(Person, ['first_name', 'surname']),
324
+ ]
325
+
326
+ def form_setup(self, form, **kwargs):
327
+ """Called for each form individually. Return a layout or None."""
328
+ if form.form_id == 'PersonForm':
329
+ form.fields['surname'].required = True
330
+ return None
331
+
332
+ def get_instances(self, form_id):
333
+ """Return a model instance for edit mode."""
334
+ if form_id == 'CompanyForm':
335
+ return Company.objects.get(pk=self.slug.get('pk'))
336
+
337
+ def forms_valid(self, valid_forms):
338
+ company = valid_forms['CompanyForm'].save()
339
+ person = valid_forms['PersonForm']
340
+ person.instance.company = company
341
+ person.save()
342
+ return self.command_response('reload')
343
+ ```
344
+
345
+ ---
346
+
347
+ ## Forms
348
+
349
+ ### `ModelCrispyForm`
350
+
351
+ The default base form used by `ModelFormModal`. Inherits from `ModelForm` and applies crispy-forms layout automatically. Use it directly when you need a custom `form_class`:
352
+
353
+ ```python
354
+ from django_modals.forms import ModelCrispyForm
355
+
356
+ class PersonForm(ModelCrispyForm):
357
+ class Meta:
358
+ model = Person
359
+ fields = ['first_name', 'surname', 'email']
360
+
361
+ class PersonModal(ModelFormModal):
362
+ form_class = PersonForm
363
+ ```
364
+
365
+ ### `CrispyForm`
366
+
367
+ For non-model forms with crispy rendering:
368
+
369
+ ```python
370
+ from django_modals.forms import CrispyForm
371
+ from django import forms
372
+
373
+ class SearchForm(CrispyForm):
374
+ query = forms.CharField()
375
+ active_only = forms.BooleanField(required=False)
376
+ ```
377
+
378
+ ### Advanced field configuration with `FieldEx`
379
+
380
+ Pass a tuple `(field_name, options_dict)` in `form_fields` for per-field configuration:
381
+
382
+ ```python
383
+ from django_modals.helper import FieldEx
384
+
385
+ class CompanyModal(ModelFormModal):
386
+ model = Company
387
+ form_fields = [
388
+ 'name',
389
+ ('status', {'label': 'Current Status', 'help_text': 'Active or inactive'}),
390
+ FieldEx('notes', template='custom_field.html'),
391
+ ]
392
+ ```
393
+
394
+ ---
395
+
396
+ ## Widgets
397
+
398
+ ```python
399
+ from django_modals.widgets.select2 import Select2, Select2Multiple
400
+ from django_modals.widgets.colour_picker import ColourPickerWidget
401
+ from django_modals.widgets.datepicker import JQueryDatepickerWidget
402
+ from django_modals.widgets.month_picker import MonthPickerWidget
403
+ ```
404
+
405
+ ### Select2
406
+
407
+ ```python
408
+ class CompanyModal(ModelFormModal):
409
+ model = Company
410
+ form_fields = ['name', 'tags', 'owner']
411
+ widgets = {
412
+ 'tags': Select2Multiple,
413
+ 'owner': Select2,
414
+ }
415
+ ```
416
+
417
+ **AJAX-powered Select2** — define a `select2_<fieldname>` method on the modal to filter results dynamically:
418
+
419
+ ```python
420
+ def select2_owner(self, search, **kwargs):
421
+ qs = Person.objects.filter(name__icontains=search)
422
+ return [(p.pk, str(p)) for p in qs]
423
+ ```
424
+
425
+ ---
426
+
427
+ ## Template Tags
428
+
429
+ ```django
430
+ {% load modal_tags %}
431
+
432
+ {# Link that opens a modal #}
433
+ {% show_modal 'url_name' 'pk-1' %}
434
+ {% show_modal 'url_name' 'pk-1' css_class='btn btn-primary' label='Edit' %}
435
+
436
+ {# AJAX button that calls a method on the current modal #}
437
+ {% modal_button_method 'Save Draft' 'save_draft' %}
438
+
439
+ {# Delete button #}
440
+ {% modal_delete 'url_name' slug='pk-1' text='Delete' %}
441
+ ```
442
+
443
+ ---
444
+
445
+ ## AJAX Commands
446
+
447
+ Modal button handlers and `form_valid` must return a command response. Commands are processed client-side by `ajax_helpers.process_commands`.
448
+
449
+ ```python
450
+ # Close the modal and reload the page
451
+ return self.command_response('reload')
452
+
453
+ # Close the modal only
454
+ return self.command_response('close')
455
+
456
+ # Redirect
457
+ return self.command_response('redirect', url='/dashboard/')
458
+
459
+ # Update a DOM element without closing the modal
460
+ self.add_command('html', selector='#status', html='<span>Saved</span>')
461
+ return self.command_response()
462
+
463
+ # Replace the modal content
464
+ return self.command_response('overwrite_modal', html=rendered_html)
465
+ ```
466
+
467
+ Custom button handlers are methods named `button_<name>`:
468
+
469
+ ```python
470
+ class ReportModal(TemplateModal):
471
+ modal_title = 'Report'
472
+ modal_template = 'report.html'
473
+
474
+ def button_export(self, **kwargs):
475
+ data = generate_csv(self.slug.get('pk'))
476
+ return self.command_response('save_file', filename='report.csv', data=data)
477
+ ```
478
+
479
+ ---
480
+
481
+ ## Lazy Loading
482
+
483
+ Set `lazy = True` on a modal to defer content loading until the modal is opened. Useful for slow queries:
484
+
485
+ ```python
486
+ class HeavyReportModal(TemplateModal):
487
+ lazy = True
488
+ modal_title = 'Report'
489
+ modal_template = 'report.html'
490
+ ```
491
+
492
+ ---
493
+
494
+ ## JSON Field Mixin
495
+
496
+ `JsonFieldMixin` expands a `JSONField` into individually editable typed form fields:
497
+
498
+ ```python
499
+ from django_modals.modals import ModelFormModal, JsonFieldMixin
500
+ from django import forms
501
+
502
+ class CompanyModal(JsonFieldMixin, ModelFormModal):
503
+ model = Company
504
+ form_fields = ['name']
505
+ json_field = 'extra'
506
+ json_field_config = {
507
+ 'notes': (forms.CharField, {'required': False, 'label': 'Notes'}),
508
+ 'priority': (forms.ChoiceField, {
509
+ 'choices': [('low', 'Low'), ('high', 'High')],
510
+ 'required': False,
511
+ }),
512
+ }
513
+ ```
514
+
515
+ The modal renders an "Add field" dropdown; selecting a key adds that field to the form. On save, all values are packed back into the JSON column.
516
+
517
+ ---
518
+
519
+ ## Development / Example Project
520
+
521
+ ```bash
522
+ docker-compose up
523
+ ```
524
+
525
+ Django app runs at `http://localhost:8007`. See `django_examples/modal_examples/views/` for working examples of every modal type.
526
+
527
+ ```bash
528
+ # Run tests
529
+ cd django_examples
530
+ python manage.py test django_modals
531
+ ```