wagtail 6.3.2__py3-none-any.whl → 6.4rc1__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 (296) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/publish_revision.py +4 -5
  3. wagtail/admin/auth.py +0 -2
  4. wagtail/admin/checks.py +1 -1
  5. wagtail/admin/filters.py +3 -1
  6. wagtail/admin/forms/account.py +21 -11
  7. wagtail/admin/forms/collections.py +2 -9
  8. wagtail/admin/forms/formsets.py +32 -0
  9. wagtail/admin/forms/pages.py +5 -1
  10. wagtail/admin/forms/workflows.py +2 -13
  11. wagtail/admin/locale/ar/LC_MESSAGES/django.mo +0 -0
  12. wagtail/admin/locale/ar/LC_MESSAGES/django.po +68 -1
  13. wagtail/admin/locale/ar/LC_MESSAGES/djangojs.mo +0 -0
  14. wagtail/admin/locale/ar/LC_MESSAGES/djangojs.po +5 -1
  15. wagtail/admin/locale/en/LC_MESSAGES/django.po +312 -356
  16. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +21 -16
  17. wagtail/admin/menu.py +0 -13
  18. wagtail/admin/panels/base.py +2 -2
  19. wagtail/admin/panels/group.py +4 -1
  20. wagtail/admin/panels/inline_panel.py +5 -2
  21. wagtail/admin/panels/model_utils.py +36 -0
  22. wagtail/admin/panels/page_utils.py +2 -40
  23. wagtail/admin/panels/signal_handlers.py +0 -2
  24. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  25. wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
  26. wagtail/admin/static/wagtailadmin/css/panels/streamfield.css +1 -1
  27. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  28. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  29. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -8
  30. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  31. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  32. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  33. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  34. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  35. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  36. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  37. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +1 -1
  38. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  39. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +7 -0
  40. wagtail/admin/templates/wagtailadmin/404.html +4 -0
  41. wagtail/admin/templates/wagtailadmin/chooser/browse.html +2 -1
  42. wagtail/admin/templates/wagtailadmin/chooser/tables/parent_page_cell.html +1 -1
  43. wagtail/admin/templates/wagtailadmin/collections/_privacy_switch.html +8 -1
  44. wagtail/admin/templates/wagtailadmin/generic/confirm_delete.html +15 -9
  45. wagtail/admin/templates/wagtailadmin/generic/confirm_unpublish.html +21 -25
  46. wagtail/admin/templates/wagtailadmin/generic/form.html +1 -1
  47. wagtail/admin/templates/wagtailadmin/generic/preview_error.html +3 -0
  48. wagtail/admin/templates/wagtailadmin/generic/revisions/compare.html +63 -76
  49. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -2
  50. wagtail/admin/templates/wagtailadmin/pages/edit.html +1 -5
  51. wagtail/admin/templates/wagtailadmin/panels/inline_panel_child.html +1 -0
  52. wagtail/admin/templates/wagtailadmin/permissions/includes/collection_member_permissions_form.html +1 -1
  53. wagtail/admin/templates/wagtailadmin/permissions/includes/collection_member_permissions_formset.html +6 -22
  54. wagtail/admin/templates/wagtailadmin/shared/formatted_field.html +2 -2
  55. wagtail/admin/templates/wagtailadmin/shared/header.html +2 -2
  56. wagtail/admin/templates/wagtailadmin/shared/page_status_tag_new.html +32 -39
  57. wagtail/admin/templates/wagtailadmin/shared/revisions/confirm_unschedule.html +13 -17
  58. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/privacy.html +15 -3
  59. wagtail/admin/templates/wagtailadmin/shared/side_panels/preview.html +1 -1
  60. wagtail/admin/templates/wagtailadmin/skeleton.html +4 -2
  61. wagtail/admin/templates/wagtailadmin/workflows/create.html +1 -1
  62. wagtail/admin/templates/wagtailadmin/workflows/edit.html +1 -1
  63. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_pages_form.html +1 -1
  64. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_pages_formset.html +6 -23
  65. wagtail/admin/templatetags/wagtailadmin_tags.py +12 -0
  66. wagtail/admin/templatetags/wagtailuserbar.py +2 -3
  67. wagtail/admin/tests/pages/test_create_page.py +110 -1
  68. wagtail/admin/tests/pages/test_edit_page.py +3 -2
  69. wagtail/admin/tests/pages/test_explorer_view.py +18 -0
  70. wagtail/admin/tests/pages/test_page_usage.py +24 -20
  71. wagtail/admin/tests/pages/test_preview.py +69 -1
  72. wagtail/admin/tests/pages/test_revisions.py +40 -6
  73. wagtail/admin/tests/test_account_management.py +39 -1
  74. wagtail/admin/tests/test_audit_log.py +4 -2
  75. wagtail/admin/tests/test_block_preview.py +224 -0
  76. wagtail/admin/tests/test_edit_handlers.py +23 -6
  77. wagtail/admin/tests/test_page_chooser.py +50 -3
  78. wagtail/admin/tests/test_privacy.py +49 -26
  79. wagtail/admin/tests/test_site_summary.py +15 -10
  80. wagtail/admin/tests/test_templatetags.py +19 -0
  81. wagtail/admin/tests/test_userbar.py +82 -1
  82. wagtail/admin/tests/test_views_generic.py +27 -12
  83. wagtail/admin/tests/test_workflows.py +69 -0
  84. wagtail/admin/tests/tests.py +23 -4
  85. wagtail/admin/tests/ui/test_sidebar.py +1 -1
  86. wagtail/admin/tests/viewsets/test_model_viewset.py +15 -13
  87. wagtail/admin/ui/side_panels.py +7 -4
  88. wagtail/admin/urls/__init__.py +6 -0
  89. wagtail/admin/urls/pages.py +1 -1
  90. wagtail/admin/userbar.py +21 -1
  91. wagtail/admin/views/account.py +5 -0
  92. wagtail/admin/views/chooser.py +5 -1
  93. wagtail/admin/views/collections.py +0 -2
  94. wagtail/admin/views/generic/base.py +20 -10
  95. wagtail/admin/views/generic/history.py +0 -1
  96. wagtail/admin/views/generic/models.py +79 -21
  97. wagtail/admin/views/generic/preview.py +50 -1
  98. wagtail/admin/views/mixins.py +4 -2
  99. wagtail/admin/views/pages/bulk_actions/delete.py +11 -23
  100. wagtail/admin/views/pages/bulk_actions/page_bulk_action.py +17 -0
  101. wagtail/admin/views/pages/bulk_actions/publish.py +11 -31
  102. wagtail/admin/views/pages/bulk_actions/unpublish.py +11 -31
  103. wagtail/admin/views/pages/create.py +1 -0
  104. wagtail/admin/views/pages/edit.py +38 -30
  105. wagtail/admin/views/pages/revisions.py +43 -114
  106. wagtail/admin/views/pages/utils.py +0 -1
  107. wagtail/admin/views/tags.py +6 -2
  108. wagtail/admin/views/workflows.py +8 -6
  109. wagtail/admin/viewsets/model.py +0 -4
  110. wagtail/admin/viewsets/pages.py +0 -1
  111. wagtail/admin/widgets/tags.py +1 -0
  112. wagtail/api/v2/tests/test_documents.py +4 -2
  113. wagtail/api/v2/tests/test_images.py +4 -2
  114. wagtail/api/v2/tests/test_pages.py +8 -4
  115. wagtail/blocks/base.py +59 -1
  116. wagtail/blocks/field_block.py +6 -0
  117. wagtail/blocks/list_block.py +4 -0
  118. wagtail/blocks/static_block.py +3 -0
  119. wagtail/blocks/stream_block.py +5 -1
  120. wagtail/blocks/struct_block.py +6 -0
  121. wagtail/compat.py +16 -0
  122. wagtail/contrib/forms/forms.py +27 -7
  123. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +2 -2
  124. wagtail/contrib/forms/tests/test_models.py +7 -5
  125. wagtail/contrib/forms/tests/test_views.py +75 -0
  126. wagtail/contrib/frontend_cache/tasks.py +83 -0
  127. wagtail/contrib/frontend_cache/tests.py +47 -32
  128. wagtail/contrib/frontend_cache/utils.py +2 -70
  129. wagtail/contrib/redirects/base_formats.py +2 -2
  130. wagtail/contrib/redirects/locale/ar/LC_MESSAGES/django.mo +0 -0
  131. wagtail/contrib/redirects/locale/ar/LC_MESSAGES/django.po +3 -0
  132. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +24 -37
  133. wagtail/contrib/redirects/templates/wagtailredirects/add.html +1 -24
  134. wagtail/contrib/redirects/templates/wagtailredirects/confirm_delete.html +3 -13
  135. wagtail/contrib/redirects/tests/test_redirects.py +122 -110
  136. wagtail/contrib/redirects/tests/test_signal_handlers.py +75 -69
  137. wagtail/contrib/redirects/urls.py +2 -2
  138. wagtail/contrib/redirects/views.py +35 -73
  139. wagtail/contrib/search_promotions/admin_urls.py +10 -3
  140. wagtail/contrib/search_promotions/forms.py +55 -26
  141. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +44 -54
  142. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/add.html +21 -31
  143. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/confirm_delete.html +3 -12
  144. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/edit.html +11 -34
  145. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/includes/searchpromotion_form.html +1 -0
  146. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/includes/searchpromotions_formset.js +2 -1
  147. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index.html +0 -1
  148. wagtail/contrib/search_promotions/tests.py +814 -13
  149. wagtail/contrib/search_promotions/views/__init__.py +1 -0
  150. wagtail/contrib/search_promotions/views/reports.py +56 -0
  151. wagtail/contrib/search_promotions/views/settings.py +258 -0
  152. wagtail/contrib/search_promotions/wagtail_hooks.py +12 -1
  153. wagtail/contrib/settings/locale/ar/LC_MESSAGES/django.mo +0 -0
  154. wagtail/contrib/settings/locale/ar/LC_MESSAGES/django.po +6 -1
  155. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +3 -3
  156. wagtail/contrib/settings/templates/wagtailsettings/edit.html +1 -5
  157. wagtail/contrib/settings/tests/generic/test_admin.py +2 -5
  158. wagtail/contrib/settings/tests/generic/test_register.py +1 -1
  159. wagtail/contrib/settings/tests/site_specific/test_admin.py +2 -5
  160. wagtail/contrib/settings/tests/site_specific/test_register.py +1 -1
  161. wagtail/contrib/settings/views.py +9 -23
  162. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  163. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +1 -1
  164. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  165. wagtail/contrib/table_block/tests.py +4 -1
  166. wagtail/contrib/typed_table_block/blocks.py +3 -0
  167. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
  168. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  169. wagtail/contrib/typed_table_block/tests.py +33 -0
  170. wagtail/documents/locale/en/LC_MESSAGES/django.po +26 -26
  171. wagtail/documents/migrations/0011_add_choose_permissions.py +1 -0
  172. wagtail/documents/models.py +1 -0
  173. wagtail/documents/signal_handlers.py +6 -2
  174. wagtail/documents/static/wagtaildocs/js/add-multiple.js +1 -1
  175. wagtail/documents/templates/wagtaildocs/documents/edit.html +1 -3
  176. wagtail/documents/templates/wagtaildocs/multiple/add.html +7 -1
  177. wagtail/documents/tests/test_admin_views.py +74 -33
  178. wagtail/documents/tests/test_views.py +21 -12
  179. wagtail/documents/views/chooser.py +1 -0
  180. wagtail/documents/views/documents.py +1 -2
  181. wagtail/documents/views/multiple.py +0 -1
  182. wagtail/documents/views/serve.py +9 -2
  183. wagtail/documents/wagtail_hooks.py +6 -1
  184. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  185. wagtail/embeds/oembed_providers.py +0 -64
  186. wagtail/fields.py +3 -0
  187. wagtail/images/apps.py +2 -1
  188. wagtail/images/blocks.py +6 -2
  189. wagtail/images/forms.py +40 -3
  190. wagtail/images/locale/ar/LC_MESSAGES/django.mo +0 -0
  191. wagtail/images/locale/ar/LC_MESSAGES/django.po +4 -0
  192. wagtail/images/locale/en/LC_MESSAGES/django.po +49 -49
  193. wagtail/images/migrations/0023_add_choose_permissions.py +1 -0
  194. wagtail/images/rich_text/contentstate.py +1 -0
  195. wagtail/images/rich_text/editor_html.py +1 -0
  196. wagtail/images/signal_handlers.py +17 -10
  197. wagtail/images/static/wagtailimages/js/add-multiple.js +1 -1
  198. wagtail/images/static/wagtailimages/js/image-block.js +1 -1
  199. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  200. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  201. wagtail/images/static/wagtailimages/js/image-url-generator.js +1 -1
  202. wagtail/images/static/wagtailimages/js/vendor/jquery.fileupload-image.js +1 -1
  203. wagtail/images/tasks.py +18 -0
  204. wagtail/images/templates/wagtailimages/images/edit.html +1 -3
  205. wagtail/images/templates/wagtailimages/images/url_generator.html +1 -1
  206. wagtail/images/templates/wagtailimages/multiple/add.html +7 -2
  207. wagtail/images/templates/wagtailimages/widgets/image_chooser.html +1 -1
  208. wagtail/images/tests/test_admin_views.py +53 -29
  209. wagtail/images/tests/test_blocks.py +3 -2
  210. wagtail/images/tests/test_models.py +12 -10
  211. wagtail/images/tests/tests.py +10 -0
  212. wagtail/images/views/chooser.py +1 -0
  213. wagtail/images/views/images.py +1 -3
  214. wagtail/images/views/multiple.py +0 -1
  215. wagtail/images/views/serve.py +18 -2
  216. wagtail/images/widgets.py +3 -0
  217. wagtail/locale/en/LC_MESSAGES/django.po +228 -216
  218. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  219. wagtail/management/commands/publish_scheduled.py +1 -1
  220. wagtail/migrations/0087_alter_grouppagepermission_unique_together_and_more.py +16 -8
  221. wagtail/models/__init__.py +300 -119
  222. wagtail/models/i18n.py +2 -2
  223. wagtail/models/panels.py +37 -0
  224. wagtail/models/sites.py +7 -6
  225. wagtail/permission_policies/pages.py +2 -2
  226. wagtail/project_template/project_name/settings/base.py +4 -0
  227. wagtail/project_template/requirements.txt +1 -1
  228. wagtail/query.py +145 -0
  229. wagtail/search/backends/database/mysql/mysql.py +25 -17
  230. wagtail/search/backends/database/postgres/postgres.py +44 -83
  231. wagtail/search/backends/database/sqlite/sqlite.py +25 -17
  232. wagtail/search/backends/elasticsearch7.py +4 -0
  233. wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
  234. wagtail/search/query.py +8 -2
  235. wagtail/search/signal_handlers.py +6 -9
  236. wagtail/search/tasks.py +10 -0
  237. wagtail/search/tests/test_elasticsearch7_backend.py +21 -0
  238. wagtail/search/tests/test_index_functions.py +10 -6
  239. wagtail/search/tests/test_postgres_backend.py +0 -14
  240. wagtail/signal_handlers.py +5 -20
  241. wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
  242. wagtail/snippets/locale/en/LC_MESSAGES/django.po +3 -13
  243. wagtail/snippets/tests/test_preview.py +5 -0
  244. wagtail/snippets/tests/test_snippets.py +100 -45
  245. wagtail/snippets/tests/test_usage.py +29 -24
  246. wagtail/snippets/tests/test_viewset.py +1 -1
  247. wagtail/snippets/views/snippets.py +0 -12
  248. wagtail/tasks.py +41 -0
  249. wagtail/templates/wagtailcore/shared/block_preview.html +29 -0
  250. wagtail/test/earlypage/__init__.py +0 -0
  251. wagtail/test/earlypage/migrations/0001_initial.py +37 -0
  252. wagtail/test/earlypage/migrations/__init__.py +0 -0
  253. wagtail/test/earlypage/models.py +14 -0
  254. wagtail/test/settings.py +3 -0
  255. wagtail/test/testapp/fixtures/test.json +7 -0
  256. wagtail/test/testapp/fixtures/test_specific.json +6 -3
  257. wagtail/test/testapp/models.py +58 -44
  258. wagtail/test/testapp/templates/tests/custom_block_preview.html +16 -0
  259. wagtail/test/testapp/templates/tests/static_block_preview.html +5 -0
  260. wagtail/test/testapp/wagtail_hooks.py +9 -0
  261. wagtail/tests/test_blocks.py +189 -2
  262. wagtail/tests/test_hooks.py +166 -1
  263. wagtail/tests/test_management_commands.py +54 -13
  264. wagtail/tests/test_page_allowed_http_methods.py +32 -0
  265. wagtail/tests/test_page_model.py +68 -0
  266. wagtail/tests/test_page_privacy.py +10 -0
  267. wagtail/tests/test_page_queryset.py +79 -0
  268. wagtail/tests/test_reference_index.py +84 -75
  269. wagtail/tests/test_streamfield.py +30 -0
  270. wagtail/tests/test_utils.py +61 -0
  271. wagtail/users/forms.py +2 -9
  272. wagtail/users/locale/en/LC_MESSAGES/django.po +17 -17
  273. wagtail/users/templates/wagtailusers/groups/create.html +0 -5
  274. wagtail/users/templates/wagtailusers/groups/includes/page_permissions_form.html +1 -1
  275. wagtail/users/templates/wagtailusers/groups/includes/page_permissions_formset.html +6 -6
  276. wagtail/users/tests/test_admin_views.py +96 -4
  277. wagtail/users/tests/test_utils.py +76 -0
  278. wagtail/users/utils.py +43 -11
  279. wagtail/utils/setup.py +2 -2
  280. wagtail/utils/templates.py +26 -0
  281. wagtail/utils/widgets.py +1 -0
  282. wagtail/views.py +9 -1
  283. wagtail/wagtail_hooks.py +67 -29
  284. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/METADATA +2 -2
  285. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/RECORD +289 -276
  286. wagtail/admin/static/wagtailadmin/js/expanding-formset.js +0 -1
  287. wagtail/admin/static/wagtailadmin/js/vendor/rangy-core.js +0 -1
  288. wagtail/admin/static/wagtailadmin/js/vendor/uuidv4.min.js +0 -1
  289. wagtail/contrib/search_promotions/views.py +0 -323
  290. wagtail/images/static/wagtailimages/js/vendor/canvas-to-blob.min.js +0 -1
  291. wagtail/users/static/wagtailusers/js/group-form.js +0 -1
  292. wagtail/users/templates/wagtailusers/groups/includes/group_form_js.html +0 -3
  293. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/LICENSE +0 -0
  294. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/WHEEL +0 -0
  295. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/entry_points.txt +0 -0
  296. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,12 @@
