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
@@ -5,6 +5,7 @@ from unittest import mock
5
5
  from django.contrib.auth import get_user_model
6
6
  from django.contrib.auth.models import Group
7
7
  from django.core import management
8
+ from django.core.cache import cache
8
9
  from django.db import models
9
10
  from django.test import TestCase, override_settings
10
11
  from django.utils import timezone
@@ -177,10 +178,19 @@ class TestSetUrlPathsCommand(TestCase):
177
178
  self.run_command()
178
179
 
179
180
 
181
+ @override_settings(
182
+ CACHES={
183
+ "default": {
184
+ "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
185
+ },
186
+ }
187
+ )
180
188
  class TestPublishScheduledPagesCommand(WagtailTestUtils, TestCase):
181
189
  fixtures = ["test.json"]
182
190
 
183
191
  def setUp(self):
192
+ cache.clear()
193
+
184
194
  # Find root page
185
195
  self.root_page = Page.objects.get(id=2)
186
196
 
@@ -215,8 +225,9 @@ class TestPublishScheduledPagesCommand(WagtailTestUtils, TestCase):
215
225
  .exclude(approved_go_live_at__isnull=True)
216
226
  .exists()
217
227
  )
218
-
219
- management.call_command("publish_scheduled_pages")
228
+ with self.assertNumQueries(49):
229
+ with self.captureOnCommitCallbacks(execute=True):
230
+ management.call_command("publish_scheduled_pages")
220
231
 
221
232
  p = Page.objects.get(slug="hello-world")
222
233
  self.assertTrue(p.live)
@@ -272,7 +283,9 @@ class TestPublishScheduledPagesCommand(WagtailTestUtils, TestCase):
272
283
  .exists()
273
284
  )
274
285
 
275
- management.call_command("publish_scheduled_pages")
286
+ with self.assertNumQueries(49):
287
+ with self.captureOnCommitCallbacks(execute=True):
288
+ management.call_command("publish_scheduled_pages")
276
289
 
277
290
  p = Page.objects.get(slug="hello-world")
278
291
  self.assertTrue(p.live)
@@ -307,7 +320,9 @@ class TestPublishScheduledPagesCommand(WagtailTestUtils, TestCase):
307
320
  page.title = "Goodbye world!"
308
321
  page.save_revision()
309
322
 
310
- management.call_command("publish_scheduled_pages")
323
+ with self.assertNumQueries(49):
324
+ with self.captureOnCommitCallbacks(execute=True):
325
+ management.call_command("publish_scheduled_pages")
311
326
 
312
327
  p = Page.objects.get(slug="hello-world")
313
328
  self.assertTrue(p.live)
@@ -334,7 +349,9 @@ class TestPublishScheduledPagesCommand(WagtailTestUtils, TestCase):
334
349
  .exists()
335
350
  )
336
351
 
337
- management.call_command("publish_scheduled_pages")
352
+ with self.assertNumQueries(47):
353
+ with self.captureOnCommitCallbacks(execute=True):
354
+ management.call_command("publish_scheduled_pages")
338
355
 
339
356
  p = Page.objects.get(slug="hello-world")
340
357
  self.assertFalse(p.live)
@@ -369,7 +386,9 @@ class TestPublishScheduledPagesCommand(WagtailTestUtils, TestCase):
369
386
  p = Page.objects.get(slug="hello-world")
370
387
  self.assertTrue(p.live)
371
388
 
372
- management.call_command("publish_scheduled_pages")
389
+ with self.assertNumQueries(29):
390
+ with self.captureOnCommitCallbacks(execute=True):
391
+ management.call_command("publish_scheduled_pages")
373
392
 
374
393
  p = Page.objects.get(slug="hello-world")
375
394
  self.assertFalse(p.live)
@@ -396,17 +415,27 @@ class TestPublishScheduledPagesCommand(WagtailTestUtils, TestCase):
396
415
  p = Page.objects.get(slug="hello-world")
397
416
  self.assertTrue(p.live)
398
417
 
399
- management.call_command("publish_scheduled_pages")
418
+ with self.assertNumQueries(6):
419
+ with self.captureOnCommitCallbacks(execute=True):
420
+ management.call_command("publish_scheduled_pages")
400
421
 
