wagtail 7.1.2__py3-none-any.whl → 7.2rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/copy_page.py +1 -1
  3. wagtail/actions/create_alias.py +1 -1
  4. wagtail/actions/delete_page.py +1 -1
  5. wagtail/actions/publish_page_revision.py +1 -1
  6. wagtail/actions/publish_revision.py +1 -1
  7. wagtail/actions/revert_to_page_revision.py +1 -1
  8. wagtail/actions/unpublish.py +1 -1
  9. wagtail/actions/unpublish_page.py +1 -1
  10. wagtail/admin/auth.py +3 -1
  11. wagtail/admin/checks.py +2 -2
  12. wagtail/admin/filters.py +28 -1
  13. wagtail/admin/forms/collections.py +1 -1
  14. wagtail/admin/forms/comments.py +1 -1
  15. wagtail/admin/forms/models.py +1 -1
  16. wagtail/admin/forms/pages.py +1 -1
  17. wagtail/admin/forms/tags.py +1 -1
  18. wagtail/admin/locale/cs/LC_MESSAGES/django.mo +0 -0
  19. wagtail/admin/locale/cs/LC_MESSAGES/django.po +25 -1
  20. wagtail/admin/locale/en/LC_MESSAGES/django.po +278 -192
  21. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +29 -15
  22. wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
  23. wagtail/admin/locale/it/LC_MESSAGES/django.po +3 -2
  24. wagtail/admin/locale/nl/LC_MESSAGES/django.mo +0 -0
  25. wagtail/admin/locale/nl/LC_MESSAGES/django.po +57 -3
  26. wagtail/admin/locale/nl/LC_MESSAGES/djangojs.mo +0 -0
  27. wagtail/admin/locale/nl/LC_MESSAGES/djangojs.po +8 -2
  28. wagtail/admin/locale/ru/LC_MESSAGES/django.mo +0 -0
  29. wagtail/admin/locale/ru/LC_MESSAGES/django.po +58 -1
  30. wagtail/admin/locale/tr/LC_MESSAGES/django.mo +0 -0
  31. wagtail/admin/locale/tr/LC_MESSAGES/django.po +3 -2
  32. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  33. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  34. wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
  35. wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
  36. wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
  37. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  38. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  39. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +2 -2
  40. wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
  41. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  42. wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
  43. wagtail/admin/static/wagtailadmin/js/icons.js +1 -1
  44. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  45. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  46. wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
  47. wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
  48. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  49. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  50. wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
  51. wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
  52. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  53. wagtail/admin/static/wagtailadmin/js/telepath/telepath.js +1 -1
  54. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  55. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  56. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +2 -2
  57. wagtail/admin/static/wagtailadmin/js/vendor/bootstrap-modal.js +1 -1
  58. wagtail/admin/static/wagtailadmin/js/vendor/bootstrap-transition.js +1 -1
  59. wagtail/admin/static/wagtailadmin/js/vendor/jquery-3.6.0.min.js +1 -1
  60. wagtail/admin/static/wagtailadmin/js/vendor/jquery-ui-1.13.2.min.js +1 -1
  61. wagtail/admin/static/wagtailadmin/js/vendor/jquery.datetimepicker.js +1 -1
  62. wagtail/admin/static/wagtailadmin/js/vendor/jquery.fileupload-process.js +1 -1
  63. wagtail/admin/static/wagtailadmin/js/vendor/jquery.fileupload.js +1 -1
  64. wagtail/admin/static/wagtailadmin/js/vendor/jquery.iframe-transport.js +1 -1
  65. wagtail/admin/static/wagtailadmin/js/vendor/tag-it.js +1 -1
  66. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  67. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +1 -1
  68. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  69. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  70. wagtail/admin/templates/wagtailadmin/account/account.html +2 -0
  71. wagtail/admin/templates/wagtailadmin/base.html +14 -0
  72. wagtail/admin/templates/wagtailadmin/generic/chooser/chooser.html +2 -1
  73. wagtail/admin/templates/wagtailadmin/generic/chooser/creation_form.html +2 -1
  74. wagtail/admin/templates/wagtailadmin/panels/multi_field_panel_child.html +1 -1
  75. wagtail/admin/templates/wagtailadmin/panels/object_list.html +1 -1
  76. wagtail/admin/templates/wagtailadmin/panels/tabbed_interface.html +3 -2
  77. wagtail/admin/templates/wagtailadmin/shared/formatted_field.html +1 -1
  78. wagtail/admin/templates/wagtailadmin/shared/forms/single_checkbox.html +1 -1
  79. wagtail/admin/templates/wagtailadmin/shared/keyboard_shortcuts_dialog.html +19 -0
  80. wagtail/admin/templates/wagtailadmin/shared/panel.html +1 -1
  81. wagtail/admin/templates/wagtailadmin/shared/set_privacy.html +15 -0
  82. wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html +28 -1
  83. wagtail/admin/templates/wagtailadmin/shared/workflow_history/detail.html +2 -2
  84. wagtail/admin/templates/wagtailadmin/{pages/listing/_ordering_header.html → tables/ordering_header.html} +2 -2
  85. wagtail/admin/templates/wagtailadmin/tables/title_cell.html +1 -1
  86. wagtail/admin/templates/wagtailadmin/widgets/{daterange_input.html → range_input.html} +1 -1
  87. wagtail/admin/templates/wagtailadmin/workflows/task_chooser/chooser.html +4 -2
  88. wagtail/admin/templatetags/wagtailadmin_tags.py +41 -22
  89. wagtail/admin/tests/api/test_pages.py +7 -7
  90. wagtail/admin/tests/api/test_renderer_classes.py +16 -0
  91. wagtail/admin/tests/pages/test_create_page.py +34 -2
  92. wagtail/admin/tests/pages/test_edit_page.py +128 -14
  93. wagtail/admin/tests/pages/test_explorer_view.py +34 -7
  94. wagtail/admin/tests/pages/test_reorder_page.py +11 -0
  95. wagtail/admin/tests/test_collections_views.py +12 -0
  96. wagtail/admin/tests/test_edit_handlers.py +3 -3
  97. wagtail/admin/tests/test_filters.py +2 -2
  98. wagtail/admin/tests/test_keyboard_shortcuts.py +52 -2
  99. wagtail/admin/tests/test_menu.py +0 -2
  100. wagtail/admin/tests/test_privacy.py +16 -16
  101. wagtail/admin/tests/test_templatetags.py +137 -0
  102. wagtail/admin/tests/test_workflows.py +34 -0
  103. wagtail/admin/tests/viewsets/test_model_viewset.py +322 -0
  104. wagtail/admin/ui/tables/orderable.py +73 -0
  105. wagtail/admin/ui/tables/pages.py +3 -13
  106. wagtail/admin/views/collection_privacy.py +6 -2
  107. wagtail/admin/views/generic/__init__.py +1 -0
  108. wagtail/admin/views/generic/mixins.py +20 -2
  109. wagtail/admin/views/generic/models.py +67 -1
  110. wagtail/admin/views/generic/ordering.py +79 -0
  111. wagtail/admin/views/home.py +3 -3
  112. wagtail/admin/views/page_privacy.py +5 -2
  113. wagtail/admin/views/pages/create.py +1 -1
  114. wagtail/admin/views/pages/edit.py +2 -2
  115. wagtail/admin/views/pages/listing.py +7 -42
  116. wagtail/admin/views/pages/move.py +1 -1
  117. wagtail/admin/views/pages/ordering.py +1 -1
  118. wagtail/admin/viewsets/base.py +1 -1
  119. wagtail/admin/viewsets/model.py +49 -1
  120. wagtail/admin/wagtail_hooks.py +2 -1
  121. wagtail/admin/widgets/slug.py +10 -10
  122. wagtail/api/v2/serializers.py +1 -1
  123. wagtail/api/v2/tests/test_renderer_classes.py +32 -0
  124. wagtail/apps.py +2 -0
  125. wagtail/bin/wagtail.py +1 -1
  126. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +14 -14
  127. wagtail/contrib/forms/locale/nl/LC_MESSAGES/django.mo +0 -0
  128. wagtail/contrib/forms/locale/nl/LC_MESSAGES/django.po +19 -2
  129. wagtail/contrib/forms/locale/ru/LC_MESSAGES/django.mo +0 -0
  130. wagtail/contrib/forms/locale/ru/LC_MESSAGES/django.po +18 -1
  131. wagtail/contrib/frontend_cache/tests.py +4 -2
  132. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +4 -4
  133. wagtail/contrib/redirects/tests/test_tmp_storages.py +20 -0
  134. wagtail/contrib/redirects/tmp_storages.py +1 -1
  135. wagtail/contrib/redirects/views.py +3 -3
  136. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +3 -3
  137. wagtail/contrib/search_promotions/locale/tr/LC_MESSAGES/django.mo +0 -0
  138. wagtail/contrib/search_promotions/locale/tr/LC_MESSAGES/django.po +43 -3
  139. wagtail/contrib/search_promotions/static/wagtailsearchpromotions/js/query-chooser-modal.js +1 -1
  140. wagtail/contrib/search_promotions/views/settings.py +2 -2
  141. wagtail/contrib/settings/locale/cs/LC_MESSAGES/django.mo +0 -0
  142. wagtail/contrib/settings/locale/cs/LC_MESSAGES/django.po +6 -1
  143. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +1 -1
  144. wagtail/contrib/settings/locale/nl/LC_MESSAGES/django.mo +0 -0
  145. wagtail/contrib/settings/locale/nl/LC_MESSAGES/django.po +6 -2
  146. wagtail/contrib/settings/locale/ru/LC_MESSAGES/django.mo +0 -0
  147. wagtail/contrib/settings/locale/ru/LC_MESSAGES/django.po +6 -1
  148. wagtail/contrib/settings/tests/site_specific/test_admin.py +40 -6
  149. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  150. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +1 -1
  151. wagtail/contrib/styleguide/templates/wagtailstyleguide/base.html +5 -5
  152. wagtail/contrib/table_block/blocks.py +1 -0
  153. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +5 -1
  154. wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
  155. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +1 -1
  156. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  157. wagtail/coreutils.py +5 -5
  158. wagtail/documents/forms.py +18 -1
  159. wagtail/documents/locale/en/LC_MESSAGES/django.po +10 -10
  160. wagtail/documents/locale/nl/LC_MESSAGES/django.mo +0 -0
  161. wagtail/documents/locale/nl/LC_MESSAGES/django.po +9 -0
  162. wagtail/documents/locale/ru/LC_MESSAGES/django.mo +0 -0
  163. wagtail/documents/locale/ru/LC_MESSAGES/django.po +9 -0
  164. wagtail/documents/models.py +1 -1
  165. wagtail/documents/static/wagtaildocs/js/add-multiple.js +1 -1
  166. wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
  167. wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
  168. wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
  169. wagtail/documents/templates/wagtaildocs/documents/add.html +0 -34
  170. wagtail/documents/tests/test_admin_views.py +132 -26
  171. wagtail/documents/tests/test_collection_privacy.py +18 -4
  172. wagtail/documents/tests/test_form_overrides.py +1 -1
  173. wagtail/documents/tests/test_search.py +21 -8
  174. wagtail/documents/views/documents.py +1 -1
  175. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  176. wagtail/embeds/static/wagtailembeds/js/embed-chooser-modal.js +1 -1
  177. wagtail/images/forms.py +16 -1
  178. wagtail/images/locale/cs/LC_MESSAGES/django.mo +0 -0
  179. wagtail/images/locale/cs/LC_MESSAGES/django.po +12 -1
  180. wagtail/images/locale/en/LC_MESSAGES/django.po +57 -46
  181. wagtail/images/locale/nl/LC_MESSAGES/django.mo +0 -0
  182. wagtail/images/locale/nl/LC_MESSAGES/django.po +37 -14
  183. wagtail/images/locale/ru/LC_MESSAGES/django.mo +0 -0
  184. wagtail/images/locale/ru/LC_MESSAGES/django.po +20 -1
  185. wagtail/images/models.py +1 -1
  186. wagtail/images/static/wagtailimages/js/add-multiple.js +1 -1
  187. wagtail/images/static/wagtailimages/js/focal-point-chooser.js +1 -1
  188. wagtail/images/static/wagtailimages/js/image-block.js +1 -1
  189. wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
  190. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  191. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  192. wagtail/images/static/wagtailimages/js/image-url-generator.js +1 -1
  193. wagtail/images/static/wagtailimages/js/vendor/jquery.Jcrop.min.js +1 -1
  194. wagtail/images/static/wagtailimages/js/vendor/jquery.fileupload-image.js +1 -1
  195. wagtail/images/static/wagtailimages/js/vendor/jquery.fileupload-validate.js +1 -1
  196. wagtail/images/static/wagtailimages/js/vendor/load-image.min.js +1 -1
  197. wagtail/images/templates/wagtailimages/chooser/chooser.html +22 -13
  198. wagtail/images/templates/wagtailimages/chooser/image_preview_column_cell.html +10 -0
  199. wagtail/images/templates/wagtailimages/chooser/results.html +24 -20
  200. wagtail/images/templates/wagtailimages/chooser/title_column_cell.html +15 -0
  201. wagtail/images/templates/wagtailimages/images/add.html +0 -34
  202. wagtail/images/templates/wagtailimages/images/index.html +3 -3
  203. wagtail/images/templates/wagtailimages/images/index_results.html +1 -1
  204. wagtail/images/templates/wagtailimages/images/layout_toggle_button.html +8 -7
  205. wagtail/images/templatetags/wagtailimages_tags.py +2 -2
  206. wagtail/images/tests/test_admin_views.py +87 -0
  207. wagtail/images/tests/test_form_overrides.py +1 -1
  208. wagtail/images/tests/test_models.py +48 -9
  209. wagtail/images/views/chooser.py +66 -2
  210. wagtail/locale/en/LC_MESSAGES/django.po +55 -55
  211. wagtail/locale/is_IS/LC_MESSAGES/django.mo +0 -0
  212. wagtail/locale/is_IS/LC_MESSAGES/django.po +3 -3
  213. wagtail/locale/nl/LC_MESSAGES/django.mo +0 -0
  214. wagtail/locale/nl/LC_MESSAGES/django.po +11 -2
  215. wagtail/locale/ru/LC_MESSAGES/django.mo +0 -0
  216. wagtail/locale/ru/LC_MESSAGES/django.po +11 -1
  217. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  218. wagtail/locales/locale/nl/LC_MESSAGES/django.mo +0 -0
  219. wagtail/locales/locale/nl/LC_MESSAGES/django.po +12 -1
  220. wagtail/locales/locale/ru/LC_MESSAGES/django.mo +0 -0
  221. wagtail/locales/locale/ru/LC_MESSAGES/django.po +10 -1
  222. wagtail/locales/views.py +2 -2
  223. wagtail/models/orderable.py +10 -0
  224. wagtail/models/pages.py +9 -11
  225. wagtail/models/sites.py +1 -1
  226. wagtail/models/workflows.py +8 -5
  227. wagtail/project_template/home/tests.py +6 -7
  228. wagtail/project_template/project_name/settings/base.py +9 -9
  229. wagtail/project_template/requirements.txt +1 -1
  230. wagtail/query.py +7 -2
  231. wagtail/rich_text/rewriters.py +1 -1
  232. wagtail/search/apps.py +4 -49
  233. wagtail/search/backends/__init__.py +1 -113
  234. wagtail/search/backends/base.py +1 -547
  235. wagtail/search/backends/database/__init__.py +1 -50
  236. wagtail/search/backends/database/fallback.py +1 -253
  237. wagtail/search/backends/database/mysql/mysql.py +1 -700
  238. wagtail/search/backends/database/mysql/query.py +1 -258
  239. wagtail/search/backends/database/postgres/postgres.py +1 -749
  240. wagtail/search/backends/database/postgres/query.py +1 -83
  241. wagtail/search/backends/database/postgres/weights.py +1 -63
  242. wagtail/search/backends/database/sqlite/query.py +1 -294
  243. wagtail/search/backends/database/sqlite/sqlite.py +1 -719
  244. wagtail/search/backends/database/sqlite/utils.py +1 -35
  245. wagtail/search/backends/deprecation.py +45 -0
  246. wagtail/search/backends/elasticsearch7.py +18 -1260
  247. wagtail/search/backends/elasticsearch8.py +21 -96
  248. wagtail/search/backends/elasticsearch9.py +35 -0
  249. wagtail/search/backends/opensearch2.py +35 -0
  250. wagtail/search/backends/opensearch3.py +35 -0
  251. wagtail/search/index.py +1 -358
  252. wagtail/search/locale/en/LC_MESSAGES/django.po +2 -10
  253. wagtail/search/management/commands/update_index.py +1 -205
  254. wagtail/search/management/commands/wagtail_update_index.py +1 -4
  255. wagtail/search/models.py +32 -158
  256. wagtail/search/query.py +1 -114
  257. wagtail/search/queryset.py +1 -43
  258. wagtail/search/signal_handlers.py +1 -24
  259. wagtail/search/tasks.py +1 -10
  260. wagtail/search/tests/test_elasticsearch.py +22 -0
  261. wagtail/search/utils.py +1 -206
  262. wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
  263. wagtail/snippets/locale/en/LC_MESSAGES/django.po +3 -3
  264. wagtail/snippets/locale/ru/LC_MESSAGES/django.mo +0 -0
  265. wagtail/snippets/locale/ru/LC_MESSAGES/django.po +8 -1
  266. wagtail/snippets/locale/tr/LC_MESSAGES/django.mo +0 -0
  267. wagtail/snippets/locale/tr/LC_MESSAGES/django.po +8 -1
  268. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  269. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  270. wagtail/snippets/tests/test_reordering.py +319 -0
  271. wagtail/snippets/tests/test_snippets.py +65 -12
  272. wagtail/snippets/views/snippets.py +16 -0
  273. wagtail/test/numberformat.py +30 -0
  274. wagtail/test/settings.py +35 -12
  275. wagtail/test/testapp/fields.py +12 -0
  276. wagtail/test/testapp/migrations/0056_commentablejsonpage.py +50 -0
  277. wagtail/test/testapp/migrations/0057_featurecompletetoy_sort_order.py +23 -0
  278. wagtail/test/testapp/migrations/0058_customlocktask.py +31 -0
  279. wagtail/test/testapp/models.py +27 -0
  280. wagtail/test/testapp/views.py +3 -1
  281. wagtail/test/utils/page_tests.py +17 -17
  282. wagtail/test/utils/template_tests.py +4 -6
  283. wagtail/test/utils/wagtail_tests.py +1 -2
  284. wagtail/tests/test_page_model.py +15 -0
  285. wagtail/{search/tests → tests}/test_page_search.py +29 -2
  286. wagtail/tests/test_search_fields.py +69 -0
  287. wagtail/tests/test_tests.py +62 -6
  288. wagtail/tests/test_workflow.py +25 -1
  289. wagtail/users/locale/cs/LC_MESSAGES/django.mo +0 -0
  290. wagtail/users/locale/cs/LC_MESSAGES/django.po +3 -0
  291. wagtail/users/locale/en/LC_MESSAGES/django.po +2 -2
  292. wagtail/users/locale/nl/LC_MESSAGES/django.mo +0 -0
  293. wagtail/users/locale/nl/LC_MESSAGES/django.po +6 -3
  294. wagtail/users/locale/ru/LC_MESSAGES/django.mo +0 -0
  295. wagtail/users/locale/ru/LC_MESSAGES/django.po +5 -1
  296. wagtail/users/locale/tr/LC_MESSAGES/django.mo +0 -0
  297. wagtail/users/locale/tr/LC_MESSAGES/django.po +78 -4
  298. wagtail/users/templates/wagtailusers/users/create.html +2 -0
  299. wagtail/users/templates/wagtailusers/users/edit.html +2 -0
  300. wagtail/users/tests/test_admin_views.py +4 -0
  301. wagtail/users/views/users.py +1 -1
  302. {wagtail-7.1.2.dist-info → wagtail-7.2rc1.dist-info}/METADATA +7 -6
  303. {wagtail-7.1.2.dist-info → wagtail-7.2rc1.dist-info}/RECORD +309 -315
  304. wagtail/admin/templates/wagtailadmin/collection_privacy/set_privacy.html +0 -13
  305. wagtail/admin/templates/wagtailadmin/page_privacy/set_privacy.html +0 -13
  306. wagtail/search/tests/__init__.py +0 -0
  307. wagtail/search/tests/elasticsearch_common_tests.py +0 -251
  308. wagtail/search/tests/test_backends.py +0 -1215
  309. wagtail/search/tests/test_db_backend.py +0 -62
  310. wagtail/search/tests/test_elasticsearch7_backend.py +0 -1452
  311. wagtail/search/tests/test_elasticsearch8_backend.py +0 -15
  312. wagtail/search/tests/test_index_functions.py +0 -256
  313. wagtail/search/tests/test_indexed_class.py +0 -157
  314. wagtail/search/tests/test_mysql_backend.py +0 -192
  315. wagtail/search/tests/test_postgres_backend.py +0 -210
  316. wagtail/search/tests/test_queries.py +0 -332
  317. wagtail/search/tests/test_related_fields.py +0 -102
  318. wagtail/search/tests/test_sqlite_backend.py +0 -52
  319. wagtail/test/search/__init__.py +0 -0
  320. wagtail/test/search/apps.py +0 -9
  321. wagtail/test/search/fixtures/search.json +0 -545
  322. wagtail/test/search/migrations/0001_initial.py +0 -146
  323. wagtail/test/search/migrations/0002_bookunindexed.py +0 -43
  324. wagtail/test/search/migrations/0003_book_summary.py +0 -18
  325. wagtail/test/search/migrations/__init__.py +0 -0
  326. wagtail/test/search/models.py +0 -137
  327. /wagtail/admin/templates/wagtailadmin/{pages/listing/_ordering_cell.html → tables/ordering_cell.html} +0 -0
  328. /wagtail/{search/checks.py → checks.py} +0 -0
  329. {wagtail-7.1.2.dist-info → wagtail-7.2rc1.dist-info}/WHEEL +0 -0
  330. {wagtail-7.1.2.dist-info → wagtail-7.2rc1.dist-info}/entry_points.txt +0 -0
  331. {wagtail-7.1.2.dist-info → wagtail-7.2rc1.dist-info}/licenses/LICENSE +0 -0
  332. {wagtail-7.1.2.dist-info → wagtail-7.2rc1.dist-info}/top_level.txt +0 -0