1
1
  from collections import OrderedDict
2
2
  from functools import reduce
3
3
 
4
- from django.db import DEFAULT_DB_ALIAS, NotSupportedError, connections, transaction
4
+ from django.db import (
5
+ NotSupportedError,
6
+ connections,
7
+ router,
8
+ transaction,
9
+ )
5
10
  from django.db.models import Avg, Count, F, Manager, Q, TextField
6
11
  from django.db.models.constants import LOOKUP_SEP
7
12
  from django.db.models.functions import Cast, Length
@@ -145,17 +150,22 @@ class ObjectIndexer:
145
150
 
146
151
 
147
152
  class Index:
148
- def __init__(self, backend, db_alias=None):
153
+ def __init__(self, backend):
149
154
  self.backend = backend
150
155
  self.name = self.backend.index_name
151
- self.db_alias = DEFAULT_DB_ALIAS if db_alias is None else db_alias
152
- self.connection = connections[self.db_alias]
153
- if self.connection.vendor != "sqlite":
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
+ ):
154
164
  raise NotSupportedError(
155
- "You must select a SQLite database " "to use the SQLite search backend."
165
+ "You must select a SQLite database to use the SQLite search backend."
156
166
  )
157
167
 
158
- self.entries = IndexEntry._default_manager.using(self.db_alias)
168
+ self.entries = IndexEntry._default_manager.all()
159
169
 
