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,719 +1 @@
1
- from collections import OrderedDict
2
- from functools import reduce
3
-
4
- from django.db import (
5
- NotSupportedError,
6
- connections,
7
- router,
8
- transaction,
9
- )
10
- from django.db.models import Avg, Count, F, Manager, Q, TextField
11
- from django.db.models.constants import LOOKUP_SEP
12
- from django.db.models.functions import Cast, Length
13
- from django.db.utils import OperationalError
14
- from django.utils.encoding import force_str
15
- from django.utils.functional import cached_property
16
-
17
- from ....index import AutocompleteField, RelatedFields, SearchField, get_indexed_models
18
- from ....models import IndexEntry, SQLiteFTSIndexEntry
19
- from ....query import And, MatchAll, Not, Or, Phrase, PlainText
20
- from ....utils import (
21
- ADD,
22
- MUL,
23
- OR,
24
- get_content_type_pk,
25
- get_descendants_content_types_pks,
26
- )
27
- from ...base import (
28
- BaseSearchBackend,
29
- BaseSearchQueryCompiler,
30
- BaseSearchResults,
31
- FilterFieldError,
32
- )
33
- from .query import (
34
- BM25,
35
- AndNot,
36
- Lexeme,
37
- MatchExpression,
38
- SearchQueryExpression,
39
- normalize,
40
- )
41
-
42
-
43
- class ObjectIndexer:
44
- """
45
- Responsible for extracting data from an object to be inserted into the index.
46
- """
47
-
48
- def __init__(self, obj, backend):
49
- self.obj = obj
50
- self.search_fields = obj.get_search_fields()
51
- self.config = backend.config
52
-
53
- def prepare_value(self, value):
54
- if isinstance(value, str):
55
- return value
56
-
57
- elif isinstance(value, list):
58
- return ", ".join(self.prepare_value(item) for item in value)
59
-
60
- elif isinstance(value, dict):
61
- return ", ".join(self.prepare_value(item) for item in value.values())
62
-
63
- return force_str(value)
64
-
65
- def prepare_field(self, obj, field):
66
- if isinstance(field, SearchField):
67
- yield (field, self.prepare_value(field.get_value(obj)))
68
-
69
- elif isinstance(field, AutocompleteField):
70
- yield (field, self.prepare_value(field.get_value(obj)))
71
-
72
- elif isinstance(field, RelatedFields):
73
- sub_obj = field.get_value(obj)
74
- if sub_obj is None:
75
- return
76
-
77
- if isinstance(sub_obj, Manager):
78
- sub_objs = sub_obj.all()
79
-
80
- else:
81
- if callable(sub_obj):
82
- sub_obj = sub_obj()
83
-
84
- sub_objs = [sub_obj]
85
-
86
- for sub_obj in sub_objs:
87
- for sub_field in field.fields:
88
- yield from self.prepare_field(sub_obj, sub_field)
89
-
90
- @cached_property
91
- def id(self):
92
- """
93
- Returns the value to use as the ID of the record in the index
94
- """
95
- return force_str(self.obj.pk)
96
-
97
- @cached_property
98
- def title(self):
99
- """
100
- Returns all values to index as "title". This is the value of all SearchFields that have the field_name 'title'
101
- """
102
- texts = []
103
- for field in self.search_fields:
104
- for current_field, value in self.prepare_field(self.obj, field):
105
- if (
106
- isinstance(current_field, SearchField)
107
- and current_field.field_name == "title"
108
- ):
109
- texts.append(value)
110
-
111
- return " ".join(texts)
112
-
113
- @cached_property
114
- def body(self):
115
- """
116
- Returns all values to index as "body". This is the value of all SearchFields excluding the title
117
- """
118
- texts = []
119
- for field in self.search_fields:
120
- for current_field, value in self.prepare_field(self.obj, field):
121
- if (
122
- isinstance(current_field, SearchField)
123
- and not current_field.field_name == "title"
124
- ):
125
- texts.append(value)
126
-
127
- return " ".join(texts)
128
-
129
- @cached_property
130
- def autocomplete(self):
131
- """
132
- Returns all values to index as "autocomplete". This is the value of all AutocompleteFields
133
- """
134
- texts = []
135
- for field in self.search_fields:
136
- for current_field, value in self.prepare_field(self.obj, field):
137
- if isinstance(current_field, AutocompleteField):
138
- texts.append(value)
139
-
140
- return " ".join(texts)
141
-
142
- def as_vector(self, texts, for_autocomplete=False):
143
- """
144
- Converts an array of strings into a SearchVector that can be indexed.
145
- """
146
- texts = [(text.strip(), weight) for text, weight in texts]
147
- texts = [(text, weight) for text, weight in texts if text]
148
-
149
- return " ".join(texts)
150
-
151
-
152
- class Index:
153
- def __init__(self, backend):
154
- self.backend = backend
155
- self.name = self.backend.index_name
156
-
157
- self.read_connection = connections[router.db_for_read(IndexEntry)]
158
- self.write_connection = connections[router.db_for_write(IndexEntry)]
159
-
160
- if (
161
- self.read_connection.vendor != "sqlite"
162
- or self.write_connection.vendor != "sqlite"
163
- ):
164
- raise NotSupportedError(
165
- "You must select a SQLite database to use the SQLite search backend."
166
- )
167
-
168
- self.entries = IndexEntry._default_manager.all()
169
-
170
- def add_model(self, model):
171
- pass
172
-
173
- def refresh(self):
174
- pass
175
-
176
- def _refresh_title_norms(self, full=False):
177
- """
178
- Refreshes the value of the title_norm field.
179
-
180
- This needs to be set to 'lavg/ld' where:
181
- - lavg is the average length of titles in all documents (also in terms)
182
- - ld is the length of the title field in this document (in terms)
183
- """
184
-
185
- lavg = (
186
- self.entries.annotate(title_length=Length("title"))
187
- .filter(title_length__gt=0)
188
- .aggregate(Avg("title_length"))["title_length__avg"]
189
- )
190
-
191
- if full:
192
- # Update the whole table
193
- # This is the most accurate option but requires a full table rewrite
194
- # so we can't do it too often as it could lead to locking issues.
195
- entries = self.entries
196
-
197
- else:
198
- # Only update entries where title_norm is 1.0
199
- # This is the default value set on new entries.
200
- # It's possible that other entries could have this exact value but there shouldn't be too many of those
201
- entries = self.entries.filter(title_norm=1.0)
202
-
203
- entries.annotate(title_length=Length("title")).filter(
204
- title_length__gt=0
205
- ).update(title_norm=lavg / F("title_length"))
206
-
207
- def delete_stale_model_entries(self, model):
208
- existing_pks = model._default_manager.annotate(
209
- object_id=Cast("pk", TextField())
210
- ).values("object_id")
211
- content_types_pks = get_descendants_content_types_pks(model)
212
- stale_entries = self.entries.filter(
213
- content_type_id__in=content_types_pks
214
- ).exclude(object_id__in=existing_pks)
215
- stale_entries.delete()
216
-
217
- def delete_stale_entries(self):
218
- for model in get_indexed_models():
219
- # We don’t need to delete stale entries for non-root models,
220
- # since we already delete them by deleting roots.
221
- if not model._meta.parents:
222
- self.delete_stale_model_entries(model)
223
-
224
- def add_item(self, obj):
225
- self.add_items(obj._meta.model, [obj])
226
-
227
- def add_items_update_then_create(self, content_type_pk, indexers):
228
- ids_and_data = {}
229
- for indexer in indexers:
230
- ids_and_data[indexer.id] = (
231
- indexer.title,
232
- indexer.autocomplete,
233
- indexer.body,
234
- )
235
-
236
- index_entries_for_ct = self.entries.filter(content_type_id=content_type_pk)
237
- indexed_ids = frozenset(
238
- index_entries_for_ct.filter(object_id__in=ids_and_data.keys()).values_list(
239
- "object_id", flat=True
240
- )
241
- )
242
- for indexed_id in indexed_ids:
243
- title, autocomplete, body = ids_and_data[indexed_id]
244
- index_entries_for_ct.filter(object_id=indexed_id).update(
245
- title=title, autocomplete=autocomplete, body=body
246
- )
247
-
248
- to_be_created = []
249
- for object_id in ids_and_data.keys():
250
- if object_id not in indexed_ids:
251
- title, autocomplete, body = ids_and_data[object_id]
252
- to_be_created.append(
253
- IndexEntry(
254
- content_type_id=content_type_pk,
255
- object_id=object_id,
256
- title=title,
257
- autocomplete=autocomplete,
258
- body=body,
259
- )
260
- )
261
-
262
- self.entries.bulk_create(to_be_created)
263
-
264
- self._refresh_title_norms()
265
-
266
- def add_items(self, model, objs):
267
- search_fields = model.get_search_fields()
268
- if not search_fields:
269
- return
270
-
271
- indexers = [ObjectIndexer(obj, self.backend) for obj in objs]
272
-
273
- # TODO: Delete unindexed objects while dealing with proxy models.
274
- if indexers:
275
- content_type_pk = get_content_type_pk(model)
276
-
277
- update_method = self.add_items_update_then_create
278
- update_method(content_type_pk, indexers)
279
-
280
- def delete_item(self, item):
281
- item.index_entries.all()._raw_delete(using=self.write_connection.alias)
282
-
283
- def __str__(self):
284
- return self.name
285
-
286
-
287
- class SQLiteSearchRebuilder:
288
- def __init__(self, index):
289
- self.index = index
290
-
291
- def start(self):
292
- self.index.delete_stale_entries()
293
- return self.index
294
-
295
- def finish(self):
296
- self.index._refresh_title_norms(full=True)
297
-
298
-
299
- class SQLiteSearchAtomicRebuilder(SQLiteSearchRebuilder):
300
- def __init__(self, index):
301
- super().__init__(index)
302
- self.transaction = transaction.atomic(using=index.write_connection.alias)
303
- self.transaction_opened = False
304
-
305
- def start(self):
306
- self.transaction.__enter__()
307
- self.transaction_opened = True
308
- return super().start()
309
-
310
- def finish(self):
311
- self.index._refresh_title_norms(full=True)
312
-
313
- self.transaction.__exit__(None, None, None)
314
- self.transaction_opened = False
315
-
316
- def __del__(self):
317
- # TODO: Implement a cleaner way to close the connection on failure.
318
- if self.transaction_opened:
319
- self.transaction.needs_rollback = True
320
- self.finish()
321
-
322
-
323
- class SQLiteSearchQueryCompiler(BaseSearchQueryCompiler):
324
- DEFAULT_OPERATOR = "AND"
325
- LAST_TERM_IS_PREFIX = False
326
- TARGET_SEARCH_FIELD_TYPE = SearchField
327
- FTS_TABLE_FIELDS = ["title", "body"]
328
- HANDLES_ORDER_BY_EXPRESSIONS = True
329
-
330
- def __init__(self, *args, **kwargs):
331
- super().__init__(*args, **kwargs)
332
-
333
- local_search_fields = self.get_search_fields_for_model()
334
-
335
- if self.fields is None:
336
- # search over the fields defined on the current model
337
- self.search_fields = local_search_fields
338
- else:
339
- # build a search_fields set from the passed definition,
340
- # which may involve traversing relations
341
- self.search_fields = {
342
- field_lookup: self.get_search_field(
343
- field_lookup, fields=local_search_fields
344
- )
345
- for field_lookup in self.fields
346
- }
347
-
348
- def get_config(self, backend):
349
- return backend.config
350
-
351
- def get_search_fields_for_model(self):
352
- return self.queryset.model.get_searchable_search_fields()
353
-
354
- def get_search_field(self, field_lookup, fields=None):
355
- if fields is None:
356
- fields = self.search_fields
357
-
358
- if LOOKUP_SEP in field_lookup:
359
- field_lookup, sub_field_name = field_lookup.split(LOOKUP_SEP, 1)
360
- else:
361
- sub_field_name = None
362
-
363
- for field in fields:
364
- if (
365
- isinstance(field, self.TARGET_SEARCH_FIELD_TYPE)
366
- and field.field_name == field_lookup
367
- ):
368
- return field
369
-
370
- # Note: Searching on a specific related field using
371
- # `.search(fields=…)` is not yet supported by Wagtail.
372
- # This method anticipates by already implementing it.
373
- if isinstance(field, RelatedFields) and field.field_name == field_lookup:
374
- return self.get_search_field(sub_field_name, field.fields)
375
-
376
- def build_search_query_content(self, query, config=None):
377
- """
378
- Takes a SearchQuery and returns another SearchQuery object, which can be used to construct the query in SQL.
379
- """
380
- if isinstance(query, PlainText):
381
- terms = query.query_string.split()
382
- if not terms:
383
- return None
384
-
385
- last_term = terms.pop()
386
-
387
- lexemes = Lexeme(
388
- last_term, prefix=self.LAST_TERM_IS_PREFIX
389
- ) # Combine all terms into a single lexeme.
390
- for term in terms:
391
- new_lexeme = Lexeme(term)
392
-
393
- if query.operator.upper() == "AND":
394
- lexemes &= new_lexeme
395
- else:
396
- lexemes |= new_lexeme
397
-
398
- return SearchQueryExpression(lexemes, config=config)
399
-
400
- elif isinstance(query, Phrase):
401
- return SearchQueryExpression(query.query_string)
402
-
403
- elif isinstance(query, AndNot):
404
- # Combine the two sub-queries into a query of the form `(first) AND NOT (second)`.
405
- subquery_a = self.build_search_query_content(
406
- query.subquery_a, config=config
407
- )
408
- subquery_b = self.build_search_query_content(
409
- query.subquery_b, config=config
410
- )
411
- combined_query = subquery_a._combine(subquery_b, "NOT")
412
- return combined_query
413
-
414
- elif isinstance(query, (And, Or)):
415
- subquery_lexemes = [
416
- self.build_search_query_content(subquery, config=config)
417
- for subquery in query.subqueries
418
- ]
419
-
420
- is_and = isinstance(query, And)
421
-
422
- if is_and:
423
- return reduce(lambda a, b: a & b, subquery_lexemes)
424
- else:
425
- return reduce(lambda a, b: a | b, subquery_lexemes)
426
-
427
- raise NotImplementedError(
428
- "`%s` is not supported by the SQLite search backend."
429
- % query.__class__.__name__
430
- )
431
-
432
- def build_search_query(self, query, config=None):
433
- if isinstance(query, MatchAll):
434
- return query
435
- if isinstance(query, Not):
436
- unwrapped_query = query.subquery
437
- built_query = Not(
438
- self.build_search_query(unwrapped_query, config=config)
439
- ) # We don't take the Not operator into account.
440
- else:
441
- built_query = self.build_search_query_content(query, config=config)
442
- return built_query
443
-
444
- def build_tsrank(self, vector, query, config=None, boost=1.0):
445
- if isinstance(query, (Phrase, PlainText, Not)):
446
- rank_expression = BM25()
447
-
448
- if boost != 1.0:
449
- rank_expression *= boost
450
-
451
- return rank_expression
452
-
453
- elif isinstance(query, And):
454
- return (
455
- MUL(
456
- 1 + self.build_tsrank(vector, subquery, config=config, boost=boost)
457
- for subquery in query.subqueries
458
- )
459
- - 1
460
- )
461
-
462
- elif isinstance(query, Or):
463
- return ADD(
464
- self.build_tsrank(vector, subquery, config=config, boost=boost)
465
- for subquery in query.subqueries
466
- ) / (len(query.subqueries) or 1)
467
-
468
- raise NotImplementedError(
469
- "`%s` is not supported by the SQLite search backend."
470
- % query.__class__.__name__
471
- )
472
-
473
- def get_index_vectors(self):
474
- return [
475
- (F("index_entries__title"), F("index_entries__title_norm")),
476
- (F("index_entries__body"), 1.0),
477
- ]
478
-
479
- def get_search_vectors(self):
480
- return self.get_index_vectors()
481
-
482
- def _build_rank_expression(self, vectors, config):
483
- # TODO: Come up with my own expression class that compiles down to bm25
484
-
485
- rank_expressions = [
486
- self.build_tsrank(vector, self.query, config=config) * boost
487
- for vector, boost in vectors
488
- ]
489
-
490
- rank_expression = rank_expressions[0]
491
- for other_rank_expression in rank_expressions[1:]:
492
- rank_expression += other_rank_expression
493
-
494
- return rank_expression
495
-
496
- def search(self, config, start, stop, score_field=None):
497
- normalized_query = normalize(self.query)
498
-
499
- if isinstance(normalized_query, MatchAll):
500
- return self.queryset[start:stop]
501
-
502
- elif isinstance(normalized_query, Not) and isinstance(
503
- normalized_query.subquery, MatchAll
504
- ):
505
- return self.queryset.none()
506
-
507
- if isinstance(normalized_query, Not):
508
- normalized_query = normalized_query.subquery
509
- negated = True
510
- else:
511
- negated = False
512
-
513
- search_query = self.build_search_query(
514
- normalized_query, config=config
515
- ) # We build a search query here, for example: "%s MATCH '(hello AND world)'"
516
- vectors = self.get_search_vectors()
517
- rank_expression = self._build_rank_expression(vectors, config)
518
-
519
- combined_vector = vectors[
520
- 0
521
- ][
522
- 0
523
- ] # We create a combined vector for the search results queryset. We start with the first vector and build from there.
524
- for vector, boost in vectors[1:]:
525
- combined_vector = combined_vector._combine(
526
- vector, " ", False
527
- ) # We add the subsequent vectors to the combined vector.
528
-
529
- # Build the FTS match expression.
530
- expr = MatchExpression(self.fields or self.FTS_TABLE_FIELDS, search_query)
531
- # Perform the FTS search. We'll get entries in the SQLiteFTSIndexEntry model.
532
- objs = (
533
- SQLiteFTSIndexEntry.objects.filter(expr)
534
- .select_related("index_entry")
535
- .filter(
536
- index_entry__content_type__in=get_descendants_content_types_pks(
537
- self.queryset.model
538
- )
539
- )
540
- )
541
-
542
- if self.order_by_relevance:
543
- objs = objs.order_by(BM25().desc())
544
- elif not objs.query.order_by:
545
- # Adds a default ordering to avoid issue #3729.
546
- queryset = objs.order_by("-pk")
547
- rank_expression = F("pk")
548
-
549
- from django.db import connection
550
- from django.db.models.sql.subqueries import InsertQuery
551
-
552
- compiler = InsertQuery(IndexEntry).get_compiler(connection=connection)
553
-
554
- try:
555
- obj_ids = [
556
- obj.index_entry.object_id for obj in objs
557
- ] # Get the IDs of the objects that matched. They're stored in the IndexEntry model, so we need to get that first.
558
- except OperationalError as e:
559
- raise OperationalError(
560
- str(e)
561
- + " The original query was: "
562
- + compiler.compile(objs.query)[0]
563
- + str(compiler.compile(objs.query)[1])
564
- ) from e
565
-
566
- if not negated:
567
- queryset = self.queryset.filter(
568
- id__in=obj_ids
569
- ) # We need to filter the source queryset to get the objects that matched the search query.
570
- else:
571
- queryset = self.queryset.exclude(
572
- id__in=obj_ids
573
- ) # We exclude the objects that matched the search query from the source queryset, if the query is negated.
574
-
575
- if score_field is not None:
576
- queryset = queryset.annotate(**{score_field: rank_expression})
577
-
578
- return queryset[start:stop]
579
-
580
- def _process_lookup(self, field, lookup, value):
581
- lhs = field.get_attname(self.queryset.model) + "__" + lookup
582
- return Q(**{lhs: value})
583
-
584
- def _process_match_none(self):
585
- return Q(pk__in=[])
586
-
587
- def _connect_filters(self, filters, connector, negated):
588
- if connector == "AND":
589
- q = Q(*filters)
590
-
591
- elif connector == "OR":
592
- q = OR([Q(fil) for fil in filters])
593
-
594
- else:
595
- return
596
-
597
- if negated:
598
- q = ~q
599
-
600
- return q
601
-
602
-
603
- class SQLiteAutocompleteQueryCompiler(SQLiteSearchQueryCompiler):
604
- LAST_TERM_IS_PREFIX = True
605
- TARGET_SEARCH_FIELD_TYPE = AutocompleteField
606
- FTS_TABLE_FIELDS = ["autocomplete"]
607
-
608
- def get_config(self, backend):
609
- return backend.autocomplete_config
610
-
611
- def get_search_fields_for_model(self):
612
- return self.queryset.model.get_autocomplete_search_fields()
613
-
614
- def get_index_vectors(self):
615
- return [(F("index_entries__autocomplete"), 1.0)]
616
-
617
-
618
- class SQLiteSearchResults(BaseSearchResults):
619
- def get_queryset(self, for_count=False):
620
- if for_count:
621
- start = None
622
- stop = None
623
- else:
624
- start = self.start
625
- stop = self.stop
626
-
627
- return self.query_compiler.search(
628
- self.query_compiler.get_config(self.backend),
629
- start,
630
- stop,
631
- score_field=self._score_field,
632
- )
633
-
634
- def _do_search(self):
635
- return list(self.get_queryset())
636
-
637
- def _do_count(self):
638
- return self.get_queryset(for_count=True).count()
639
-
640
- supports_facet = True
641
-
642
- def facet(self, field_name):
643
- # Get field
644
- field = self.query_compiler._get_filterable_field(field_name)
645
- if field is None:
646
- raise FilterFieldError(
647
- 'Cannot facet search results with field "'
648
- + field_name
649
- + "\". Please add index.FilterField('"
650
- + field_name
651
- + "') to "
652
- + self.query_compiler.queryset.model.__name__
653
- + ".search_fields.",
654
- field_name=field_name,
655
- )
656
-
657
- query = self.query_compiler.search(
658
- self.query_compiler.get_config(self.backend), None, None
659
- )
660
- results = (
661
- query.values(field_name).annotate(count=Count("pk")).order_by("-count")
662
- )
663
-
664
- return OrderedDict(
665
- [(result[field_name], result["count"]) for result in results]
666
- )
667
-
668
-
669
- class SQLiteSearchBackend(BaseSearchBackend):
670
- query_compiler_class = SQLiteSearchQueryCompiler
671
- autocomplete_query_compiler_class = SQLiteAutocompleteQueryCompiler
672
-
673
- results_class = SQLiteSearchResults
674
- rebuilder_class = SQLiteSearchRebuilder
675
- atomic_rebuilder_class = SQLiteSearchAtomicRebuilder
676
-
677
- def __init__(self, params):
678
- super().__init__(params)
679
- self.index_name = params.get("INDEX", "default")
680
-
681
- # SQLite backend currently has no config options
682
- self.config = None
683
- self.autocomplete_config = None
684
-
685
- if params.get("ATOMIC_REBUILD", False):
686
- self.rebuilder_class = self.atomic_rebuilder_class
687
-
688
- def get_index_for_model(self, model):
689
- return Index(self)
690
-
691
- def get_index_for_object(self, obj):
692
- return self.get_index_for_model(obj._meta.model)
693
-
694
- def reset_index(self):
695
- for connection in [
696
- connection
697
- for connection in connections.all()
698
- if connection.vendor == "sqlite"
699
- ]:
700
- IndexEntry._default_manager.all()._raw_delete(using=connection.alias)
701
-
702
- def add_type(self, model):
703
- pass # Not needed.
704
-
705
- def refresh_index(self):
706
- pass # Not needed.
707
-
708
- def add(self, obj):
709
- self.get_index_for_object(obj).add_item(obj)
710
-
711
- def add_bulk(self, model, obj_list):
712
- if obj_list:
713
- self.get_index_for_object(obj_list[0]).add_items(model, obj_list)
714
-
715
- def delete(self, obj):
716
- self.get_index_for_object(obj).delete_item(obj)
717
-
718
-
719
- SearchBackend = SQLiteSearchBackend
1
+ from wagtailmodelsearch.backends.database.sqlite.sqlite import * # noqa: F403