@@ -1,205 +1 @@
1
- import collections
2
-
3
- from django.conf import settings
4
- from django.core.management.base import BaseCommand
5
- from django.db import transaction
6
-
7
- from wagtail.search.backends import get_search_backend
8
- from wagtail.search.index import get_indexed_models
9
-
10
- DEFAULT_CHUNK_SIZE = 1000
11
-
12
-
13
- def group_models_by_index(backend, models):
14
- """
15
- This takes a search backend and a list of models. By calling the
16
- get_index_for_model method on the search backend, it groups the models into
17
- the indices that they will be indexed into.
18
-
19
- It returns an ordered mapping of indices to lists of models within each
20
- index.
21
-
22
- For example, Elasticsearch 2 requires all page models to be together, but
23
- separate from other content types (eg, images and documents) to prevent
24
- field mapping collisions:
25
-
26
- >>> group_models_by_index(elasticsearch2_backend, [
27
- ... wagtailcore.Page,
28
- ... myapp.HomePage,
29
- ... myapp.StandardPage,
30
- ... wagtailimages.Image
31
- ... ])
32
- {
33
- <Index wagtailcore_page>: [wagtailcore.Page, myapp.HomePage, myapp.StandardPage],
34
- <Index wagtailimages_image>: [wagtailimages.Image],
35
- }
36
- """
37
- indices = {}
38
- models_by_index = collections.OrderedDict()
39
-
40
- for model in models:
41
- index = backend.get_index_for_model(model)
42
-
43
- if index:
44
- indices.setdefault(index.name, index)
45
- models_by_index.setdefault(index.name, [])
46
- models_by_index[index.name].append(model)
47
-
48
- return collections.OrderedDict(
49
- [
50
- (indices[index_name], index_models)
51
- for index_name, index_models in models_by_index.items()
52
- ]
53
- )
54
-
55
-
56
- class Command(BaseCommand):
57
- def write(self, *args, **kwargs):
58
- """Helper function that respects verbosity when printing."""
59
- if self.verbosity > 0:
60
- self.stdout.write(*args, **kwargs)
61
-
62
- def update_backend(
63
- self, backend_name, schema_only=False, chunk_size=DEFAULT_CHUNK_SIZE
64
- ):
65
- self.write("Updating backend: " + backend_name)
66
-
67
- backend = get_search_backend(backend_name)
68
-
69
- if not backend.rebuilder_class:
70
- self.write("Backend '%s' doesn't require rebuilding" % backend_name)
71
- return
72
-
73
- models_grouped_by_index = group_models_by_index(
74
- backend, get_indexed_models()
75
- ).items()
76
- if not models_grouped_by_index:
77
- self.write(backend_name + ": No indices to rebuild")
78
-
79
- for index, models in models_grouped_by_index:
80
- self.write(backend_name + ": Rebuilding index %s" % index.name)
81
-
82
- # Start rebuild
83
- rebuilder = backend.rebuilder_class(index)
84
- index = rebuilder.start()
85
-
86
- # Add models
87
- for model in models:
88
- index.add_model(model)
89
-
90
- # Add objects
91
- object_count = 0
92
- if not schema_only:
93
- for model in models:
94
- self.write(
95
- "{}: {}.{} ".format(
96
- backend_name, model._meta.app_label, model.__name__
97
- ).ljust(35),
98
- ending="",
99
- )
100
-
101
- # Add items (chunk_size at a time)
102
- for chunk in self.print_iter_progress(
103
- self.queryset_chunks(
104
- model.get_indexed_objects().order_by("pk"), chunk_size
105
- )
106
- ):
107
- index.add_items(model, chunk)
108
- object_count += len(chunk)
109
-
110
- self.print_newline()
111
-
112
- # Finish rebuild
113
- rebuilder.finish()
114
-
115
- self.write(backend_name + ": indexed %d objects" % object_count)
116
- self.print_newline()
117
-
118
- def add_arguments(self, parser):
119
- parser.add_argument(
120
- "--backend",
121
- action="store",
122
- dest="backend_name",
123
- default=None,
124
- help="Specify a backend to update",
125
- )
126
- parser.add_argument(
127
- "--schema-only",
128
- action="store_true",
129
- dest="schema_only",
130
- default=False,
131
- help="Prevents loading any data into the index",
132
- )
133
- parser.add_argument(
134
- "--chunk_size",
135
- action="store",
136
- dest="chunk_size",
137
- default=DEFAULT_CHUNK_SIZE,
138
- type=int,
139
- help="Set number of records to be fetched at once for inserting into the index",
140
- )
141
-
142
- def handle(self, **options):
143
- self.verbosity = options["verbosity"]
144
-
145
- # Get list of backends to index
146
- if options["backend_name"]:
147
- # index only the passed backend
148
- backend_names = [options["backend_name"]]
149
- elif hasattr(settings, "WAGTAILSEARCH_BACKENDS"):
150
- # index all backends listed in settings
151
- backend_names = settings.WAGTAILSEARCH_BACKENDS.keys()
152
- else:
153
- # index the 'default' backend only
154
- backend_names = ["default"]
155
-
156
- # Update backends
157
- for backend_name in backend_names:
158
- self.update_backend(
159
- backend_name,
160
- schema_only=options.get("schema_only", False),
161
- chunk_size=options.get("chunk_size"),
162
- )
163
-
164
- def print_newline(self):
165
- self.write("")
166
-
167
- def print_iter_progress(self, iterable):
168
- """
169
- Print a progress meter while iterating over an iterable. Use it as part
170
- of a ``for`` loop::
171
-
172
- for item in self.print_iter_progress(big_long_list):
173
- self.do_expensive_computation(item)
174
-
175
- A ``.`` character is printed for every value in the iterable,
176
- a space every 10 items, and a new line every 50 items.
177
- """
178
- for i, value in enumerate(iterable, start=1):
179
- yield value
180
- self.write(".", ending="")
181
- if i % 40 == 0:
182
- self.print_newline()
183
- self.write(" " * 35, ending="")
184
-
185
- elif i % 10 == 0:
186
- self.write(" ", ending="")
187
-
188
- self.stdout.flush()
189
-
190
- # Atomic so the count of models doesn't change as it is iterated
191
- @transaction.atomic
192
- def queryset_chunks(self, qs, chunk_size=DEFAULT_CHUNK_SIZE):
193
- """
194
- Yield a queryset in chunks of at most ``chunk_size``. The chunk yielded
195
- will be a list, not a queryset. Iterating over the chunks is done in a
196
- transaction so that the order and count of items in the queryset
197
- remains stable.
198
- """
199
- i = 0
200
- while True:
201
- items = list(qs[i * chunk_size :][:chunk_size])
202
- if not items:
203
- break
204
- yield items
205
- i += 1
1
+ from wagtailmodelsearch.management.commands.rebuild_modelsearch_index import * # noqa: F403
@@ -1,4 +1 @@
1
- # Alias for the update_index command, to avoid clashes with other packages
2
- # that implement an update_index command (such as django-haystack)
3
-
4
- from wagtail.search.management.commands.update_index import Command # NOQA: F401
1
+ from wagtailmodelsearch.management.commands.rebuild_modelsearch_index import * # noqa: F403
wagtail/search/models.py CHANGED
@@ -1,175 +1,49 @@
1
- from django.apps import apps
2
- from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
3
- from django.contrib.contenttypes.models import ContentType
4
- from django.db import connection, models
5
- from django.db.models.fields import TextField
6
- from django.db.models.fields.related import OneToOneField
7
- from django.db.models.functions import Cast
8
- from django.db.models.sql.where import WhereNode
9
- from django.utils.translation import gettext_lazy as _
1
+ from django.db import models
2
+ from django.db.models import OneToOneField
3
+ from wagtailmodelsearch.abstract_models import (
4
+ AbstractIndexEntry,
5
+ AbstractSQLiteFTSIndexEntry,
6
+ )
10
7
 