401
422
  p = Page.objects.get(slug="hello-world")
402
423
  self.assertTrue(p.live)
403
424
  self.assertFalse(p.expired)
404
425
 
405
426
 
427
+ @override_settings(
428
+ CACHES={
429
+ "default": {
430
+ "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
431
+ },
432
+ }
433
+ )
406
434
  class TestPublishScheduledCommand(WagtailTestUtils, TestCase):
407
435
  fixtures = ["test.json"]
408
436
 
409
437
  def setUp(self):
438
+ cache.clear()
410
439
  self.snippet = DraftStateModel.objects.create(text="Hello world!", live=False)
411
440
 
412
441
  def test_go_live_will_be_published(self):
@@ -435,7 +464,9 @@ class TestPublishScheduledCommand(WagtailTestUtils, TestCase):
435
464
  .exists()
436
465
  )
437
466
 
438
- management.call_command("publish_scheduled")
467
+ with self.assertNumQueries(15):
468
+ with self.captureOnCommitCallbacks(execute=True):
469
+ management.call_command("publish_scheduled")
439
470
 
440
471
  self.snippet.refresh_from_db()
441
472
  self.assertTrue(self.snippet.live)
@@ -482,7 +513,9 @@ class TestPublishScheduledCommand(WagtailTestUtils, TestCase):
482
513
  .exists()
483
514
  )
484
515
 
485
- management.call_command("publish_scheduled")
516
+ with self.assertNumQueries(15):
517
+ with self.captureOnCommitCallbacks(execute=True):
518
+ management.call_command("publish_scheduled")
486
519
 
487
520
  self.snippet.refresh_from_db()
488
521
  self.assertTrue(self.snippet.live)
@@ -510,7 +543,9 @@ class TestPublishScheduledCommand(WagtailTestUtils, TestCase):
510
543
  self.snippet.text = "Goodbye world!"
511
544
  self.snippet.save_revision()
512
545
 
513
- management.call_command("publish_scheduled")
546
+ with self.assertNumQueries(15):
547
+ with self.captureOnCommitCallbacks(execute=True):
548
+ management.call_command("publish_scheduled")
514
549
 
515
550
  self.snippet.refresh_from_db()
516
551
  self.assertTrue(self.snippet.live)
@@ -533,7 +568,9 @@ class TestPublishScheduledCommand(WagtailTestUtils, TestCase):
533
568
  .exists()
534
569
  )
535
570
 
536
- management.call_command("publish_scheduled")
571
+ with self.assertNumQueries(14):
572
+ with self.captureOnCommitCallbacks(execute=True):
573
+ management.call_command("publish_scheduled")
537
574
 
538
575
  self.assertFalse(self.snippet.live)
