Flask-Exts 0.3.0__tar.gz → 0.3.1__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 (387) hide show
  1. {flask_exts-0.3.0 → flask_exts-0.3.1}/.gitignore +1 -1
  2. flask_exts-0.3.1/CHANGES.rst +39 -0
  3. {flask_exts-0.3.0 → flask_exts-0.3.1}/Makefile +1 -1
  4. flask_exts-0.3.1/PKG-INFO +111 -0
  5. flask_exts-0.3.1/README.rst +69 -0
  6. flask_exts-0.3.1/docs/advanced/database.rst +123 -0
  7. {flask_exts-0.3.0 → flask_exts-0.3.1}/pyproject.toml +37 -26
  8. flask_exts-0.3.1/src/flask_exts/__about__.py +1 -0
  9. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/filter_mixin.py +2 -3
  10. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/view.py +11 -12
  11. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/builtin_extensions/__init__.py +2 -2
  12. flask_exts-0.3.1/src/flask_exts/builtin_extensions/portal_ext.py +14 -0
  13. flask_exts-0.3.1/src/flask_exts/builtin_extensions/security_ext.py +24 -0
  14. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/builtin_extensions/usercenter_ext.py +2 -8
  15. flask_exts-0.3.1/src/flask_exts/datastore/sqla/database.py +154 -0
  16. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/query.py +4 -3
  17. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/extension_core/base.py +9 -0
  18. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/extension_core/manager.py +12 -1
  19. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/extension_core/registry.py +1 -1
  20. flask_exts-0.3.1/src/flask_exts/frontend/plugins/modal_plugin.py +41 -0
  21. flask_exts-0.3.1/src/flask_exts/portal/index_view.py +25 -0
  22. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/authorizer/simple_authorizer.py +1 -3
  23. flask_exts-0.3.1/src/flask_exts/static/js/modal.js +621 -0
  24. flask_exts-0.3.1/src/flask_exts/templates/macro/modal.html +15 -0
  25. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/profile.html +23 -4
  26. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/setup_tfa.html +8 -5
  27. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/show_tfa.html +8 -5
  28. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/verify_tfa.html +1 -1
  29. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/user_view.py +47 -21
  30. flask_exts-0.3.1/tests/datastore/sqla/test_db.py +171 -0
  31. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/extension_core/test_manager.py +0 -11
  32. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/extension_core/test_registry.py +1 -2
  33. flask_exts-0.3.1/tests/frontend/__init__.py +0 -0
  34. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/security/test_authorizer.py +22 -0
  35. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/test_builtin_exts.py +23 -16
  36. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/usercenter/test_user_view.py +41 -18
  37. flask_exts-0.3.0/CHANGES.rst +0 -32
  38. flask_exts-0.3.0/PKG-INFO +0 -96
  39. flask_exts-0.3.0/README.rst +0 -40
  40. flask_exts-0.3.0/docs/advanced/database.rst +0 -10
  41. flask_exts-0.3.0/src/flask_exts/__about__.py +0 -1
  42. flask_exts-0.3.0/src/flask_exts/builtin_extensions/security_ext.py +0 -15
  43. flask_exts-0.3.0/src/flask_exts/builtin_extensions/startup_ext.py +0 -62
  44. flask_exts-0.3.0/src/flask_exts/datastore/sqla/database.py +0 -85
  45. flask_exts-0.3.0/src/flask_exts/frontend/plugins/admin_modal_plugin.py +0 -12
  46. flask_exts-0.3.0/src/flask_exts/static/js/modal.js +0 -49
  47. flask_exts-0.3.0/src/flask_exts/templates/macro/modal.html +0 -11
  48. flask_exts-0.3.0/src/flask_exts/templates/user/verify_tfa_modal.html +0 -13
  49. flask_exts-0.3.0/tests/datastore/sqla/test_db.py +0 -87
  50. flask_exts-0.3.0/tests/test_db.py +0 -20
  51. {flask_exts-0.3.0 → flask_exts-0.3.1}/.editorconfig +0 -0
  52. {flask_exts-0.3.0 → flask_exts-0.3.1}/.github/workflows/docs.yml +0 -0
  53. {flask_exts-0.3.0 → flask_exts-0.3.1}/.github/workflows/release.yml +0 -0
  54. {flask_exts-0.3.0 → flask_exts-0.3.1}/.readthedocs.yaml +0 -0
  55. {flask_exts-0.3.0 → flask_exts-0.3.1}/.vscode/settings.json +0 -0
  56. {flask_exts-0.3.0 → flask_exts-0.3.1}/LICENSE.rst +0 -0
  57. {flask_exts-0.3.0 → flask_exts-0.3.1}/babel/babel.cfg +0 -0
  58. {flask_exts-0.3.0 → flask_exts-0.3.1}/babel/babel.sh +0 -0
  59. {flask_exts-0.3.0 → flask_exts-0.3.1}/babel/messages.pot +0 -0
  60. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/Makefile +0 -0
  61. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/_static/logo.png +0 -0
  62. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/advanced/index.rst +0 -0
  63. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/api.rst +0 -0
  64. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/changes.rst +0 -0
  65. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/conf.py +0 -0
  66. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/configure.rst +0 -0
  67. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/develop.rst +0 -0
  68. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/examples.rst +0 -0
  69. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/index.rst +0 -0
  70. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/make.bat +0 -0
  71. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/quickstart.rst +0 -0
  72. {flask_exts-0.3.0 → flask_exts-0.3.1}/docs/requirements.txt +0 -0
  73. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/README.rst +0 -0
  74. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/__init__.py +0 -0
  75. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/admin_views.py +0 -0
  76. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/build_sample.py +0 -0
  77. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/file_op.py +0 -0
  78. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/make_app.py +0 -0
  79. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/models/__init__.py +0 -0
  80. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/models/author.py +0 -0
  81. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/models/post.py +0 -0
  82. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/models/post_tag.py +0 -0
  83. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/models/tag.py +0 -0
  84. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/models/tree.py +0 -0
  85. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/static/favicon.ico +0 -0
  86. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/templates/field_list.html +0 -0
  87. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/templates/locations.html +0 -0
  88. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/templates/tree_list.html +0 -0
  89. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/views/author_view.py +0 -0
  90. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/views/post_view.py +0 -0
  91. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/views/tag_view.py +0 -0
  92. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/demo/views/tree_view.py +0 -0
  93. {flask_exts-0.3.0 → flask_exts-0.3.1}/examples/simple/__init__.py +0 -0
  94. {flask_exts-0.3.0 → flask_exts-0.3.1}/package.json +0 -0
  95. {flask_exts-0.3.0 → flask_exts-0.3.1}/requirements/build.in +0 -0
  96. {flask_exts-0.3.0 → flask_exts-0.3.1}/requirements/develop.in +0 -0
  97. {flask_exts-0.3.0 → flask_exts-0.3.1}/requirements/test.in +0 -0
  98. {flask_exts-0.3.0 → flask_exts-0.3.1}/scripts/copy-vendor.js +0 -0
  99. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/__init__.py +0 -0
  100. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/__init__.py +0 -0
  101. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/base.py +0 -0
  102. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/exposer.py +0 -0
  103. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/menu.py +0 -0
  104. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/__init__.py +0 -0
  105. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/actions_mixin.py +0 -0
  106. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/ajax.py +0 -0
  107. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/base.py +0 -0
  108. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/core/__init__.py +0 -0
  109. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/core/columns.py +0 -0
  110. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/core/forms.py +0 -0
  111. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/core/pagination.py +0 -0
  112. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/core/sorting.py +0 -0
  113. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/core/values.py +0 -0
  114. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/filter.py +0 -0
  115. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/operations/__init__.py +0 -0
  116. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/operations/create.py +0 -0
  117. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/operations/delete.py +0 -0
  118. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/operations/export.py +0 -0
  119. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/operations/read.py +0 -0
  120. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/operations/update.py +0 -0
  121. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/row_actions.py +0 -0
  122. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/type_formatters.py +0 -0
  123. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/model/types.py +0 -0
  124. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/__init__.py +0 -0
  125. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/ajax.py +0 -0
  126. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/filter/__init__.py +0 -0
  127. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/filter/filter_converter.py +0 -0
  128. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/filter/filters.py +0 -0
  129. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/form/__init__.py +0 -0
  130. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/form/field_converters.py +0 -0
  131. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/form/inline_form.py +0 -0
  132. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/form/inline_model_convert.py +0 -0
  133. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/form/model_field_convert.py +0 -0
  134. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/form/utils.py +0 -0
  135. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/sqla_view/__init__.py +0 -0
  136. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/sqla_view/pagination_handler.py +0 -0
  137. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/sqla_view/query_handler.py +0 -0
  138. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/sqla_view/relationships_handler.py +0 -0
  139. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/sqla_view/sorting_handler.py +0 -0
  140. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/type_formatters.py +0 -0
  141. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/sqla/view.py +0 -0
  142. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/admin/view.py +0 -0
  143. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/builtin_extensions/admin_ext.py +0 -0
  144. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/builtin_extensions/babel_ext.py +0 -0
  145. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/builtin_extensions/database_ext.py +0 -0
  146. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/builtin_extensions/emailer_ext.py +0 -0
  147. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/builtin_extensions/frontend_ext.py +0 -0
  148. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/builtin_extensions/login_ext.py +0 -0
  149. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/builtin_extensions/userstore_ext.py +0 -0
  150. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/commands.py +0 -0
  151. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/constants.py +0 -0
  152. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/__init__.py +0 -0
  153. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/__init__.py +0 -0
  154. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/_db.py +0 -0
  155. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/query_builders/__init__.py +0 -0
  156. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/query_builders/filter_builder.py +0 -0
  157. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/query_builders/join_builder.py +0 -0
  158. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/query_builders/pagination_builder.py +0 -0
  159. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/query_builders/sort_builder.py +0 -0
  160. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/utils/__init__.py +0 -0
  161. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/utils/model_mapper.py +0 -0
  162. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/datastore/sqla/utils/orm.py +0 -0
  163. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/emailer/__init__.py +0 -0
  164. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/emailer/base.py +0 -0
  165. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/emailer/reset_password_sender.py +0 -0
  166. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/emailer/sender.py +0 -0
  167. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/emailer/smtp.py +0 -0
  168. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/emailer/verify_email_sender.py +0 -0
  169. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/extension_core/__init__.py +0 -0
  170. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/__init__.py +0 -0
  171. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/__init__.py +0 -0
  172. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/ajax_select.py +0 -0
  173. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/datetime.py +0 -0
  174. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/file.py +0 -0
  175. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/inline.py +0 -0
  176. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/json.py +0 -0
  177. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/recaptcha.py +0 -0
  178. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/select.py +0 -0
  179. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/sqla.py +0 -0
  180. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/switch.py +0 -0
  181. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/fields/taglist.py +0 -0
  182. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/form/__init__.py +0 -0
  183. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/form/base_form.py +0 -0
  184. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/form/csrf.py +0 -0
  185. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/form/flask_form.py +0 -0
  186. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/form/meta.py +0 -0
  187. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/form/session_csrf.py +0 -0
  188. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/form/utils.py +0 -0
  189. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/validators/__init__.py +0 -0
  190. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/validators/field_list.py +0 -0
  191. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/validators/file.py +0 -0
  192. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/validators/recaptcha.py +0 -0
  193. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/validators/sqla.py +0 -0
  194. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/widgets/__init__.py +0 -0
  195. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/widgets/checkbox.py +0 -0
  196. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/widgets/datetime.py +0 -0
  197. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/widgets/editable.py +0 -0
  198. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/widgets/file.py +0 -0
  199. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/widgets/inline.py +0 -0
  200. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/widgets/recaptcha.py +0 -0
  201. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/forms/widgets/select.py +0 -0
  202. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/__init__.py +0 -0
  203. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugin_base.py +0 -0
  204. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugin_manager.py +0 -0
  205. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/__init__.py +0 -0
  206. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/admin_detail_filter_plugin.py +0 -0
  207. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/admin_filters_plugin.py +0 -0
  208. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/admin_form_plugin.py +0 -0
  209. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/admin_list_action_plugin.py +0 -0
  210. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/admin_plugin.py +0 -0
  211. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/bootstrap5_plugin.py +0 -0
  212. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/clipboard_plugin.py +0 -0
  213. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/copybutton_plugin.py +0 -0
  214. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/dayjs_plugin.py +0 -0
  215. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/editable_plugin.py +0 -0
  216. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/flatpickr_plugin.py +0 -0
  217. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/qrcode_plugin.py +0 -0
  218. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/rediscli_plugin.py +0 -0
  219. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/sphinx_copybutton_plugin.py +0 -0
  220. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/plugins/tom_select_plugin.py +0 -0
  221. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/template.py +0 -0
  222. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/frontend/theme.py +0 -0
  223. {flask_exts-0.3.0/src/flask_exts/security/authorizer → flask_exts-0.3.1/src/flask_exts/portal}/__init__.py +0 -0
  224. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/proxies.py +0 -0
  225. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/__init__.py +0 -0
  226. {flask_exts-0.3.0/src/flask_exts/translations → flask_exts-0.3.1/src/flask_exts/security/authorizer}/__init__.py +0 -0
  227. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/authorizer/base.py +0 -0
  228. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/core.py +0 -0
  229. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/exceptions.py +0 -0
  230. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/hasher.py +0 -0
  231. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/keys.py +0 -0
  232. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/plugins/email_change.py +0 -0
  233. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/plugins/email_verification.py +0 -0
  234. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/plugins/reset_password.py +0 -0
  235. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/plugins/two_factor_authentication.py +0 -0
  236. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/registry.py +0 -0
  237. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/serializer.py +0 -0
  238. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/token_action.py +0 -0
  239. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/urls.py +0 -0
  240. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/security/user_mixin.py +0 -0
  241. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/signals.py +0 -0
  242. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/css/admin.css +0 -0
  243. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/css/editable.css +0 -0
  244. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/css/rediscli.css +0 -0
  245. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/copybutton.js +0 -0
  246. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/detail_filter.js +0 -0
  247. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/editable.js +0 -0
  248. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/field-registry.js +0 -0
  249. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/filters.js +0 -0
  250. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/form.js +0 -0
  251. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/list_action.js +0 -0
  252. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/qrcode.js +0 -0
  253. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/rediscli.js +0 -0
  254. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/security/base64.js +0 -0
  255. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/security/webauthn.js +0 -0
  256. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/js/ui-factory.js +0 -0
  257. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/logo.png +0 -0
  258. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/bootstrap5/bootstrap.bundle.min.js +0 -0
  259. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/bootstrap5/bootstrap.min.css +0 -0
  260. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/clipboard/clipboard.min.js +0 -0
  261. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/dayjs/dayjs.min.js +0 -0
  262. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/dayjs/timezone.js +0 -0
  263. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/dayjs/utc.js +0 -0
  264. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/flatpickr/flatpickr.min.css +0 -0
  265. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/flatpickr/flatpickr.min.js +0 -0
  266. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/sphinx-copybutton/sphinx-copybutton.css +0 -0
  267. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/sphinx-copybutton/sphinx-copybutton.js +0 -0
  268. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/tom-select/tom-select.bootstrap5.min.css +0 -0
  269. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/static/vendor/tom-select/tom-select.complete.min.js +0 -0
  270. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/admin/index.html +0 -0
  271. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/admin/master.html +0 -0
  272. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/admin/model/create.html +0 -0
  273. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/admin/model/details.html +0 -0
  274. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/admin/model/edit.html +0 -0
  275. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/admin/model/list.html +0 -0
  276. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/base.html +0 -0
  277. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/index.html +0 -0
  278. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/layout.html +0 -0
  279. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/macro/form.html +0 -0
  280. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/macro/icon.html +0 -0
  281. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/macro/layout.html +0 -0
  282. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/macro/menu.html +0 -0
  283. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/macro/message.html +0 -0
  284. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/macro/nav.html +0 -0
  285. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/macro/pagination.html +0 -0
  286. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/change_password.html +0 -0
  287. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/email_change.html +0 -0
  288. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/forgot_password.html +0 -0
  289. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/index.html +0 -0
  290. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/list.html +0 -0
  291. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/login.html +0 -0
  292. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/recovery.html +0 -0
  293. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/recovery_codes.html +0 -0
  294. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/register.html +0 -0
  295. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/reset_password.html +0 -0
  296. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/templates/user/verify_email.html +0 -0
  297. {flask_exts-0.3.0/src/flask_exts/usercenter → flask_exts-0.3.1/src/flask_exts/translations}/__init__.py +0 -0
  298. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/translations/en/LC_MESSAGES/messages.mo +0 -0
  299. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/translations/en/LC_MESSAGES/messages.po +0 -0
  300. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/translations/zh_CN/LC_MESSAGES/messages.mo +0 -0
  301. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/translations/zh_CN/LC_MESSAGES/messages.po +0 -0
  302. {flask_exts-0.3.0/tests/admin → flask_exts-0.3.1/src/flask_exts/usercenter}/__init__.py +0 -0
  303. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/auth_requires.py +0 -0
  304. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/core.py +0 -0
  305. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/forms/__init__.py +0 -0
  306. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/forms/change_password.py +0 -0
  307. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/forms/email_change.py +0 -0
  308. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/forms/forgot_password.py +0 -0
  309. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/forms/login.py +0 -0
  310. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/forms/recovery.py +0 -0
  311. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/forms/register.py +0 -0
  312. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/forms/resend_verification.py +0 -0
  313. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/forms/reset_password.py +0 -0
  314. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/usercenter/forms/two_factor.py +0 -0
  315. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/userstore/base.py +0 -0
  316. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/userstore/exceptions.py +0 -0
  317. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/userstore/sqla/__init__.py +0 -0
  318. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/userstore/sqla/models/__init__.py +0 -0
  319. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/userstore/sqla/models/role.py +0 -0
  320. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/userstore/sqla/models/user.py +0 -0
  321. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/userstore/sqla/models/user_profile.py +0 -0
  322. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/userstore/sqla/models/user_role.py +0 -0
  323. {flask_exts-0.3.0 → flask_exts-0.3.1}/src/flask_exts/userstore/sqla/userstore.py +0 -0
  324. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/__init__.py +0 -0
  325. {flask_exts-0.3.0/tests/extension_core → flask_exts-0.3.1/tests/admin}/__init__.py +0 -0
  326. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/model/core/test_columns.py +0 -0
  327. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/model/test_action_mixin.py +0 -0
  328. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/model/test_model_view.py +0 -0
  329. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/model/test_model_view_csrf.py +0 -0
  330. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/__init__.py +0 -0
  331. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/custom_sqla_model_view.py +0 -0
  332. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/templates/another_macro.html +0 -0
  333. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/templates/macro.html +0 -0
  334. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/test_basic.py +0 -0
  335. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/test_filter.py +0 -0
  336. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/test_form.py +0 -0
  337. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/test_inline_model.py +0 -0
  338. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/test_multi_pk.py +0 -0
  339. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/test_primary_string.py +0 -0
  340. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/sqla/test_translation.py +0 -0
  341. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/test_admin.py +0 -0
  342. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/admin/test_view.py +0 -0
  343. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/conftest.py +0 -0
  344. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/data/copyleft.gif +0 -0
  345. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/data/copyleft.jpeg +0 -0
  346. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/data/copyleft.jpg +0 -0
  347. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/data/copyleft.png +0 -0
  348. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/data/copyleft.tiff +0 -0
  349. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/datastore/sqla/test_query.py +0 -0
  350. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/emailer/test_base.py +0 -0
  351. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/emailer/test_smtp.py +0 -0
  352. {flask_exts-0.3.0/tests/frontend → flask_exts-0.3.1/tests/extension_core}/__init__.py +0 -0
  353. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/forms/common.py +0 -0
  354. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/forms/fields/test_datetime.py +0 -0
  355. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/forms/fields/test_filefield.py +0 -0
  356. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/forms/fields/test_inline.py +0 -0
  357. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/forms/fields/test_json.py +0 -0
  358. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/forms/fields/test_uploadfile.py +0 -0
  359. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/forms/fields/test_wtforms.py +0 -0
  360. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/forms/form/test_form.py +0 -0
  361. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/frontend/conftest.py +0 -0
  362. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/frontend/macros/test_render_breadcrumb_item.py +0 -0
  363. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/frontend/macros/test_render_nav_item.py +0 -0
  364. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/frontend/test_base.py +0 -0
  365. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/frontend/test_plugin_manager.py +0 -0
  366. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/helper.py +0 -0
  367. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/models/__init__.py +0 -0
  368. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/models/demo.py +0 -0
  369. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/models/multpk.py +0 -0
  370. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/models/primary_string_model.py +0 -0
  371. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/models/relations.py +0 -0
  372. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/models/simple.py +0 -0
  373. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/models/tree.py +0 -0
  374. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/models/unique.py +0 -0
  375. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/security/test_core.py +0 -0
  376. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/security/test_tfa.py +0 -0
  377. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/security/test_urls.py +0 -0
  378. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/templates/method.html +0 -0
  379. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/templates/mock.html +0 -0
  380. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/test_babel.py +0 -0
  381. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/test_login.py +0 -0
  382. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/translations/en/LC_MESSAGES/messages.mo +0 -0
  383. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/translations/en/LC_MESSAGES/messages.po +0 -0
  384. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/translations/messages.pot +0 -0
  385. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/translations/zh_CN/LC_MESSAGES/messages.mo +0 -0
  386. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/translations/zh_CN/LC_MESSAGES/messages.po +0 -0
  387. {flask_exts-0.3.0 → flask_exts-0.3.1}/tests/userstore/test_sqla_userstore.py +0 -0