11
- from .index import class_is_indexed
12
- from .utils import get_descendants_content_types_pks
8
+ # We import abstract models from the modelsearch app and define concrete implementations here in the
9
+ # wagtail.search app. This preserves backwards compatibility for existing Wagtail projects that have
10
+ # these models in the wagtailsearch namespace, and avoids the need to add modelsearch to INSTALLED_APPS.
13
11
 
14
12
 
15
- class TextIDGenericRelation(GenericRelation):
16
- auto_created = True
17
-
18
- def get_content_type_lookup(self, alias, remote_alias):
19
- field = self.remote_field.model._meta.get_field(self.content_type_field_name)
20
- return field.get_lookup("in")(
21
- field.get_col(remote_alias), get_descendants_content_types_pks(self.model)
22
- )
23
-
24
- def get_object_id_lookup(self, alias, remote_alias):
25
- from_field = self.remote_field.model._meta.get_field(self.object_id_field_name)
26
- to_field = self.model._meta.pk
27
- return from_field.get_lookup("exact")(
28
- from_field.get_col(remote_alias), Cast(to_field.get_col(alias), from_field)
29
- )
30
-
31
- def get_extra_restriction(self, alias, remote_alias):
32
- cond = WhereNode()
33
- cond.add(self.get_content_type_lookup(alias, remote_alias), "AND")
34
- cond.add(self.get_object_id_lookup(alias, remote_alias), "AND")
35
- return cond
36
-
37
- def resolve_related_fields(self):
38
- return []
39
-
40
-
41
- class BaseIndexEntry(models.Model):
13
+ class IndexEntry(AbstractIndexEntry):
42
14
  """
43
- This is an abstract class that only contains fields common to all database vendors.
44
- It should be extended by the models specific for each backend.
15
+ The IndexEntry model that will get created in the database.
45
16
  """