539
576
  self.assertTrue(
@@ -560,7 +597,9 @@ class TestPublishScheduledCommand(WagtailTestUtils, TestCase):
560
597
  self.snippet.refresh_from_db()
561
598
  self.assertTrue(self.snippet.live)
562
599
 
563
- management.call_command("publish_scheduled")
600
+ with self.assertNumQueries(10):
601
+ with self.captureOnCommitCallbacks(execute=True):
602
+ management.call_command("publish_scheduled")
564
603
 
565
604
  self.snippet.refresh_from_db()
566
605
  self.assertFalse(self.snippet.live)
@@ -580,7 +619,9 @@ class TestPublishScheduledCommand(WagtailTestUtils, TestCase):
580
619
  self.snippet.refresh_from_db()
581
620
  self.assertTrue(self.snippet.live)
582
621
 
583
- management.call_command("publish_scheduled")
622
+ with self.assertNumQueries(6):
623
+ with self.captureOnCommitCallbacks(execute=True):
624
+ management.call_command("publish_scheduled")
584
625
 
585
626
  self.snippet.refresh_from_db()
586
627
  self.assertTrue(self.snippet.live)
@@ -0,0 +1,32 @@
1
+ from django.test import TestCase
2
+
3
+ from wagtail.models import Page, Site
4
+ from wagtail.test.testapp.models import EventIndex
5
+
6
+
7
+ class AllowedHttpMethodsTestCase(TestCase):
8
+ @classmethod
9
+ def setUpTestData(cls):
10
+ super().setUpTestData()
11
+ site = Site.objects.select_related("root_page").get(is_default_site=True)
12
+ cls.page = Page(title="Page", slug="page")
13
+ site.root_page.add_child(instance=cls.page)
14
+ cls.event_index_page = EventIndex(title="Event index", slug="event-index")
15
+ site.root_page.add_child(instance=cls.event_index_page)
16
+
17
+ def test_options_request_for_default_page(self):
18
+ response = self.client.options(self.page.url)
19
+ self.assertEqual(response.status_code, 200)
20
+ self.assertEqual(
21
+ response["Allow"], "DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT"
22
+ )
23
+
24
+ def test_options_request_for_restricted_page(self):
25
+ response = self.client.options(self.event_index_page.url)
26
+ self.assertEqual(response.status_code, 200)
27
+ self.assertEqual(response["Allow"], "GET, OPTIONS")
28
+
29
+ def test_invalid_request_method_for_restricted_page(self):
30
+ response = self.client.post(self.event_index_page.url)
31
+ self.assertEqual(response.status_code, 405)
32
+ self.assertEqual(response["Allow"], "GET, OPTIONS")
@@ -10,6 +10,7 @@ from django.core.exceptions import ValidationError
10
10
  from django.http import Http404
11
11
  from django.test import Client, TestCase, override_settings
12
12
  from django.test.client import RequestFactory
13
+ from django.urls import reverse
13
14
  from django.utils import timezone, translation
14
15
  from freezegun import freeze_time
15
16
 
@@ -326,6 +327,12 @@ class TestSiteRouting(TestCase):
326
327
  Site.find_for_request(request), self.alternate_port_events_site
327
328
  )
328
329
 
330
+ def test_site_with_disallowed_host(self):
331
+ request = get_dummy_request()
332
+ request.META["HTTP_HOST"] = "disallowed:80"
333
+ with self.assertNumQueries(1):
334
+ self.assertEqual(Site.find_for_request(request), self.default_site)
335
+
329
336
 
330
337
  class TestRouting(TestCase):
331
338
  fixtures = ["test.json"]
@@ -3994,3 +4001,64 @@ class TestPageCachedParentObjExists(TestCase):
3994
4001
  "_cached_parent_obj_exists",
3995
4002
  "Page.get_parent() (treebeard) no longer uses _cached_parent_obj to cache the parent object",
3996
4003
  )
4004
+
4005
+
4006
+ class TestPageServeWithPasswordRestriction(TestCase, WagtailTestUtils):
4007
+ def setUp(self):
4008
+ self.root_page = Page.objects.get(id=2)
4009
+ self.test_page = Page(
4010
+ title="Test Page",
4011
+ slug="test",
4012
+ )
4013
+ self.root_page.add_child(instance=self.test_page)
4014
+
4015
+ self.password_restriction = PageViewRestriction.objects.create(
4016
+ page=self.test_page,
4017
+ restriction_type=PageViewRestriction.PASSWORD,
4018
+ password="password123",
4019
+ )
4020
+
4021
+ def test_page_with_password_restriction_authenticated_has_cache_headers(self):
4022
+ auth_url = reverse(
4023
+ "wagtailcore_authenticate_with_password",
4024
+ args=[self.password_restriction.id, self.test_page.id],
4025
+ )
4026
+
4027
+ post_response = self.client.post(
4028
+ auth_url,
4029
+ {
4030
+ "password": "password123",
4031
+ "return_url": "/test/",
4032
+ },
4033
+ )
4034
+
4035
+ self.assertRedirects(post_response, "/test/")
4036
+
4037
+ response = self.client.get("/test/")
4038
+
4039
+ self.assertTrue("Cache-Control" in response)
4040
+ self.assertIn("max-age=0", response["Cache-Control"])
4041
+ self.assertIn("no-cache", response["Cache-Control"])
4042
+ self.assertIn("no-store", response["Cache-Control"])
4043
+ self.assertIn("must-revalidate", response["Cache-Control"])
4044
+ self.assertIn("private", response["Cache-Control"])
4045
+ self.assertTrue("Expires" in response)
4046
+
4047
+ def test_page_with_password_restriction_has_cache_headers(self):
4048
+ response = self.client.get("/test/")
4049
+
4050
+ self.assertTrue("Cache-Control" in response)
4051
+ self.assertIn("max-age=0", response["Cache-Control"])
4052
+ self.assertIn("no-cache", response["Cache-Control"])
4053
+ self.assertIn("no-store", response["Cache-Control"])
4054
+ self.assertIn("must-revalidate", response["Cache-Control"])
4055
+ self.assertIn("private", response["Cache-Control"])
4056
+ self.assertTrue("Expires" in response)
4057
+
4058
+ def test_page_without_password_restriction_has_no_cache_headers(self):
4059
+ self.password_restriction.delete()
4060
+
4061
+ response = self.client.get("/test/")
4062
+
4063
+ self.assertFalse("Cache-Control" in response)
4064
+ self.assertFalse("Expires" in response)
@@ -231,3 +231,13 @@ class TestPagePrivacy(WagtailTestUtils, TestCase):
231
231
  response = self.client.get("/secret-login-plans/")