160
170
  def add_model(self, model):
161
171
  pass
@@ -195,11 +205,9 @@ class Index:
195
205
  ).update(title_norm=lavg / F("title_length"))
196
206
 
197
207
  def delete_stale_model_entries(self, model):
198
- existing_pks = (
199
- model._default_manager.using(self.db_alias)
200
- .annotate(object_id=Cast("pk", TextField()))
201
- .values("object_id")
202
- )
208
+ existing_pks = model._default_manager.annotate(
209
+ object_id=Cast("pk", TextField())
210
+ ).values("object_id")
203
211
  content_types_pks = get_descendants_content_types_pks(model)
204
212
  stale_entries = self.entries.filter(
205
213
  content_type_id__in=content_types_pks
@@ -270,7 +278,7 @@ class Index:
270
278
  update_method(content_type_pk, indexers)
271
279
 
272
280
  def delete_item(self, item):
273
- item.index_entries.all()._raw_delete(using=self.db_alias)
281
+ item.index_entries.all()._raw_delete(using=self.write_connection.alias)
274
282
 
275
283
  def __str__(self):
276
284
  return self.name
@@ -291,7 +299,7 @@ class SQLiteSearchRebuilder:
291
299
  class SQLiteSearchAtomicRebuilder(SQLiteSearchRebuilder):
292
300
  def __init__(self, index):
293
301
  super().__init__(index)
294
- self.transaction = transaction.atomic(using=index.db_alias)
302
+ self.transaction = transaction.atomic(using=index.write_connection.alias)
295
303
  self.transaction_opened = False
296
304
 
297
305
  def start(self):
@@ -673,11 +681,11 @@ class SQLiteSearchBackend(BaseSearchBackend):
673
681
  if params.get("ATOMIC_REBUILD", False):
674
682
  self.rebuilder_class = self.atomic_rebuilder_class
675
683
 
676
- def get_index_for_model(self, model, db_alias=None):
677
- return Index(self, db_alias)
684
+ def get_index_for_model(self, model):
685
+ return Index(self)
678
686
 
679
687
  def get_index_for_object(self, obj):
680
- return self.get_index_for_model(obj._meta.model, obj._state.db)
688
+ return self.get_index_for_model(obj._meta.model)
681
689
 
682
690
  def reset_index(self):
683
691
  for connection in [
@@ -605,6 +605,10 @@ class Elasticsearch7SearchQueryCompiler(BaseSearchQueryCompiler):
605
605
  "query": query.query_string,
606
606
  "fuzziness": "AUTO",
607
607
  }
608
+
609
+ if query.operator != "or":
610
+ match_query["operator"] = query.operator
611
+
608
612
  if len(fields) == 1:
609
613
  if fields[0].boost != 1.0:
610
614
  match_query["boost"] = fields[0].boost
@@ -8,7 +8,7 @@ msgid ""
8
8
  msgstr ""
9
9
  "Project-Id-Version: PACKAGE VERSION\n"
10
10
  "Report-Msgid-Bugs-To: \n"
11
- "POT-Creation-Date: 2024-10-21 17:53+0100\n"
11
+ "POT-Creation-Date: 2025-01-20 17:59+0000\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
wagtail/search/query.py CHANGED
@@ -50,11 +50,17 @@ class Phrase(SearchQuery):
50
50
 
51
51
 
52
52
  class Fuzzy(SearchQuery):
53
- def __init__(self, query_string: str):
53
+ OPERATORS = ["and", "or"]
54
+ DEFAULT_OPERATOR = "or"
55
+
56
+ def __init__(self, query_string: str, operator: str = DEFAULT_OPERATOR):
54
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'.")
55
61
 
56
62
  def __repr__(self):
57
- return f"<Fuzzy {repr(self.query_string)}>"
63
+ return f"<Fuzzy {repr(self.query_string)} operator={repr(self.operator)}>"
58
64
 
59
65
 
60
66
  class MatchAll(SearchQuery):
@@ -1,16 +1,13 @@
1
1
  from django.db.models.signals import post_delete, post_save
2
2
 
3
- from wagtail.search import index
3
+ from . import index
4
+ from .tasks import insert_or_update_object_task
4
5
 
5
6
 
6
- def post_save_signal_handler(instance, update_fields=None, **kwargs):
7
- if update_fields is not None:
8
- # fetch a fresh copy of instance from the database to ensure
9
- # that we're not indexing any of the unsaved data contained in
10
- # the fields that were not passed in update_fields
11
- instance = type(instance).objects.get(pk=instance.pk)
12
-
13
- index.insert_or_update_object(instance)
7
+ def post_save_signal_handler(instance, **kwargs):
8
+ insert_or_update_object_task.enqueue(
9
+ instance._meta.app_label, instance._meta.model_name, instance.pk
10
+ )
14
11
 
15
12
 
16
13
  def post_delete_signal_handler(instance, **kwargs):
@@ -0,0 +1,10 @@
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))
@@ -840,6 +840,27 @@ class TestElasticsearch7SearchQuery(TestCase):
840
840
  }