46
17
 
47
- content_type = models.ForeignKey(
48
- ContentType, on_delete=models.CASCADE, related_name="+"
49
- )
50
- # We do not use an IntegerField since primary keys are not always integers.
51
- object_id = models.CharField(max_length=50)
52
- content_object = GenericForeignKey()
53
-
54
- # TODO: Add per-object boosting.
55
- # This field stores the "Title Normalisation Factor"
56
- # This factor is multiplied onto the rank of the title field.
57
- # This allows us to apply a boost to results with shorter titles
58
- # elevating more specific matches to the top.
59
- title_norm = models.FloatField(default=1.0)
60
-
61
- wagtail_reference_index_ignore = True
62
-
63
- class Meta:
64
- unique_together = ("content_type", "object_id")
65
- verbose_name = _("index entry")
66
- verbose_name_plural = _("index entries")
67
- abstract = True
68
-
69
- def __str__(self):
70
- return f"{self.content_type.name}: {self.content_object}"
71
-
72
- @property
73
- def model(self):
74
- return self.content_type.model
75
-
76
- @classmethod
77
- def add_generic_relations(cls):
78
- for model in apps.get_models():
79
- if class_is_indexed(model):
80
- TextIDGenericRelation(cls).contribute_to_class(model, "index_entries")
81
-
82
-
83
- # AbstractIndexEntry will be defined depending on which database system we're using.
84
- if connection.vendor == "postgresql":
85
- from django.contrib.postgres.indexes import GinIndex
86
- from django.contrib.postgres.search import SearchVectorField
87
-
88
- class AbstractPostgresIndexEntry(BaseIndexEntry):
89
- """
90
- This class is the specific IndexEntry model for PostgreSQL database systems.
91
- It inherits the fields defined in BaseIndexEntry, and adds PostgreSQL-specific
92
- fields (tsvectors), plus indexes for doing full-text search on those fields.
93
- """
94
-
95
- # TODO: Add per-object boosting.
96
- autocomplete = SearchVectorField()
97
- title = SearchVectorField()
98
- body = SearchVectorField()
99
-
100
- class Meta(BaseIndexEntry.Meta):
101
- abstract = True
102
- # An additional computed GIN index on 'title || body' is created in a SQL migration
103
- # covers the default case of PostgresSearchQueryCompiler.get_index_vectors.
104
- indexes = [
105
- GinIndex(fields=["autocomplete"]),
106
- GinIndex(fields=["title"]),
107
- GinIndex(fields=["body"]),
108
- ]
109
-
110
- AbstractIndexEntry = AbstractPostgresIndexEntry
111
-
112
- elif connection.vendor == "sqlite":
113
- from wagtail.search.backends.database.sqlite.utils import fts5_available
114
-
115
- class AbstractSQLiteIndexEntry(BaseIndexEntry):
18
+ class Meta(AbstractIndexEntry.Meta):
116
19
  """
117
- This class is the specific IndexEntry model for SQLite database systems. The autocomplete, title, and body fields store additional
20
+ Contains everything in the AbstractIndexEntry Meta class, but makes this model concrete.
118
21
  """