232
232
  self.assertEqual(response.status_code, 200)
233
233
  self.assertContains(response, "<title>Secret login plans</title>")
234
+
235
+ def test_password_protected_page_headers(self):
236
+ response = self.client.get("/secret-plans/")
237
+ self.assertEqual(
238
+ response.templates[0].name, "wagtailcore/password_required.html"
239
+ )
240
+ self.assertIn("no-cache", response["Cache-Control"])
241
+ self.assertIn("no-store", response["Cache-Control"])
242
+ self.assertIn("must-revalidate", response["Cache-Control"])
243
+ self.assertIn("max-age=0", response["Cache-Control"])
@@ -919,6 +919,85 @@ class TestSpecificQuery(WagtailTestUtils, TestCase):
919
919
  self.assertEqual(results.first().subscribers_count, 1)
920
920
  self.assertEqual(results.last().subscribers_count, 1)
921
921
 
922
+ def test_specific_subquery_select_related(self):
923
+ with self.assertNumQueries(2):
924
+ pages = list(
925
+ Page.objects.type(EventPage)
926
+ .specific()
927
+ .select_related("feed_image", for_specific_subqueries=True)
928
+ )
929
+ self.assertEqual(len(pages), 4)
930
+ with self.assertNumQueries(0):
931
+ for page in pages:
932
+ self.assertTrue(page.feed_image)
933
+
934
+ def test_specific_subquery_select_related_without_fields(
935
+ self,
936
+ ):
937
+ with self.assertRaises(ValueError):
938
+ Page.objects.all().select_related(for_specific_subqueries=True)
939
+
940
+ def test_specific_subquery_select_related_negation(self):
941
+ with self.assertNumQueries(2):
942
+ pages = list(
943
+ Page.objects.type(EventPage)
944
+ .specific()
945
+ .select_related("feed_image", for_specific_subqueries=True)
946
+ .select_related(
947
+ None, for_specific_subqueries=True
948
+ ) # This should negate the above line
949
+ )
950
+ with self.assertNumQueries(4):
951
+ for page in pages:
952
+ self.assertTrue(page.feed_image)
953
+
954
+ def test_specific_subquery_prefetch_related(self):
955
+ with self.assertNumQueries(3):
956
+ pages = list(
957
+ Page.objects.type(EventPage)
958
+ .specific()
959
+ .prefetch_related("categories", for_specific_subqueries=True)
960
+ )
961
+ self.assertEqual(len(pages), 4)
962
+ with self.assertNumQueries(0):
963
+ for page in pages:
964
+ self.assertFalse(page.categories.all())
965
+
966
+ def test_specific_subquery_prefetch_related_without_lookups(self):
967
+ with self.assertRaises(ValueError):
968
+ Page.objects.all().prefetch_related(for_specific_subqueries=True)
969
+
970
+ def test_specific_subquery_prefetch_related_negation(self):
971
+ with self.assertNumQueries(2):
972
+ pages = list(
973
+ Page.objects.type(EventPage)
974
+ .specific()
975
+ .prefetch_related("categories", for_specific_subqueries=True)
976
+ .prefetch_related(
977
+ None, for_specific_subqueries=True
978
+ ) # This should negate the above line
979
+ )
980
+ self.assertEqual(len(pages), 4)
981
+ with self.assertNumQueries(4):
982
+ for page in pages:
983
+ self.assertFalse(page.categories.all())
984
+
985
+ def test_specific_subquery_select_related_and_prefetch_related(self):
986
+ with self.assertNumQueries(3):
987
+ pages = list(
988
+ Page.objects.type(EventPage)
989
+ .specific()
990
+ .select_related("feed_image", for_specific_subqueries=True)
991
+ .prefetch_related(
992
+ "feed_image__renditions", for_specific_subqueries=True
993
+ )
994
+ )
995
+ self.assertEqual(len(pages), 4)
996
+ with self.assertNumQueries(0):
997
+ for page in pages:
998
+ self.assertTrue(page.feed_image)
999
+ self.assertFalse(page.feed_image.renditions.all())
1000
+
922
1001
  def test_specific_query_with_alias(self):