841
841
  self.assertDictEqual(query_compiler.get_inner_query(), expected_result)
842
842
 
843
+ def test_fuzzy_query_with_operator(self):
844
+ # Create a query
845
+ query_compiler = self.query_compiler_class(
846
+ models.Book.objects.all(),
847
+ Fuzzy("Hello world", operator="and"),
848
+ )
849
+
850
+ # Check it
851
+ expected_result = {
852
+ "multi_match": {
853
+ "fields": [
854
+ "_all_text",
855
+ "_all_text_boost_2_0^2.0",
856
+ ],
857
+ "query": "Hello world",
858
+ "fuzziness": "AUTO",
859
+ "operator": "and",
860
+ }
861
+ }
862
+ self.assertDictEqual(query_compiler.get_inner_query(), expected_result)
863
+
843
864
  def test_year_filter(self):
844
865
  # Create a query
845
866
  query_compiler = self.query_compiler_class(
@@ -160,9 +160,10 @@ class TestRemoveObject(WagtailTestUtils, TestCase):
160
160
  class TestSignalHandlers(WagtailTestUtils, TestCase):
161
161
  def test_index_on_create(self, backend):
162
162
  backend().reset_mock()
163
- obj = models.Book.objects.create(
164
- title="Test", publication_date=date(2017, 10, 18), number_of_pages=100
165
- )
163
+ with self.captureOnCommitCallbacks(execute=True):
164
+ obj = models.Book.objects.create(
165
+ title="Test", publication_date=date(2017, 10, 18), number_of_pages=100
166
+ )
166
167
  backend().add.assert_called_with(obj)
167
168
 
168
169
  def test_index_on_update(self, backend):
@@ -172,7 +173,8 @@ class TestSignalHandlers(WagtailTestUtils, TestCase):
172
173
 
173
174
  backend().reset_mock()
174
175
  obj.title = "Updated test"
175
- obj.save()
176
+ with self.captureOnCommitCallbacks(execute=True):
177
+ obj.save()
176
178
 
177
179
  self.assertEqual(backend().add.call_count, 1)
178
180
  indexed_object = backend().add.call_args[0][0]
@@ -184,7 +186,8 @@ class TestSignalHandlers(WagtailTestUtils, TestCase):
184
186
  )