119
22
 
120
- autocomplete = TextField(null=True)
121
- title = TextField(null=False)
122
- body = TextField(null=True)
123
-
124
- class Meta(BaseIndexEntry.Meta):
125
- abstract = True
126
-
127
- AbstractIndexEntry = AbstractSQLiteIndexEntry
128
-
129
- if fts5_available():
130
-
131
- class SQLiteFTSIndexEntry(models.Model):
132
- autocomplete = TextField(null=True)
133
- title = TextField(null=False)
134
- body = TextField(null=True)
135
- index_entry = OneToOneField(
136
- primary_key=True,
137
- to="wagtailsearch.indexentry",
138
- on_delete=models.CASCADE,
139
- db_column="rowid",
140
- )
23
+ abstract = False
141
24
 
142
- class Meta:
143
- db_table = "wagtailsearch_indexentry_fts"
144
25
 
145
- elif connection.vendor == "mysql":
26
+ if AbstractSQLiteFTSIndexEntry:
27
+ # The SQLite backend additionally requires a second model with a OneToOneField to IndexEntry. If a
28
+ # SQLite connection is in use, modelsearch will define a AbstractSQLiteFTSIndexEntry model
29
+ # (otherwise this will be None).
146
30
 
147
- class AbstractMySQLIndexEntry(BaseIndexEntry):
31
+ class SQLiteFTSIndexEntry(AbstractSQLiteFTSIndexEntry):
148
32
  """
149
- This class is the specific IndexEntry model for MySQL database systems.
33
+ The SQLite FTS IndexEntry model that will get created in the database if using SQLite with FTS5 support.
150
34
  """