923
1002
  """
924
1003
  Ensure alias() works with specific() queries.
@@ -28,45 +28,46 @@ from wagtail.test.testapp.models import (
28
28
 
29
29
  class TestCreateOrUpdateForObject(TestCase):
30
30
  def setUp(self):
31
- image_model = get_image_model()
32
- self.image_content_type = ContentType.objects.get_for_model(image_model)
31
+ with self.captureOnCommitCallbacks(execute=True):
32
+ image_model = get_image_model()
33
+ self.image_content_type = ContentType.objects.get_for_model(image_model)
33
34
 
34
- self.test_feed_image = image_model.objects.create(
35
- title="Test feed image",
36
- file=get_test_image_file(),
37
- )
38
- self.test_image_1 = image_model.objects.create(
39
- title="Test image 1",
40
- file=get_test_image_file(),
41
- )
42
- self.test_image_2 = image_model.objects.create(
43
- title="Test image 2",
44
- file=get_test_image_file(),
45
- )
35
+ self.test_feed_image = image_model.objects.create(
36
+ title="Test feed image",
37
+ file=get_test_image_file(),
38
+ )
39
+ self.test_image_1 = image_model.objects.create(
40
+ title="Test image 1",
41
+ file=get_test_image_file(),
42
+ )
43
+ self.test_image_2 = image_model.objects.create(
44
+ title="Test image 2",
45
+ file=get_test_image_file(),
46
+ )
46
47
 
47
- # Add event page
48
- self.event_page = EventPage(
49
- title="Event page",
50
- slug="event-page",
51
- location="the moon",
52
- audience="public",
53
- cost="free",
54
- date_from="2001-01-01",
55
- feed_image=self.test_feed_image,
56
- )
57
- self.event_page.carousel_items = [
58
- EventPageCarouselItem(
59
- caption="1234567", image=self.test_image_1, sort_order=1
60
- ),
61
- EventPageCarouselItem(
62
- caption="7654321", image=self.test_image_2, sort_order=2
63
- ),
64
- EventPageCarouselItem(
65
- caption="abcdefg", image=self.test_image_1, sort_order=3
66
- ),
67
- ]
68
- self.root_page = Page.objects.get(id=2)
69
- self.root_page.add_child(instance=self.event_page)
48
+ # Add event page
49
+ self.event_page = EventPage(
50
+ title="Event page",
51
+ slug="event-page",
52
+ location="the moon",
53
+ audience="public",
54
+ cost="free",
55
+ date_from="2001-01-01",
56
+ feed_image=self.test_feed_image,
57
+ )
58
+ self.event_page.carousel_items = [
59
+ EventPageCarouselItem(
60
+ caption="1234567", image=self.test_image_1, sort_order=1
61
+ ),
62
+ EventPageCarouselItem(
63
+ caption="7654321", image=self.test_image_2, sort_order=2
64
+ ),
65
+ EventPageCarouselItem(
66
+ caption="abcdefg", image=self.test_image_1, sort_order=3
67
+ ),
68
+ ]
69
+ self.root_page = Page.objects.get(id=2)
70
+ self.root_page.add_child(instance=self.event_page)
70
71
 
71
72
  self.expected_references = {
72
73
  (
@@ -168,8 +169,9 @@ class TestCreateOrUpdateForObject(TestCase):
168
169
  )
169
170
 
170
171
  def test_saving_base_model_does_not_remove_references(self):
171
- page = Page.objects.get(pk=self.event_page.pk)
172
- page.save()
172
+ with self.captureOnCommitCallbacks(execute=True):
173
+ page = Page.objects.get(pk=self.event_page.pk)
174
+ page.save()
173
175
  self.assertSetEqual(
174
176
  set(
175
177
  ReferenceIndex.get_references_for_object(self.event_page).values_list(
@@ -180,11 +182,12 @@ class TestCreateOrUpdateForObject(TestCase):
180
182
  )
181
183
 
182
184
  def test_null_parental_key(self):
183
- obj = ModelWithNullableParentalKey(
184
- content="""<p><a linktype="page" id="%d">event page</a></p>"""
185
- % self.event_page.id
186
- )
187
- obj.save()
185
+ with self.captureOnCommitCallbacks(execute=True):
186
+ obj = ModelWithNullableParentalKey(
187
+ content="""<p><a linktype="page" id="%d">event page</a></p>"""
188
+ % self.event_page.id
189
+ )
190
+ obj.save()
188
191
 
189
192
  # Models with a ParentalKey are not considered indexable - references are recorded against the parent model
190
193
  # instead. Since the ParentalKey is null here, no reference will be recorded.
@@ -192,51 +195,56 @@ class TestCreateOrUpdateForObject(TestCase):
192
195
  self.assertEqual(refs.count(), 0)
193
196
 
194
197
  def test_lazy_parental_key(self):
195
- event_page_related_link = EventPageRelatedLink()
196
- # The parent model is a lazy object
197
- event_page_related_link.page = SimpleLazyObject(lambda: self.event_page)
198
- event_page_related_link.link_page = self.root_page
199
- event_page_related_link.save()
198
+ with self.captureOnCommitCallbacks(execute=True):
199
+ event_page_related_link = EventPageRelatedLink()
200
+ # The parent model is a lazy object
201
+ event_page_related_link.page = SimpleLazyObject(lambda: self.event_page)
202
+ event_page_related_link.link_page = self.root_page
203
+ event_page_related_link.save()
200
204
  refs = ReferenceIndex.get_references_to(self.root_page)
201
205
  self.assertEqual(refs.count(), 1)
202
206
 
203
207
  def test_generic_foreign_key(self):
204
- page1 = GenericSnippetPage(
205
- title="generic snippet page", snippet_content_object=self.event_page
206
- )
207
- self.root_page.add_child(instance=page1)
208
- page2 = GenericSnippetPage(
209
- title="generic snippet page", snippet_content_object=None
210
- )
211
- self.root_page.add_child(instance=page2)
208
+ with self.captureOnCommitCallbacks(execute=True):
209
+ page1 = GenericSnippetPage(
210
+ title="generic snippet page", snippet_content_object=self.event_page
211
+ )
212
+ self.root_page.add_child(instance=page1)
213
+ page2 = GenericSnippetPage(
214
+ title="generic snippet page", snippet_content_object=None
215
+ )
216
+ self.root_page.add_child(instance=page2)
212
217
 
213
218
  refs = ReferenceIndex.get_references_to(self.event_page)
214
219
  self.assertEqual(refs.count(), 1)
215
220
 
216
221
  def test_model_index_ignore_generic_foreign_key(self):
217
- page1 = GenericSnippetNoIndexPage(
218
- title="generic snippet page", snippet_content_object=self.event_page
219
- )
220
- self.root_page.add_child(instance=page1)
221
- page2 = GenericSnippetNoIndexPage(
222
- title="generic snippet page", snippet_content_object=None
223
- )
224
- self.root_page.add_child(instance=page2)
222
+ with self.captureOnCommitCallbacks(execute=True):
223
+ page1 = GenericSnippetNoIndexPage(
224
+ title="generic snippet page", snippet_content_object=self.event_page
225
+ )
226
+ self.root_page.add_child(instance=page1)
227
+ page2 = GenericSnippetNoIndexPage(
228
+ title="generic snippet page", snippet_content_object=None
229
+ )
230
+ self.root_page.add_child(instance=page2)
225
231
 
226
232
  # There should be no references
227
233
  refs = ReferenceIndex.get_references_to(self.event_page)
228
234
  self.assertEqual(refs.count(), 0)
229
235
 
230
236
  def test_model_field_index_ignore_generic_foreign_key(self):
231
- content_type = ContentType.objects.get_for_model(self.event_page)
232
- page1 = GenericSnippetNoFieldIndexPage(
233
- title="generic snippet page", snippet_content_type_nonindexed=content_type
234
- )
235
- self.root_page.add_child(instance=page1)
236
- page2 = GenericSnippetNoFieldIndexPage(
237
- title="generic snippet page", snippet_content_type_nonindexed=None
238
- )
239
- self.root_page.add_child(instance=page2)
237
+ with self.captureOnCommitCallbacks(execute=True):
238
+ content_type = ContentType.objects.get_for_model(self.event_page)
239
+ page1 = GenericSnippetNoFieldIndexPage(
240
+ title="generic snippet page",
241
+ snippet_content_type_nonindexed=content_type,
242
+ )
243
+ self.root_page.add_child(instance=page1)
244
+ page2 = GenericSnippetNoFieldIndexPage(
245
+ title="generic snippet page", snippet_content_type_nonindexed=None
246
+ )
247
+ self.root_page.add_child(instance=page2)
240
248
 
241
249
  # There should be no references
242
250
  refs = ReferenceIndex.get_references_to(content_type)
@@ -449,7 +457,8 @@ class TestDescribeOnDelete(TestCase):
449
457
  if "on_delete_set_default" not in init_kwargs:
450
458
  init_kwargs["on_delete_set_default"] = None
451
459
 
452
- obj = VariousOnDeleteModel.objects.create(**init_kwargs)
460
+ with self.captureOnCommitCallbacks(execute=True):
461
+ obj = VariousOnDeleteModel.objects.create(**init_kwargs)
453
462
  usage = ReferenceIndex.get_references_to(
454
463
  referred_object
455
464
  ).group_by_source_object()
@@ -237,6 +237,24 @@ class TestStreamValueAccess(TestCase):
237
237
  self.assertEqual(fetched_body[1].block_type, "text")
238
238
  self.assertEqual(fetched_body[1].value, "bar")
239
239
 
240
+ def test_can_append_on_queried_instance(self):
241
+ # The test is analog to test_can_append(), but instead of working with the
242
+ # in-memory version from JSONStreamModel.objects.create(), we query a fresh
243
+ # instance from the db.
244
+ # It tests adding data to child blocks that
245
+ # have not yet been lazy loaded. This would previously crash.
246
+ self.json_body = JSONStreamModel.objects.get(pk=self.json_body.pk)
247
+ self.json_body.body.append(("text", "bar"))
248
+ self.json_body.save()
249
+
250
+ fetched_body = JSONStreamModel.objects.get(id=self.json_body.id).body
251
+ self.assertIsInstance(fetched_body, StreamValue)
252
+ self.assertEqual(len(fetched_body), 2)
253
+ self.assertEqual(fetched_body[0].block_type, "text")
254
+ self.assertEqual(fetched_body[0].value, "foo")
255
+ self.assertEqual(fetched_body[1].block_type, "text")
256
+ self.assertEqual(fetched_body[1].value, "bar")
257
+
240
258
  def test_complex_assignment(self):
241
259
  page = StreamPage(title="Test page", body=[])
242
260
  page.body = [
@@ -279,6 +297,18 @@ class TestComplexDefault(TestCase):
279
297
  self.assertEqual(self.page.body[1].value[1].block_type, "author")
280
298
  self.assertEqual(self.page.body[1].value[1].value, "F. Scott Fitzgerald")
281
299
 
300
+ def test_creation_form_with_initial_instance(self):
301
+ form_class = ComplexDefaultStreamPage.get_edit_handler().get_form_class()
302
+ form = form_class(instance=self.page)
303
+ form_html = form.as_p()
304
+ self.assertIn("The Great Gatsby", form_html)
305
+
306
+ def test_creation_form_without_initial_instance(self):
307
+ form_class = ComplexDefaultStreamPage.get_edit_handler().get_form_class()
308
+ form = form_class()
309
+ form_html = form.as_p()
310
+ self.assertIn("The Great Gatsby", form_html)
311
+
282
312
 
283
313
  class TestStreamFieldRenderingBase(TestCase):
284
314
  model = JSONStreamModel