185
187
 
186
188
  backend().reset_mock()
187
- obj.delete()
189
+ with self.captureOnCommitCallbacks(execute=True):
190
+ obj.delete()
188
191
  backend().delete.assert_called_with(obj)
189
192
 
190
193
  def test_do_not_index_fields_omitted_from_update_fields(self, backend):
@@ -195,7 +198,8 @@ class TestSignalHandlers(WagtailTestUtils, TestCase):
195
198
  backend().reset_mock()
196
199
  obj.title = "Updated test"
197
200
  obj.publication_date = date(2001, 10, 19)
198
- obj.save(update_fields=["title"])
201
+ with self.captureOnCommitCallbacks(execute=True):
202
+ obj.save(update_fields=["title"])
199
203
 
200
204
  self.assertEqual(backend().add.call_count, 1)
201
205
  indexed_object = backend().add.call_args[0][0]
@@ -160,20 +160,6 @@ class TestPostgresSearchBackend(BackendTests, TestCase):
160
160
  results = self.backend.autocomplete("first <-> second", models.Book)
161
161
  self.assertUnsortedListEqual([r.title for r in results], [])
162
162
 
163
- def test_index_without_upsert(self):
164
- # Test the add_items code path for Postgres 9.4, where upsert is not available
165
- self.backend.reset_index()
166
-
167
- index = self.backend.get_index_for_model(models.Book)
168
- index._enable_upsert = False
169
- index.add_items(models.Book, models.Book.objects.all())
170
-
171
- results = self.backend.search("JavaScript", models.Book)
172
- self.assertUnsortedListEqual(
173
- [r.title for r in results],
174
- ["JavaScript: The good parts", "JavaScript: The Definitive Guide"],
175
- )
176
-
177
163
 