151
35
 
152
- autocomplete = TextField(null=True)
153
- title = TextField(null=False)
154
- body = TextField(null=True)
155
-
156
- class Meta(BaseIndexEntry.Meta):
157
- abstract = True
158
-
159
- AbstractIndexEntry = AbstractMySQLIndexEntry
160
-
161
- else:
162
- AbstractIndexEntry = BaseIndexEntry
163
-
164
-
165
- class IndexEntry(AbstractIndexEntry):
166
- """
167
- The IndexEntry model that will get created in the database.
168
- """
36
+ index_entry = OneToOneField(
37
+ primary_key=True,
38
+ to=IndexEntry,
39
+ on_delete=models.CASCADE,
40
+ db_column="rowid",
41
+ )
169
42
 
170
- class Meta(AbstractIndexEntry.Meta):
171
- """
172
- Contains everything in the AbstractIndexEntry Meta class, but makes this model concrete.
173
- """
43
+ class Meta(AbstractSQLiteFTSIndexEntry.Meta):
44
+ """
45
+ Contains everything in the AbstractSQLiteFTSIndexEntry Meta class, but makes this model concrete.
46
+ """
174
47
 
175
- abstract = False
48
+ abstract = False
49
+ db_table = "wagtailsearch_indexentry_fts"
wagtail/search/query.py CHANGED
@@ -1,114 +1 @@
1
- #
2
- # Base classes
3
- #
4
-
5
-
6
- class SearchQuery:
7
- def __and__(self, other):
8
- return And([self, other])
9
-
10
- def __or__(self, other):
11
- return Or([self, other])
12
-
13
- def __invert__(self):
14
- return Not(self)
15
-
16
- def __repr__(self):
17
- raise NotImplementedError
18
-
19
-
20
- #
21
- # Basic query classes
22
- #
23
-
24
-
25
- class PlainText(SearchQuery):
26
- OPERATORS = ["and", "or"]
27
- DEFAULT_OPERATOR = "and"
28
-
29
- def __init__(
30
- self, query_string: str, operator: str = DEFAULT_OPERATOR, boost: float = 1
31
- ):
32
- self.query_string = query_string
33
- self.operator = operator.lower()
34
- if self.operator not in self.OPERATORS:
35
- raise ValueError("`operator` must be either 'or' or 'and'.")
36
- self.boost = boost
37
-
38
- def __repr__(self):
39
- return "<PlainText {} operator={} boost={}>".format(
40
- repr(self.query_string), repr(self.operator), repr(self.boost)
41
- )
42
-
43
-
44
- class Phrase(SearchQuery):
45
- def __init__(self, query_string: str):
46
- self.query_string = query_string
47
-
48
- def __repr__(self):
49
- return f"<Phrase {repr(self.query_string)}>"
50
-
51
-
52
- class Fuzzy(SearchQuery):
53
- OPERATORS = ["and", "or"]
54
- DEFAULT_OPERATOR = "or"
55
-
56
- def __init__(self, query_string: str, operator: str = DEFAULT_OPERATOR):
57
- self.query_string = query_string
58
- self.operator = operator.lower()
59
- if self.operator not in self.OPERATORS:
60
- raise ValueError("`operator` must be either 'or' or 'and'.")
61
-
62
- def __repr__(self):
63
- return f"<Fuzzy {repr(self.query_string)} operator={repr(self.operator)}>"
64
-
65
-
66
- class MatchAll(SearchQuery):
67
- def __repr__(self):
68
- return "<MatchAll>"
69
-
70
-
71
- class Boost(SearchQuery):
72
- def __init__(self, subquery: SearchQuery, boost: float):
73
- self.subquery = subquery
74
- self.boost = boost
75
-
76
- def __repr__(self):
77
- return f"<Boost {repr(self.subquery)} boost={repr(self.boost)}>"
78
-
79
-
80
- #
81
- # Combinators
82
- #
83
-
84
-
85
- class And(SearchQuery):
86
- def __init__(self, subqueries):
87
- self.subqueries = subqueries
88
-
89
- def __repr__(self):
90
- return "<And {}>".format(
91
- " ".join(repr(subquery) for subquery in self.subqueries)
92
- )
93
-
94
-
95
- class Or(SearchQuery):
96
- def __init__(self, subqueries):
97
- self.subqueries = subqueries
98
-
99
- def __repr__(self):
100
- return "<Or {}>".format(
101
- " ".join(repr(subquery) for subquery in self.subqueries)
102
- )
103
-
104
-
105
- class Not(SearchQuery):
106
- def __init__(self, subquery: SearchQuery):
107
- self.subquery = subquery
108
-
109
- def __repr__(self):
110
- return f"<Not {repr(self.subquery)}>"
111
-
112
-
113
- MATCH_ALL = MatchAll()
114
- MATCH_NONE = Not(MATCH_ALL)
1
+ from wagtailmodelsearch.query import * # noqa: F403
@@ -1,43 +1 @@
1
- from wagtail.search.backends import get_search_backend
2
-
3
-
4
- class SearchableQuerySetMixin:
5
- def search(
6
- self,
7
- query,
8
- fields=None,
9
- operator=None,
10
- order_by_relevance=True,
11
- backend="default",
12
- ):
13
- """
14
- This runs a search query on all the items in the QuerySet
15
- """
16
- search_backend = get_search_backend(backend)
17
- return search_backend.search(
18
- query,
19
- self,
20
- fields=fields,
21
- operator=operator,
22
- order_by_relevance=order_by_relevance,
23
- )
24
-
25
- def autocomplete(
26
- self,
27
- query,
28
- fields=None,
29
- operator=None,
30
- order_by_relevance=True,
31
- backend="default",
32
- ):
33
- """
34
- This runs an autocomplete query on all the items in the QuerySet
35
- """
36
- search_backend = get_search_backend(backend)
37
- return search_backend.autocomplete(
38
- query,
39
- self,
40
- fields=fields,
41
- operator=operator,
42
- order_by_relevance=order_by_relevance,
43
- )
1
+ from wagtailmodelsearch.queryset import * # noqa: F403
@@ -1,24 +1 @@
1
- from django.db.models.signals import post_delete, post_save
2
-
3
- from . import index
4
- from .tasks import insert_or_update_object_task
5
-
6
-
7
- def post_save_signal_handler(instance, **kwargs):
8
- insert_or_update_object_task.enqueue(
9
- instance._meta.app_label, instance._meta.model_name, str(instance.pk)
10
- )
11
-
12
-
13
- def post_delete_signal_handler(instance, **kwargs):
14
- index.remove_object(instance)
15
-
16
-
17
- def register_signal_handlers():
18
- # Loop through list and register signal handlers for each one
19
- for model in index.get_indexed_models():
20
- if not getattr(model, "search_auto_update", True):
21
- continue
22
-
23
- post_save.connect(post_save_signal_handler, sender=model)
24
- post_delete.connect(post_delete_signal_handler, sender=model)
1
+ from wagtailmodelsearch.signal_handlers import * # noqa: F403
wagtail/search/tasks.py CHANGED
@@ -1,10 +1 @@
1
- from django.apps import apps
2
- from django_tasks import task
3
-
4
- from wagtail.search import index
5
-
6
-
7
- @task()
8
- def insert_or_update_object_task(app_label, model_name, pk):
9
- model = apps.get_model(app_label, model_name)
10
- index.insert_or_update_object(model.objects.get(pk=pk))
1
+ from wagtailmodelsearch.tasks import * # noqa: F403
@@ -0,0 +1,22 @@
1
+ import unittest
2
+
3
+ from django.conf import settings
4
+ from django.test import TestCase
5
+
6
+ from wagtail.search.backends import get_search_backend
7
+ from wagtail.utils.deprecation import RemovedInWagtail80Warning
8
+
9
+
10
+ @unittest.skipIf(
11
+ "elasticsearch_with_index_option" not in settings.WAGTAILSEARCH_BACKENDS,
12
+ "No elasticsearch backend active",
13
+ )
14
+ class TestIndexOptionDeprecation(TestCase):
15
+ def test_index_option_deprecation_warning(self):
16
+ with self.assertWarnsMessage(
17
+ RemovedInWagtail80Warning,
18
+ "The INDEX option on Elasticsearch / OpenSearch backends is deprecated",
19
+ ):
20
+ backend = get_search_backend("elasticsearch_with_index_option")
21
+
22
+ self.assertEqual(backend.index_prefix, "wagtailtest_")