@@ -33,7 +33,7 @@ docs/_build/
33
33
  # sqlite db
34
34
  *.sqlite
35
35
 
36
- # npm front-end deps (symlinked into src/flask_exts/static/vendor; install via `npm ci`)
36
+ # npm front-end deps
37
37
  node_modules/
38
38
  package-lock.json
39
39
 
@@ -0,0 +1,39 @@
1
+ v0.3.1
2
+ ------
3
+
4
+ Released 2026-10-11
5
+
6
+ - Improve SQLAlchemy lifecycle management, session configuration, and in-memory SQLite support.
7
+ - Add reusable modal form flows and improve two-factor authentication setup.
8
+ - Allow extensions to provide admin views for automatic registration, and clarify duplicate extension-name errors.
9
+
10
+ v0.3.0
11
+ ------
12
+
13
+ Released 2026-10-07
14
+
15
+ - Add user registration, login, logout, profile management, and user-center views.
16
+ - Add email verification, email change, password reset, and recovery workflows.
17
+ - Add a SQLAlchemy-backed user store with user profiles and role management.
18
+ - Add authentication and role-based authorization.
19
+ - Add TOTP-based two-factor authentication and recovery codes.
20
+
21
+ v0.2.0
22
+ ------
23
+
24
+ Released 2026-01-07
25
+
26
+ - Add an extension system with registration, dependency management, and lifecycle hooks.
27
+ - Add an admin interface with model views, CRUD operations, filtering, sorting, pagination, and data export.
28
+ - Add SQLAlchemy integration for database access, model forms, relationships, and queries.
29
+ - Add form fields, widgets, validators, and CSRF protection.
30
+ - Add email delivery and notification support.
31
+ - Add frontend plugin management and Bootstrap-based components.
32
+ - Add Babel integration for localization.
33
+
34
+ v0.1.0
35
+ ------
36
+
37
+ Released 2024-03-20
38
+
39
+ - Initial release of the Flask-Exts package and documentation.
@@ -9,7 +9,7 @@ docs-serve:
9
9
  docs-clean:
10
10
  rm -rf docs/_build
11
11
 
12
- pytest:
12
+ test:
13
13
  pytest
14
14
 
15
15
  pytest-last:
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.5
2
+ Name: Flask-Exts
3
+ Version: 0.3.1
4
+ Summary: Flask extensions with admin, SQLAlchemy, Babel, forms, fields, widgets, and more.
5
+ Project-URL: Documentation, https://flask-exts.readthedocs.io
6
+ Project-URL: Source, https://github.com/ojso/flask-exts
7
+ Project-URL: Homepage, https://github.com/ojso/flask-exts
8
+ Project-URL: Issues, https://github.com/ojso/flask-exts/issues
9
+ Project-URL: Changelog, https://flask-exts.readthedocs.io/en/latest/changes.html
10
+ Author-email: David Hua <david.dong.hua@gmail.com>
11
+ License-Expression: MIT
12
+ License-File: LICENSE.rst
13
+ Keywords: admin,babel,flask,form,sqlalchemy,widget
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Web Environment
16
+ Classifier: Framework :: Flask
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: email-validator>=2.0
29
+ Requires-Dist: flask-babel>=4.0
30
+ Requires-Dist: flask-login>=0.6
31
+ Requires-Dist: flask>=3.0
32
+ Requires-Dist: pyjwt>=2.0
33
+ Requires-Dist: pyotp>=2.0
34
+ Requires-Dist: sqlalchemy>=2.0
35
+ Requires-Dist: tablib>=3.0
36
+ Requires-Dist: wtforms>=3.0
37
+ Provides-Extra: dev
38
+ Requires-Dist: djlint; extra == 'dev'
39
+ Requires-Dist: pytest; extra == 'dev'
40
+ Requires-Dist: ruff; extra == 'dev'
41
+ Description-Content-Type: text/x-rst
42
+
43
+ Flask-Exts
44
+ ==========
45
+
46
+ Flask-Exts is a toolkit for building Flask applications with an integrated
47
+ admin interface, SQLAlchemy support, forms, localization, and user-account
48
+ features. It brings common application components together behind a
49
+ dependency-aware extension system. The project is inspired by
50
+ `Flask-Admin <https://github.com/pallets-eco/flask-admin/>`_ and
51
+ `Bootstrap <https://getbootstrap.com/>`_.
52
+
53
+ Highlights
54
+ ----------
55
+
56
+ - **Modern frontend:** Uses Bootstrap 5 and native browser APIs, with no
57
+ jQuery dependency or Bootstrap 4 frontend.
58
+ - **Composable extensions:** Initializes extensions in dependency order and
59
+ lets extensions contribute views to the admin interface.
60
+ - **Built-in account features:** Provides authentication, email verification
61
+ and recovery flows, TOTP-based two-factor authentication, and recovery
62
+ codes.
63
+ - **Integrated admin and data tools:** Includes SQLAlchemy integration,
64
+ model management, forms, filtering, and data export.
65
+ - **Localization support:** Integrates Babel for translating application
66
+ interfaces.
67
+
68
+ Installation
69
+ ------------
70
+
71
+ Flask-Exts requires Python 3.10 or later. Install it with pip:
72
+
73
+ .. code-block:: console
74
+
75
+ pip install flask-exts
76
+
77
+ Quick start
78
+ -----------
79
+
80
+ Create a Flask application and initialize the extension manager:
81
+
82
+ .. code-block:: python
83
+
84
+ from flask import Flask
85
+ from flask_exts import ExtensionManager
86
+
87
+ app = Flask(__name__)
88
+ app.config["SECRET_KEY"] = "change-this-for-local-development"
89
+ app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.sqlite"
90
+
91
+ extensions = ExtensionManager()
92
+ extensions.init_app(app)
93
+
94
+ if __name__ == "__main__":
95
+ app.run(debug=True)
96
+
97
+ For a complete example with a custom admin view and database setup, see
98
+ ``examples/simple``.
99
+
100
+ Documentation
101
+ -------------
102
+
103
+ - `Documentation <https://flask-exts.readthedocs.io/>`_
104
+ - `Runnable examples <https://github.com/ojso/flask-exts/tree/main/examples>`_
105
+ - `Changelog <https://flask-exts.readthedocs.io/en/latest/changes.html>`_
106
+
107
+ License
108
+ -------
109
+
110
+ Flask-Exts is distributed under the terms of the
111
+ `MIT License <https://opensource.org/licenses/MIT>`_.
@@ -0,0 +1,69 @@
1
+ Flask-Exts
2
+ ==========
3
+
4
+ Flask-Exts is a toolkit for building Flask applications with an integrated
5
+ admin interface, SQLAlchemy support, forms, localization, and user-account
6
+ features. It brings common application components together behind a
7
+ dependency-aware extension system. The project is inspired by
8
+ `Flask-Admin <https://github.com/pallets-eco/flask-admin/>`_ and
9
+ `Bootstrap <https://getbootstrap.com/>`_.
10
+
11
+ Highlights
12
+ ----------
13
+
14
+ - **Modern frontend:** Uses Bootstrap 5 and native browser APIs, with no
15
+ jQuery dependency or Bootstrap 4 frontend.
16
+ - **Composable extensions:** Initializes extensions in dependency order and
17
+ lets extensions contribute views to the admin interface.
18
+ - **Built-in account features:** Provides authentication, email verification
19
+ and recovery flows, TOTP-based two-factor authentication, and recovery
20
+ codes.
21
+ - **Integrated admin and data tools:** Includes SQLAlchemy integration,
22
+ model management, forms, filtering, and data export.
23
+ - **Localization support:** Integrates Babel for translating application
24
+ interfaces.
25
+
26
+ Installation
27
+ ------------
28
+
29
+ Flask-Exts requires Python 3.10 or later. Install it with pip:
30
+
31
+ .. code-block:: console
32
+
33
+ pip install flask-exts
34
+
35
+ Quick start
36
+ -----------
37
+
38
+ Create a Flask application and initialize the extension manager:
39
+
40
+ .. code-block:: python
41
+
42
+ from flask import Flask
43
+ from flask_exts import ExtensionManager
44
+
45
+ app = Flask(__name__)
46
+ app.config["SECRET_KEY"] = "change-this-for-local-development"
47
+ app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.sqlite"
48
+
49
+ extensions = ExtensionManager()
50
+ extensions.init_app(app)
51
+
52
+ if __name__ == "__main__":
53
+ app.run(debug=True)
54
+
55
+ For a complete example with a custom admin view and database setup, see
56
+ ``examples/simple``.
57
+
58
+ Documentation
59
+ -------------
60
+
61
+ - `Documentation <https://flask-exts.readthedocs.io/>`_
62
+ - `Runnable examples <https://github.com/ojso/flask-exts/tree/main/examples>`_
63
+ - `Changelog <https://flask-exts.readthedocs.io/en/latest/changes.html>`_
64
+
65
+ License
66
+ -------
67
+
68
+ Flask-Exts is distributed under the terms of the
69
+ `MIT License <https://opensource.org/licenses/MIT>`_.
@@ -0,0 +1,123 @@
1
+ Database
2
+ ========
3
+
4
+ This page documents the database helpers and SQLAlchemy integration used by Flask-Exts.
5
+
6
+ Overview
7
+ --------
8
+
9
+ Flask-Exts ships a small SQLAlchemy wrapper, :class:`~flask_exts.datastore.sqla.database.SqlaDb`,
10
+ instead of depending on Flask-SQLAlchemy. A ready-to-use singleton is
11
+ exposed as ``db``::
12
+
13
+ from flask_exts.datastore.sqla import db
14
+
15
+ ``db`` is created at import time but stays unbound (``db.engine`` and
16
+ ``db.session`` are ``None``) until :meth:`~flask_exts.datastore.sqla.database.SqlaDb.init_app`
17
+ runs -- which happens automatically when the built-in ``database`` extension
18
+ initializes. This two-step lifecycle is what lets model modules do::
19
+
20
+ from flask_exts.datastore.sqla import db
21
+
22
+ class User(db.Model):
23
+ __tablename__ = "users"
24
+ ...
25
+
26
+ at import time, before any Flask application exists yet.
27
+
28
+ .. warning::
29
+
30
+ ``db`` is a **single-application** singleton. Calling ``init_app()``
31
+ while it is still bound to an application -- whether that's the same
32
+ app again or a different one -- raises ``RuntimeError`` rather than
33
+ silently swapping out ``db.engine``/``db.session``. Call
34
+ :meth:`~flask_exts.datastore.sqla.database.SqlaDb.dispose` first if you
35
+ genuinely intend to rebind this instance to a different app (this is
36
+ how the test suite reuses one ``db`` across many short-lived apps,
37
+ calling ``dispose()`` in each test's teardown). For a long-running
38
+ process that serves more than one Flask application at once, create a
39
+ separate ``SqlaDb()`` instance per application instead.
40
+
41
+ Configuration
42
+ -------------
43
+
44
+ .. list-table:: Database configuration options
45
+ :header-rows: 1
46
+ :widths: 28 72
47
+
48
+ * - Name
49
+ - Description
50
+ * - ``SQLALCHEMY_DATABASE_URI``
51
+ - **Required.** SQLAlchemy connection URL, e.g.
52
+ ``"sqlite:///app.db"`` or ``"postgresql+psycopg://user:pass@host/db"``.
53
+ ``init_app()`` raises ``RuntimeError`` if this is unset.
54
+ * - ``SQLALCHEMY_ECHO``
55
+ - Set to ``True`` to log every SQL statement. Default is ``False``.
56
+ * - ``SQLALCHEMY_ENGINE_OPTIONS``
57
+ - Dict of keyword arguments forwarded to SQLAlchemy's
58
+ ``create_engine()``. Use this for ``pool_size``, ``pool_recycle``,
59
+ ``pool_pre_ping``, etc. Takes precedence over the two options above
60
+ when keys overlap.
61
+ * - ``SQLALCHEMY_SESSION_OPTIONS``
62
+ - Dict of keyword arguments forwarded to ``sessionmaker()``, e.g.
63
+ ``{"expire_on_commit": False}``. Default is ``{}``.
64
+
65
+ In-memory SQLite
66
+ -----------------
67
+
68
+ When ``SQLALCHEMY_DATABASE_URI`` points at an in-memory SQLite database
69
+ (``"sqlite://"`` or any URI containing ``":memory:"``), ``init_app()``
70
+ automatically pins the engine to a single shared connection
71
+ (``poolclass=StaticPool``, ``connect_args={"check_same_thread": False}``).
72
+
73
+ This matters because SQLite's default pooling for in-memory databases hands
74
+ each thread its *own*, separately empty, in-memory database -- so without
75
+ this override, a request served on a different thread than the one that
76
+ created the schema would see no tables at all. This is the same workaround
77
+ Flask-SQLAlchemy applies for this case.
78
+
79
+ You can override the pool explicitly via ``SQLALCHEMY_ENGINE_OPTIONS`` if
80
+ you need different behavior.
81
+
82
+ Long-lived connections (MySQL/PostgreSQL)
83
+ ------------------------------------------
84
+
85
+ Database servers often close idle connections after a timeout (MySQL's
86
+ ``wait_timeout`` is a common culprit). If your process keeps a connection
87
+ pool open across requests, configure ``pool_pre_ping`` and/or
88
+ ``pool_recycle`` to avoid "server has gone away"-style errors::
89
+
90
+ app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
91
+ "pool_pre_ping": True,
92
+ "pool_recycle": 280,
93
+ }
94
+
95
+ Session scope
96
+ -------------
97
+
98
+ ``db.session`` is a SQLAlchemy ``scoped_session`` keyed on the current Flask
99
+ application context, matching Flask-SQLAlchemy's behavior: accessing
100
+ ``db.session`` inside a request (or any ``with app.app_context():`` block)
101
+ always returns the same session object for that context, and a fresh one
102
+ once the context ends. The session is removed automatically via
103
+ ``teardown_appcontext``.
104
+
105
+ Shutdown
106
+ --------
107
+
108
+ :meth:`~flask_exts.datastore.sqla.database.SqlaDb.dispose` releases the
109
+ session registry and the engine's connection pool, and marks the instance
110
+ unbound again (``db.app`` becomes ``None``), allowing a subsequent
111
+ ``init_app()`` call with a different app. It tolerates being called with
112
+ no active application context (e.g. from a shutdown hook that isn't
113
+ running inside a request), in which case it skips removing the session
114
+ registry entry (there's nothing addressable without a context) but still
115
+ disposes the engine.
116
+
117
+ API reference
118
+ --------------
119
+
120
+ .. automodule:: flask_exts.datastore.sqla._db
121
+
122
+ .. automodule:: flask_exts.datastore.sqla.database
123
+ :members:
@@ -5,10 +5,11 @@ build-backend = "hatchling.build"
5
5
  [project]
6
6
  name = "Flask-Exts"
7
7
  dynamic = ["version"]
8
- description = 'Flask extensions with admin, SQLAlchemy, babel, forms, fields, widgets, and so on.'
8
+ description = "Flask extensions with admin, SQLAlchemy, Babel, forms, fields, widgets, and more."
9
9
  readme = "README.rst"
10
- license = { file = "LICENSE.rst" }
11
- keywords = ["flask","babel", "admin", "form", "widget", "sql"]
10
+ license = "MIT"
11
+ license-files = ["LICENSE.rst"]
12
+ keywords = ["flask", "admin", "sqlalchemy", "babel", "form", "widget"]
12
13
  authors = [
13
14
  { name = "David Hua", email = "david.dong.hua@gmail.com" },
14
15
  ]
@@ -17,8 +18,9 @@ classifiers = [
17
18
  "Environment :: Web Environment",
18
19
  "Framework :: Flask",
19
20
  "Intended Audience :: Developers",
20
- "License :: OSI Approved :: MIT License",
21
- "Topic :: Software Development :: Build Tools",
21
+ "Operating System :: OS Independent",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
22
24
  "Programming Language :: Python",
23
25
  "Programming Language :: Python :: 3",
24
26
  "Programming Language :: Python :: 3.10",
@@ -28,40 +30,49 @@ classifiers = [
28
30
  ]
29
31
  requires-python = ">=3.10"
30
32
  dependencies = [
31
- "Flask>=3.0",
32
- "WTForms>=3.0",
33
- "SQLAlchemy>=2.0",
34
- "flask-babel>=4.0",
35
- "Flask-Login>=0.6",
36
- "tablib>=3.0",
37
- "PyJWT>=2.0",
38
- "pyotp>=2.0",
39
- "email-validator>=2.0",
33
+ "Flask>=3.0",
34
+ "WTForms>=3.0",
35
+ "SQLAlchemy>=2.0",
36
+ "flask-babel>=4.0",
37
+ "Flask-Login>=0.6",
38
+ "tablib>=3.0",
39
+ "PyJWT>=2.0",
40
+ "pyotp>=2.0",
41
+ "email-validator>=2.0",
40
42
  ]
41
43
 
44
+ [project.optional-dependencies]
45
+ dev = ["pytest", "djlint", "ruff"]
46
+
42
47
  [project.urls]
43
48
  Documentation = "https://flask-exts.readthedocs.io"
44
49
  Source = "https://github.com/ojso/flask-exts"
45
50
  Homepage = "https://github.com/ojso/flask-exts"
46
51
  Issues = "https://github.com/ojso/flask-exts/issues"
47
-
48
- [tool.hatch.version]
49
- path = "src/flask_exts/__about__.py"
52
+ Changelog = "https://flask-exts.readthedocs.io/en/latest/changes.html"
50
53
 
51
54
  [project.entry-points."flask.commands"]
52
55
  security = "flask_exts.commands:security_cli"
53
56
  datastore = "flask_exts.commands:datastore_cli"
54
57
 
58
+ [tool.hatch.version]
59
+ path = "src/flask_exts/__about__.py"
60
+ source = "code"
61
+
62
+ [tool.hatch.build.targets.wheel]
63
+ packages = ["src/flask_exts"]
64
+
55
65
  [tool.djlint]
56
- profile="jinja"
57
- indent=2
58
- no_indent_inner_html=true
59
- max_attribute_length=180
60
- max_line_length=180
61
- sort_attributes=true
62
- preserve_blank_lines=true
63
- ignore="T003"
66
+ profile = "jinja"
67
+ indent = 2
68
+ no_indent_inner_html = true
69
+ max_attribute_length = 180
70
+ max_line_length = 180
71
+ sort_attributes = true
72
+ preserve_blank_lines = true
73
+ ignore = "T003"
64
74
 
65
75
  [tool.pytest.ini_options]
76
+ minversion = "8.0"
66
77
  testpaths = ["tests"]
67
- addopts = "-ra"
78
+ addopts = "-ra --strict-markers --strict-config"
@@ -0,0 +1 @@
1
+ __version__ = "0.3.1"
@@ -119,7 +119,6 @@ class FilterMixin:
119
119
  name:
120
120
  Name of the field
121
121
  """
122
- return None
123
122
 
124
123
  def _get_filter_arg(self, index, flt):
125
124
  """
@@ -148,8 +147,8 @@ class FilterMixin:
148
147
 
149
148
  if active_filters:
150
149
  for i, pair in enumerate(active_filters):
151
- idx, flt_name, value = pair
152
- key = "flt%d_%s" % (i, self._get_filter_arg(idx, self._filters[idx]))
150
+ idx, _flt_name, value = pair
151
+ key = f"flt{i}_{self._get_filter_arg(idx, self._filters[idx])}"
153
152
  kwargs[key] = value
154
153
 
155
154
  return kwargs
@@ -10,8 +10,8 @@ from flask import (
10
10
  )
11
11
  from flask_babel import gettext, ngettext
12
12
 
13
- from ..exposer import expose_url
14
13
  from ...security.urls import safe_redirect_target
14
+ from ..exposer import expose_url
15
15
  from .base import BaseModelView
16
16
  from .type_formatters import BASE_FORMATTERS, DETAIL_FORMATTERS, EXPORT_FORMATTERS
17
17
 
@@ -315,17 +315,16 @@ class ModelView(BaseModelView):
315
315
 
316
316
  form = self._edit_form_class(obj=model)
317
317
 
318
- if form.validate_on_submit():
319
- if self.update_model(form, model):
320
- flash(gettext("Record was successfully saved."), "success")
321
- if "_add_another" in request.form:
322
- return redirect(self.get_url(".create_view", url=return_url))
323
- elif "_continue_editing" in request.form:
324
- return redirect(
325
- self.get_url(".edit_view", id=self.get_pk_value(model))
326
- )
327
- else:
328
- return redirect(self.get_save_return_url(model, is_created=False))
318
+ if form.validate_on_submit() and self.update_model(form, model):
319
+ flash(gettext("Record was successfully saved."), "success")
320
+ if "_add_another" in request.form:
321
+ return redirect(self.get_url(".create_view", url=return_url))
322
+ elif "_continue_editing" in request.form:
323
+ return redirect(
324
+ self.get_url(".edit_view", id=self.get_pk_value(model))
325
+ )
326
+ else:
327
+ return redirect(self.get_save_return_url(model, is_created=False))
329
328
 
330
329
  form_opts = {"widget_args": self.form_widget_args}
331
330
 
@@ -4,8 +4,8 @@ from .database_ext import DatabaseExtension
4
4
  from .emailer_ext import EmailerExtension
5
5
  from .frontend_ext import FrontendExtension
6
6
  from .login_ext import LoginExtension
7
+ from .portal_ext import PortalExtension
7
8
  from .security_ext import SecurityExtension
8
- from .startup_ext import StartupExtension
9
9
  from .usercenter_ext import UserCenterExtension
10
10
  from .userstore_ext import UserStoreExtension
11
11
 
@@ -19,7 +19,7 @@ BUILTIN_EXTENSIONS = (
19
19
  UserCenterExtension,
20
20
  UserStoreExtension,
21
21
  AdminExtension,
22
- StartupExtension,
22
+ PortalExtension,
23
23
  )
24
24
 
25
25
 
@@ -0,0 +1,14 @@
1
+ from ..extension_core.base import Extension
2
+ from ..portal.index_view import IndexView
3
+
4
+
5
+ class PortalExtension(Extension):
6
+ @property
7
+ def name(self) -> str:
8
+ return "portal"
9
+
10
+ def init_app(self, app):
11
+ pass
12
+
13
+ def get_admin_views(self):
14
+ return [(IndexView(), False)]
@@ -0,0 +1,24 @@
1
+ from ..extension_core.base import Extension
2
+ from ..security.core import Security
3
+ from ..signals import user_registered
4
+
5
+
6
+ class SecurityExtension(Extension):
7
+ @property
8
+ def name(self) -> str:
9
+ return "security"
10
+
11
+ def init_app(self, app):
12
+ self._security = Security()
13
+ self._security.init_app(app)
14
+ self._subscribe_signals(app)
15
+
16
+ def get_security(self):
17
+ return self._security
18
+
19
+ def _subscribe_signals(self, app):
20
+ @user_registered.connect_via(app)
21
+ def on_user_registered(sender, user, **extra):
22
+ """Send an email-verification token to freshly registered users."""
23
+ if user.email and not user.email_verified:
24
+ self._security.get_plugin("email_verification").send_token(user)
@@ -8,15 +8,9 @@ class UserCenterExtension(Extension):
8
8
  def name(self) -> str:
9
9
  return "usercenter"
10
10
 
11
- @property
12
- def dependencies(self) -> list[str]:
13
- return ["admin"]
14
-
15
11
  def init_app(self, app):
16
12
  self._usercenter = UserCenter()
17
13
  self._usercenter.init_app(app)
18
- self.register_views(app)
19
14
 
20
- def register_views(self, app):
21
- admin = app.extensions["exts"].get_extension("admin").get_admin()
22
- admin.register_view(UserView(), is_menu=False)
15
+ def get_admin_views(self):
16
+ return [(UserView(), False)]