178
164
  @unittest.skipUnless(
179
165
  connection.vendor == "postgresql", "The current database is not PostgreSQL"
@@ -11,10 +11,11 @@ from django.db.models.signals import (
11
11
  pre_delete,
12
12
  pre_migrate,
13
13
  )
14
- from modelcluster.fields import ParentalKey
15
14
 
16
15
  from wagtail.models import Locale, Page, ReferenceIndex, Site
17
16
 
17
+ from .tasks import update_reference_index_task
18
+
18
19
  logger = logging.getLogger("wagtail")
19
20
 
20
21
 
@@ -70,25 +71,9 @@ def update_reference_index_on_save(instance, **kwargs):
70
71
  if getattr(reference_index_auto_update_disabled, "value", False):
71
72
  return
72
73
 
73
- # If the model is a child model, find the parent instance and index that instead
74
- while True:
75
- parental_keys = list(
76
- filter(
77
- lambda field: isinstance(field, ParentalKey),
78
- instance._meta.get_fields(),
79
- )
80
- )
81
- if not parental_keys:
82
- break
83
-
84
- instance = getattr(instance, parental_keys[0].name)
85
- if instance is None:
86
- # parent is null, so there is no valid object to record references against
87
- return
88
-
89
- if ReferenceIndex.is_indexed(instance._meta.model):
90
- with transaction.atomic():
91
- ReferenceIndex.create_or_update_for_object(instance)
74
+ update_reference_index_task.enqueue(
75
+ instance._meta.app_label, instance._meta.model_name, instance.pk
76
+ )
92
77
 
93
78
 
94
79
  def remove_reference_index_on_delete(instance, **kwargs):
@@ -8,7 +8,7 @@ msgid ""
8
8
  msgstr ""
9
9
  "Project-Id-Version: PACKAGE VERSION\n"
10
10
  "Report-Msgid-Bugs-To: \n"
11
- "POT-Creation-Date: 2024-10-21 17:53+0100\n"
11
+ "POT-Creation-Date: 2025-01-20 17:59+0000\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -8,7 +8,7 @@ msgid ""
8
8
  msgstr ""
9
9
  "Project-Id-Version: PACKAGE VERSION\n"
10
10
  "Report-Msgid-Bugs-To: \n"
11
- "POT-Creation-Date: 2024-10-21 17:53+0100\n"
11
+ "POT-Creation-Date: 2025-01-20 17:59+0000\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -177,7 +177,7 @@ msgstr ""
177
177
  msgid "Choose"
178
178
  msgstr ""
179
179
 
180
- #: views/snippets.py:76 views/snippets.py:882 wagtail_hooks.py:42
180
+ #: views/snippets.py:76 views/snippets.py:870 wagtail_hooks.py:42
181
181
  msgid "Snippets"
182
182
  msgstr ""
183
183
 
@@ -194,17 +194,7 @@ msgstr ""
194
194
  msgid "More options for '%(title)s'"
195
195
  msgstr ""
196
196
 
197
- #: views/snippets.py:363
198
- #, python-format
199
- msgid "Edit this %(model_name)s"
200
- msgstr ""
201
-
202
- #: views/snippets.py:369
203
- #, python-format
204
- msgid "%(model_name)s history"
205
- msgstr ""
206
-
207
- #: views/snippets.py:881
197
+ #: views/snippets.py:869
208
198
  msgid "Home"
209
199
  msgstr ""
210
200
 
@@ -5,6 +5,7 @@ from django.urls import reverse
5
5
  from django.utils import timezone
6
6
  from freezegun import freeze_time
7
7
 
8
+ from wagtail.admin.staticfiles import versioned_static
8
9
  from wagtail.admin.views.generic.preview import PreviewOnEdit
9
10
  from wagtail.test.testapp.models import (
10
11
  EventCategory,
@@ -59,6 +60,7 @@ class TestPreview(WagtailTestUtils, TestCase):
59
60
  '<h1 class="preview-error__title">Preview not available</h1>',
60
61
  html=True,
61
62
  )
63
+ self.assertNotContains(response, versioned_static("wagtailadmin/js/icons.js"))
62
64
 
63
65
  def test_preview_on_create_with_invalid_data(self):
64
66
  self.assertNotIn(self.session_key_prefix, self.client.session)
@@ -90,6 +92,7 @@ class TestPreview(WagtailTestUtils, TestCase):
90
92
  '<h1 class="preview-error__title">Preview not available</h1>',
91
93
  html=True,
92
94
  )
95
+ self.assertNotContains(response, versioned_static("wagtailadmin/js/icons.js"))
93
96
 
94
97
  def test_preview_on_create_with_m2m_field(self):
95
98
  response = self.client.post(self.preview_on_add_url, self.post_data)
@@ -229,6 +232,7 @@ class TestPreview(WagtailTestUtils, TestCase):
229
232
  '<h1 class="preview-error__title">Preview not available</h1>',
230
233
  html=True,
231
234
  )
235
+ self.assertNotContains(response, versioned_static("wagtailadmin/js/icons.js"))
232
236
 
233
237
  def test_preview_on_edit_clear_preview_data(self):
234
238
  # Set a fake preview session data for the page
@@ -259,6 +263,7 @@ class TestPreview(WagtailTestUtils, TestCase):
259
263
  '<h1 class="preview-error__title">Preview not available</h1>',
260
264
  html=True,
261
265
  )
266
+ self.assertNotContains(response, versioned_static("wagtailadmin/js/icons.js"))
262
267
 
263
268
  def test_preview_revision(self):
264
269
  snippet = MultiPreviewModesModel.objects.create(text="Multiple modes")
@@ -4129,9 +4129,10 @@ class TestSnippetDelete(WagtailTestUtils, TestCase):
4129
4129
  self.assertContains(response, delete_url)
4130
4130
 
4131
4131
  def test_delete_get_with_protected_reference(self):
4132
- VariousOnDeleteModel.objects.create(
4133
- text="Undeletable", on_delete_protect=self.test_snippet
4134
- )
4132
+ with self.captureOnCommitCallbacks(execute=True):
4133
+ VariousOnDeleteModel.objects.create(
4134
+ text="Undeletable", on_delete_protect=self.test_snippet
4135
+ )
4135
4136
  delete_url = reverse(
4136
4137
  "wagtailsnippets_tests_advert:delete",
4137
4138
  args=[quote(self.test_snippet.pk)],
@@ -4186,9 +4187,10 @@ class TestSnippetDelete(WagtailTestUtils, TestCase):
4186
4187
  self.assertEqual(Advert.objects.filter(text="test_advert").count(), 0)
4187
4188
 
4188
4189
  def test_delete_post_with_protected_reference(self):
4189
- VariousOnDeleteModel.objects.create(
4190
- text="Undeletable", on_delete_protect=self.test_snippet
4191
- )
4190
+ with self.captureOnCommitCallbacks(execute=True):
4191
+ VariousOnDeleteModel.objects.create(
4192
+ text="Undeletable", on_delete_protect=self.test_snippet
4193
+ )
4192
4194
  delete_url = reverse(
4193
4195
  "wagtailsnippets_tests_advert:delete",
4194
4196
  args=[quote(self.test_snippet.pk)],
@@ -4870,7 +4872,7 @@ class TestSnippetRevisions(WagtailTestUtils, TestCase):
4870
4872
  self.assertEqual(self.snippet.live_revision, self.snippet.latest_revision)
4871
4873
 
4872
4874
 
4873
- class TestCompareRevisions(WagtailTestUtils, TestCase):
4875
+ class TestCompareRevisions(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
4874
4876
  # Actual tests for the comparison classes can be found in test_compare.py
4875
4877
 
4876
4878
  def setUp(self):
@@ -4908,6 +4910,32 @@ class TestCompareRevisions(WagtailTestUtils, TestCase):
4908
4910
  html=True,
4909
4911
  )
4910
4912
 
4913
+ index_url = reverse("wagtailsnippets_tests_revisablemodel:list", args=[])
4914
+ edit_url = reverse(
4915
+ "wagtailsnippets_tests_revisablemodel:edit",
4916
+ args=(self.snippet.id,),
4917
+ )
4918
+ history_url = reverse(
4919
+ "wagtailsnippets_tests_revisablemodel:history",
4920
+ args=(self.snippet.id,),
4921
+ )
4922
+
4923
+ self.assertBreadcrumbsItemsRendered(
4924
+ [
4925
+ {"url": reverse("wagtailsnippets:index"), "label": "Snippets"},
4926
+ {"url": index_url, "label": "Revisable models"},
4927
+ {"url": edit_url, "label": str(self.snippet)},
4928
+ {"url": history_url, "label": "History"},
4929
+ {"url": "", "label": "Compare", "sublabel": str(self.snippet)},
4930
+ ],
4931
+ response.content,
4932
+ )
4933
+
4934
+ soup = self.get_soup(response.content)
4935
+ edit_button = soup.select_one(f"a.w-header-button[href='{edit_url}']")
4936
+ self.assertIsNotNone(edit_button)
4937
+ self.assertEqual(edit_button.text.strip(), "Edit")
4938
+
4911
4939
  def test_compare_revisions_earliest(self):
4912
4940
  response = self.get("earliest", self.edit_revision.pk)
4913
4941
  self.assertEqual(response.status_code, 200)
@@ -5496,7 +5524,11 @@ class TestSnippetChooserBlock(TestCase):
5496
5524
  self.assertEqual(block.to_python(test_advert.id), test_advert)
5497
5525
 
5498
5526
  def test_adapt(self):
5499
- block = SnippetChooserBlock(Advert, help_text="pick an advert, any advert")
5527
+ block = SnippetChooserBlock(
5528
+ Advert,
5529
+ help_text="pick an advert, any advert",
5530
+ description="An advert to be displayed on the sidebar.",
5531
+ )
5500
5532
 
5501
5533
  block.set_name("test_snippetchooserblock")
5502
5534
  js_args = FieldBlockAdapter().js_args(block)
@@ -5508,8 +5540,11 @@ class TestSnippetChooserBlock(TestCase):
5508
5540
  js_args[2],
5509
5541
  {
5510
5542
  "label": "Test snippetchooserblock",
5543
+ "description": "An advert to be displayed on the sidebar.",
5511
5544
  "required": True,
5512
5545
  "icon": "snippet",
5546
+ "blockDefId": block.definition_prefix,
5547
+ "isPreviewable": block.is_previewable,
5513
5548
  "helpText": "pick an advert, any advert",
5514
5549
  "classname": "w-field w-field--model_choice_field w-field--admin_snippet_chooser",
5515
5550
  "showAddCommentButton": True,
@@ -5622,6 +5657,9 @@ class TestSnippetViewWithCustomPrimaryKey(WagtailTestUtils, TestCase):
5622
5657
  self.snippet_a = StandardSnippetWithCustomPrimaryKey.objects.create(
5623
5658
  snippet_id="snippet/01", text="Hello"
5624
5659
  )
5660
+ self.snippet_b = StandardSnippetWithCustomPrimaryKey.objects.create(
5661
+ snippet_id="abc_407269_1", text="Goodbye"
5662
+ )
5625
5663
 
5626
5664
  def get(self, snippet, params={}):
5627
5665
  args = [quote(snippet.pk)]
@@ -5644,9 +5682,11 @@ class TestSnippetViewWithCustomPrimaryKey(WagtailTestUtils, TestCase):
5644
5682
  )
5645
5683
 
5646
5684
  def test_show_edit_view(self):
5647
- response = self.get(self.snippet_a)
5648
- self.assertEqual(response.status_code, 200)
5649
- self.assertTemplateUsed(response, "wagtailsnippets/snippets/edit.html")
5685
+ for snippet in [self.snippet_a, self.snippet_b]:
5686
+ with self.subTest(snippet=snippet):
5687
+ response = self.get(snippet)
5688
+ self.assertEqual(response.status_code, 200)
5689
+ self.assertTemplateUsed(response, "wagtailsnippets/snippets/edit.html")
5650
5690
 
5651
5691
  def test_edit_invalid(self):
5652
5692
  response = self.post(self.snippet_a, post_data={"foo": "bar"})
@@ -5669,8 +5709,10 @@ class TestSnippetViewWithCustomPrimaryKey(WagtailTestUtils, TestCase):
5669
5709
  )
5670
5710
 
5671
5711
  snippets = StandardSnippetWithCustomPrimaryKey.objects.all()
5672
- self.assertEqual(snippets.count(), 2)
5673
- self.assertEqual(snippets.last().snippet_id, "snippet_id_edited")
5712
+ self.assertEqual(snippets.count(), 3)
5713
+ self.assertEqual(
5714
+ snippets.order_by("snippet_id").last().snippet_id, "snippet_id_edited"
5715
+ )
5674
5716
 
5675
5717
  def test_create(self):
5676
5718
  response = self.create(
@@ -5685,40 +5727,48 @@ class TestSnippetViewWithCustomPrimaryKey(WagtailTestUtils, TestCase):
5685
5727
  )
5686
5728
 
5687
5729
  snippets = StandardSnippetWithCustomPrimaryKey.objects.all()
5688
- self.assertEqual(snippets.count(), 2)
5689
- self.assertEqual(snippets.last().text, "test snippet")
5730
+ self.assertEqual(snippets.count(), 3)
5731
+ self.assertEqual(snippets.order_by("snippet_id").last().text, "test snippet")
5690
5732
 
5691
5733
  def test_get_delete(self):
5692
- response = self.client.get(
5693
- reverse(
5694
- "wagtailsnippets_snippetstests_standardsnippetwithcustomprimarykey:delete",
5695
- args=[quote(self.snippet_a.pk)],
5696
- )
5697
- )
5698
- self.assertEqual(response.status_code, 200)
5699
- self.assertTemplateUsed(response, "wagtailadmin/generic/confirm_delete.html")
5734
+ for snippet in [self.snippet_a, self.snippet_b]:
5735
+ with self.subTest(snippet=snippet):
5736
+ response = self.client.get(
5737
+ reverse(
5738
+ "wagtailsnippets_snippetstests_standardsnippetwithcustomprimarykey:delete",
5739
+ args=[quote(snippet.pk)],
5740
+ )
5741
+ )
5742
+ self.assertEqual(response.status_code, 200)
5743
+ self.assertTemplateUsed(
5744
+ response, "wagtailadmin/generic/confirm_delete.html"
5745
+ )
5700
5746
 
5701
5747
  def test_usage_link(self):
5702
- response = self.client.get(
5703
- reverse(
5704
- "wagtailsnippets_snippetstests_standardsnippetwithcustomprimarykey:delete",
5705
- args=[quote(self.snippet_a.pk)],
5706
- )
5707
- )
5708
- self.assertEqual(response.status_code, 200)
5709
- self.assertTemplateUsed(response, "wagtailadmin/generic/confirm_delete.html")
5710
- self.assertContains(
5711
- response,
5712
- "This standard snippet with custom primary key is referenced 0 times",
5713
- )
5714
- self.assertContains(
5715
- response,
5716
- reverse(
5717
- "wagtailsnippets_snippetstests_standardsnippetwithcustomprimarykey:usage",
5718
- args=[quote(self.snippet_a.pk)],
5719
- )
5720
- + "?describe_on_delete=1",
5721
- )
5748
+ for snippet in [self.snippet_a, self.snippet_b]:
5749
+ with self.subTest(snippet=snippet):
5750
+ response = self.client.get(
5751
+ reverse(
5752
+ "wagtailsnippets_snippetstests_standardsnippetwithcustomprimarykey:delete",
5753
+ args=[quote(snippet.pk)],
5754
+ )
5755
+ )
5756
+ self.assertEqual(response.status_code, 200)
5757
+ self.assertTemplateUsed(
5758
+ response, "wagtailadmin/generic/confirm_delete.html"
5759
+ )
5760
+ self.assertContains(
5761
+ response,
5762
+ "This standard snippet with custom primary key is referenced 0 times",
5763
+ )
5764
+ self.assertContains(
5765
+ response,
5766
+ reverse(
5767
+ "wagtailsnippets_snippetstests_standardsnippetwithcustomprimarykey:usage",
5768
+ args=[quote(snippet.pk)],
5769
+ )
5770
+ + "?describe_on_delete=1",
5771
+ )
5722
5772
 
5723
5773
  def test_redirect_to_edit(self):
5724
5774
  with self.assertWarnsRegex(
@@ -5788,7 +5838,9 @@ class TestSnippetChooserBlockWithCustomPrimaryKey(TestCase):
5788
5838
 
5789
5839
  def test_adapt(self):
5790
5840
  block = SnippetChooserBlock(
5791
- AdvertWithCustomPrimaryKey, help_text="pick an advert, any advert"
5841
+ AdvertWithCustomPrimaryKey,
5842
+ help_text="pick an advert, any advert",
5843
+ description="An advert to be displayed on the footer.",
5792
5844
  )
5793
5845
 
5794
5846
  block.set_name("test_snippetchooserblock")
@@ -5801,8 +5853,11 @@ class TestSnippetChooserBlockWithCustomPrimaryKey(TestCase):
5801
5853
  js_args[2],
5802
5854
  {
5803
5855
  "label": "Test snippetchooserblock",
5856
+ "description": "An advert to be displayed on the footer.",
5804
5857
  "required": True,
5805
5858
  "icon": "snippet",
5859
+ "blockDefId": block.definition_prefix,
5860
+ "isPreviewable": block.is_previewable,
5806
5861
  "helpText": "pick an advert, any advert",
5807
5862
  "classname": "w-field w-field--model_choice_field w-field--admin_snippet_chooser",
5808
5863
  "showAddCommentButton": True,
@@ -6000,7 +6055,7 @@ class TestPanelConfigurationChecks(WagtailTestUtils, TestCase):
6000
6055
 
6001
6056
  warning = checks.Warning(
6002
6057
  "StandardSnippet.content_panels will have no effect on snippets editing",
6003
- hint="""Ensure that StandardSnippet uses `panels` instead of `content_panels`\
6058
+ hint="""Ensure that StandardSnippet uses `panels` instead of `content_panels` \
6004
6059
  or set up an `edit_handler` if you want a tabbed editing interface.
6005
6060
  There are no default tabs on non-Page models so there will be no\
6006
6061
  Content tab for the content_panels to render in.""",