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.
- wagtail/__init__.py +1 -1
- wagtail/actions/publish_revision.py +4 -5
- wagtail/admin/auth.py +0 -2
- wagtail/admin/checks.py +1 -1
- wagtail/admin/filters.py +3 -1
- wagtail/admin/forms/account.py +21 -11
- wagtail/admin/forms/collections.py +2 -9
- wagtail/admin/forms/formsets.py +32 -0
- wagtail/admin/forms/pages.py +5 -1
- wagtail/admin/forms/workflows.py +2 -13
- wagtail/admin/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ar/LC_MESSAGES/django.po +68 -1
- wagtail/admin/locale/ar/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/ar/LC_MESSAGES/djangojs.po +5 -1
- wagtail/admin/locale/en/LC_MESSAGES/django.po +312 -356
- wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +21 -16
- wagtail/admin/menu.py +0 -13
- wagtail/admin/panels/base.py +2 -2
- wagtail/admin/panels/group.py +4 -1
- wagtail/admin/panels/inline_panel.py +5 -2
- wagtail/admin/panels/model_utils.py +36 -0
- wagtail/admin/panels/page_utils.py +2 -40
- wagtail/admin/panels/signal_handlers.py +0 -2
- wagtail/admin/static/wagtailadmin/css/core.css +1 -1
- wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
- wagtail/admin/static/wagtailadmin/css/panels/streamfield.css +1 -1
- wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
- wagtail/admin/static/wagtailadmin/js/core.js +1 -1
- wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -8
- wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
- wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
- wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
- wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
- wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
- wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
- wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
- wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +1 -1
- wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
- wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +7 -0
- wagtail/admin/templates/wagtailadmin/404.html +4 -0
- wagtail/admin/templates/wagtailadmin/chooser/browse.html +2 -1
- wagtail/admin/templates/wagtailadmin/chooser/tables/parent_page_cell.html +1 -1
- wagtail/admin/templates/wagtailadmin/collections/_privacy_switch.html +8 -1
- wagtail/admin/templates/wagtailadmin/generic/confirm_delete.html +15 -9
- wagtail/admin/templates/wagtailadmin/generic/confirm_unpublish.html +21 -25
- wagtail/admin/templates/wagtailadmin/generic/form.html +1 -1
- wagtail/admin/templates/wagtailadmin/generic/preview_error.html +3 -0
- wagtail/admin/templates/wagtailadmin/generic/revisions/compare.html +63 -76
- wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -2
- wagtail/admin/templates/wagtailadmin/pages/edit.html +1 -5
- wagtail/admin/templates/wagtailadmin/panels/inline_panel_child.html +1 -0
- wagtail/admin/templates/wagtailadmin/permissions/includes/collection_member_permissions_form.html +1 -1
- wagtail/admin/templates/wagtailadmin/permissions/includes/collection_member_permissions_formset.html +6 -22
- wagtail/admin/templates/wagtailadmin/shared/formatted_field.html +2 -2
- wagtail/admin/templates/wagtailadmin/shared/header.html +2 -2
- wagtail/admin/templates/wagtailadmin/shared/page_status_tag_new.html +32 -39
- wagtail/admin/templates/wagtailadmin/shared/revisions/confirm_unschedule.html +13 -17
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/privacy.html +15 -3
- wagtail/admin/templates/wagtailadmin/shared/side_panels/preview.html +1 -1
- wagtail/admin/templates/wagtailadmin/skeleton.html +4 -2
- wagtail/admin/templates/wagtailadmin/workflows/create.html +1 -1
- wagtail/admin/templates/wagtailadmin/workflows/edit.html +1 -1
- wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_pages_form.html +1 -1
- wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_pages_formset.html +6 -23
- wagtail/admin/templatetags/wagtailadmin_tags.py +12 -0
- wagtail/admin/templatetags/wagtailuserbar.py +2 -3
- wagtail/admin/tests/pages/test_create_page.py +110 -1
- wagtail/admin/tests/pages/test_edit_page.py +3 -2
- wagtail/admin/tests/pages/test_explorer_view.py +18 -0
- wagtail/admin/tests/pages/test_page_usage.py +24 -20
- wagtail/admin/tests/pages/test_preview.py +69 -1
- wagtail/admin/tests/pages/test_revisions.py +40 -6
- wagtail/admin/tests/test_account_management.py +39 -1
- wagtail/admin/tests/test_audit_log.py +4 -2
- wagtail/admin/tests/test_block_preview.py +224 -0
- wagtail/admin/tests/test_edit_handlers.py +23 -6
- wagtail/admin/tests/test_page_chooser.py +50 -3
- wagtail/admin/tests/test_privacy.py +49 -26
- wagtail/admin/tests/test_site_summary.py +15 -10
- wagtail/admin/tests/test_templatetags.py +19 -0
- wagtail/admin/tests/test_userbar.py +82 -1
- wagtail/admin/tests/test_views_generic.py +27 -12
- wagtail/admin/tests/test_workflows.py +69 -0
- wagtail/admin/tests/tests.py +23 -4
- wagtail/admin/tests/ui/test_sidebar.py +1 -1
- wagtail/admin/tests/viewsets/test_model_viewset.py +15 -13
- wagtail/admin/ui/side_panels.py +7 -4
- wagtail/admin/urls/__init__.py +6 -0
- wagtail/admin/urls/pages.py +1 -1
- wagtail/admin/userbar.py +21 -1
- wagtail/admin/views/account.py +5 -0
- wagtail/admin/views/chooser.py +5 -1
- wagtail/admin/views/collections.py +0 -2
- wagtail/admin/views/generic/base.py +20 -10
- wagtail/admin/views/generic/history.py +0 -1
- wagtail/admin/views/generic/models.py +79 -21
- wagtail/admin/views/generic/preview.py +50 -1
- wagtail/admin/views/mixins.py +4 -2
- wagtail/admin/views/pages/bulk_actions/delete.py +11 -23
- wagtail/admin/views/pages/bulk_actions/page_bulk_action.py +17 -0
- wagtail/admin/views/pages/bulk_actions/publish.py +11 -31
- wagtail/admin/views/pages/bulk_actions/unpublish.py +11 -31
- wagtail/admin/views/pages/create.py +1 -0
- wagtail/admin/views/pages/edit.py +38 -30
- wagtail/admin/views/pages/revisions.py +43 -114
- wagtail/admin/views/pages/utils.py +0 -1
- wagtail/admin/views/tags.py +6 -2
- wagtail/admin/views/workflows.py +8 -6
- wagtail/admin/viewsets/model.py +0 -4
- wagtail/admin/viewsets/pages.py +0 -1
- wagtail/admin/widgets/tags.py +1 -0
- wagtail/api/v2/tests/test_documents.py +4 -2
- wagtail/api/v2/tests/test_images.py +4 -2
- wagtail/api/v2/tests/test_pages.py +8 -4
- wagtail/blocks/base.py +59 -1
- wagtail/blocks/field_block.py +6 -0
- wagtail/blocks/list_block.py +4 -0
- wagtail/blocks/static_block.py +3 -0
- wagtail/blocks/stream_block.py +5 -1
- wagtail/blocks/struct_block.py +6 -0
- wagtail/compat.py +16 -0
- wagtail/contrib/forms/forms.py +27 -7
- wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/forms/tests/test_models.py +7 -5
- wagtail/contrib/forms/tests/test_views.py +75 -0
- wagtail/contrib/frontend_cache/tasks.py +83 -0
- wagtail/contrib/frontend_cache/tests.py +47 -32
- wagtail/contrib/frontend_cache/utils.py +2 -70
- wagtail/contrib/redirects/base_formats.py +2 -2
- wagtail/contrib/redirects/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/ar/LC_MESSAGES/django.po +3 -0
- wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +24 -37
- wagtail/contrib/redirects/templates/wagtailredirects/add.html +1 -24
- wagtail/contrib/redirects/templates/wagtailredirects/confirm_delete.html +3 -13
- wagtail/contrib/redirects/tests/test_redirects.py +122 -110
- wagtail/contrib/redirects/tests/test_signal_handlers.py +75 -69
- wagtail/contrib/redirects/urls.py +2 -2
- wagtail/contrib/redirects/views.py +35 -73
- wagtail/contrib/search_promotions/admin_urls.py +10 -3
- wagtail/contrib/search_promotions/forms.py +55 -26
- wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +44 -54
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/add.html +21 -31
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/confirm_delete.html +3 -12
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/edit.html +11 -34
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/includes/searchpromotion_form.html +1 -0
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/includes/searchpromotions_formset.js +2 -1
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index.html +0 -1
- wagtail/contrib/search_promotions/tests.py +814 -13
- wagtail/contrib/search_promotions/views/__init__.py +1 -0
- wagtail/contrib/search_promotions/views/reports.py +56 -0
- wagtail/contrib/search_promotions/views/settings.py +258 -0
- wagtail/contrib/search_promotions/wagtail_hooks.py +12 -1
- wagtail/contrib/settings/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/settings/locale/ar/LC_MESSAGES/django.po +6 -1
- wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +3 -3
- wagtail/contrib/settings/templates/wagtailsettings/edit.html +1 -5
- wagtail/contrib/settings/tests/generic/test_admin.py +2 -5
- wagtail/contrib/settings/tests/generic/test_register.py +1 -1
- wagtail/contrib/settings/tests/site_specific/test_admin.py +2 -5
- wagtail/contrib/settings/tests/site_specific/test_register.py +1 -1
- wagtail/contrib/settings/views.py +9 -23
- wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/table_block/tests.py +4 -1
- wagtail/contrib/typed_table_block/blocks.py +3 -0
- wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
- wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
- wagtail/contrib/typed_table_block/tests.py +33 -0
- wagtail/documents/locale/en/LC_MESSAGES/django.po +26 -26
- wagtail/documents/migrations/0011_add_choose_permissions.py +1 -0
- wagtail/documents/models.py +1 -0
- wagtail/documents/signal_handlers.py +6 -2
- wagtail/documents/static/wagtaildocs/js/add-multiple.js +1 -1
- wagtail/documents/templates/wagtaildocs/documents/edit.html +1 -3
- wagtail/documents/templates/wagtaildocs/multiple/add.html +7 -1
- wagtail/documents/tests/test_admin_views.py +74 -33
- wagtail/documents/tests/test_views.py +21 -12
- wagtail/documents/views/chooser.py +1 -0
- wagtail/documents/views/documents.py +1 -2
- wagtail/documents/views/multiple.py +0 -1
- wagtail/documents/views/serve.py +9 -2
- wagtail/documents/wagtail_hooks.py +6 -1
- wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/embeds/oembed_providers.py +0 -64
- wagtail/fields.py +3 -0
- wagtail/images/apps.py +2 -1
- wagtail/images/blocks.py +6 -2
- wagtail/images/forms.py +40 -3
- wagtail/images/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/ar/LC_MESSAGES/django.po +4 -0
- wagtail/images/locale/en/LC_MESSAGES/django.po +49 -49
- wagtail/images/migrations/0023_add_choose_permissions.py +1 -0
- wagtail/images/rich_text/contentstate.py +1 -0
- wagtail/images/rich_text/editor_html.py +1 -0
- wagtail/images/signal_handlers.py +17 -10
- wagtail/images/static/wagtailimages/js/add-multiple.js +1 -1
- wagtail/images/static/wagtailimages/js/image-block.js +1 -1
- wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
- wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
- wagtail/images/static/wagtailimages/js/image-url-generator.js +1 -1
- wagtail/images/static/wagtailimages/js/vendor/jquery.fileupload-image.js +1 -1
- wagtail/images/tasks.py +18 -0
- wagtail/images/templates/wagtailimages/images/edit.html +1 -3
- wagtail/images/templates/wagtailimages/images/url_generator.html +1 -1
- wagtail/images/templates/wagtailimages/multiple/add.html +7 -2
- wagtail/images/templates/wagtailimages/widgets/image_chooser.html +1 -1
- wagtail/images/tests/test_admin_views.py +53 -29
- wagtail/images/tests/test_blocks.py +3 -2
- wagtail/images/tests/test_models.py +12 -10
- wagtail/images/tests/tests.py +10 -0
- wagtail/images/views/chooser.py +1 -0
- wagtail/images/views/images.py +1 -3
- wagtail/images/views/multiple.py +0 -1
- wagtail/images/views/serve.py +18 -2
- wagtail/images/widgets.py +3 -0
- wagtail/locale/en/LC_MESSAGES/django.po +228 -216
- wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/management/commands/publish_scheduled.py +1 -1
- wagtail/migrations/0087_alter_grouppagepermission_unique_together_and_more.py +16 -8
- wagtail/models/__init__.py +300 -119
- wagtail/models/i18n.py +2 -2
- wagtail/models/panels.py +37 -0
- wagtail/models/sites.py +7 -6
- wagtail/permission_policies/pages.py +2 -2
- wagtail/project_template/project_name/settings/base.py +4 -0
- wagtail/project_template/requirements.txt +1 -1
- wagtail/query.py +145 -0
- wagtail/search/backends/database/mysql/mysql.py +25 -17
- wagtail/search/backends/database/postgres/postgres.py +44 -83
- wagtail/search/backends/database/sqlite/sqlite.py +25 -17
- wagtail/search/backends/elasticsearch7.py +4 -0
- wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/search/query.py +8 -2
- wagtail/search/signal_handlers.py +6 -9
- wagtail/search/tasks.py +10 -0
- wagtail/search/tests/test_elasticsearch7_backend.py +21 -0
- wagtail/search/tests/test_index_functions.py +10 -6
- wagtail/search/tests/test_postgres_backend.py +0 -14
- wagtail/signal_handlers.py +5 -20
- wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/snippets/locale/en/LC_MESSAGES/django.po +3 -13
- wagtail/snippets/tests/test_preview.py +5 -0
- wagtail/snippets/tests/test_snippets.py +100 -45
- wagtail/snippets/tests/test_usage.py +29 -24
- wagtail/snippets/tests/test_viewset.py +1 -1
- wagtail/snippets/views/snippets.py +0 -12
- wagtail/tasks.py +41 -0
- wagtail/templates/wagtailcore/shared/block_preview.html +29 -0
- wagtail/test/earlypage/__init__.py +0 -0
- wagtail/test/earlypage/migrations/0001_initial.py +37 -0
- wagtail/test/earlypage/migrations/__init__.py +0 -0
- wagtail/test/earlypage/models.py +14 -0
- wagtail/test/settings.py +3 -0
- wagtail/test/testapp/fixtures/test.json +7 -0
- wagtail/test/testapp/fixtures/test_specific.json +6 -3
- wagtail/test/testapp/models.py +58 -44
- wagtail/test/testapp/templates/tests/custom_block_preview.html +16 -0
- wagtail/test/testapp/templates/tests/static_block_preview.html +5 -0
- wagtail/test/testapp/wagtail_hooks.py +9 -0
- wagtail/tests/test_blocks.py +189 -2
- wagtail/tests/test_hooks.py +166 -1
- wagtail/tests/test_management_commands.py +54 -13
- wagtail/tests/test_page_allowed_http_methods.py +32 -0
- wagtail/tests/test_page_model.py +68 -0
- wagtail/tests/test_page_privacy.py +10 -0
- wagtail/tests/test_page_queryset.py +79 -0
- wagtail/tests/test_reference_index.py +84 -75
- wagtail/tests/test_streamfield.py +30 -0
- wagtail/tests/test_utils.py +61 -0
- wagtail/users/forms.py +2 -9
- wagtail/users/locale/en/LC_MESSAGES/django.po +17 -17
- wagtail/users/templates/wagtailusers/groups/create.html +0 -5
- wagtail/users/templates/wagtailusers/groups/includes/page_permissions_form.html +1 -1
- wagtail/users/templates/wagtailusers/groups/includes/page_permissions_formset.html +6 -6
- wagtail/users/tests/test_admin_views.py +96 -4
- wagtail/users/tests/test_utils.py +76 -0
- wagtail/users/utils.py +43 -11
- wagtail/utils/setup.py +2 -2
- wagtail/utils/templates.py +26 -0
- wagtail/utils/widgets.py +1 -0
- wagtail/views.py +9 -1
- wagtail/wagtail_hooks.py +67 -29
- {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/METADATA +2 -2
- {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/RECORD +289 -276
- wagtail/admin/static/wagtailadmin/js/expanding-formset.js +0 -1
- wagtail/admin/static/wagtailadmin/js/vendor/rangy-core.js +0 -1
- wagtail/admin/static/wagtailadmin/js/vendor/uuidv4.min.js +0 -1
- wagtail/contrib/search_promotions/views.py +0 -323
- wagtail/images/static/wagtailimages/js/vendor/canvas-to-blob.min.js +0 -1
- wagtail/users/static/wagtailusers/js/group-form.js +0 -1
- wagtail/users/templates/wagtailusers/groups/includes/group_form_js.html +0 -3
- {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/LICENSE +0 -0
- {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/WHEEL +0 -0
- {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/entry_points.txt +0 -0
- {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/top_level.txt +0 -0
wagtail/models/i18n.py
CHANGED
|
@@ -56,7 +56,7 @@ class Locale(models.Model):
|
|
|
56
56
|
@classmethod
|
|
57
57
|
def get_default(cls):
|
|
58
58
|
"""
|
|
59
|
-
Returns the default Locale based on the site's LANGUAGE_CODE setting
|
|
59
|
+
Returns the default Locale based on the site's ``LANGUAGE_CODE`` setting.
|
|
60
60
|
"""
|
|
61
61
|
return cls.objects.get_for_language(settings.LANGUAGE_CODE)
|
|
62
62
|
|
|
@@ -309,7 +309,7 @@ class TranslatableMixin(models.Model):
|
|
|
309
309
|
"""
|
|
310
310
|
Finds the translation in the specified locale.
|
|
311
311
|
|
|
312
|
-
If there is no translation in that locale, this returns None
|
|
312
|
+
If there is no translation in that locale, this returns ``None``.
|
|
313
313
|
"""
|
|
314
314
|
try:
|
|
315
315
|
return self.get_translation(locale)
|
wagtail/models/panels.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Placeholder for panel types defined in wagtail.admin.panels.
|
|
2
|
+
# These are needed because we wish to define properties such as `content_panels` on core models
|
|
3
|
+
# such as Page, but importing from wagtail.admin would create a circular import. We therefore use a
|
|
4
|
+
# placeholder object, and swap it out for the real panel class inside
|
|
5
|
+
# `wagtail.admin.panels.model_utils.expand_panel_list` at the same time as converting strings to
|
|
6
|
+
# FieldPanel instances.
|
|
7
|
+
|
|
8
|
+
from django.conf import settings
|
|
9
|
+
from django.utils.functional import cached_property
|
|
10
|
+
from django.utils.module_loading import import_string
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PanelPlaceholder:
|
|
14
|
+
def __init__(self, path, args, kwargs):
|
|
15
|
+
self.path = path
|
|
16
|
+
self.args = args
|
|
17
|
+
self.kwargs = kwargs
|
|
18
|
+
|
|
19
|
+
@cached_property
|
|
20
|
+
def panel_class(self):
|
|
21
|
+
return import_string(self.path)
|
|
22
|
+
|
|
23
|
+
def construct(self):
|
|
24
|
+
return self.panel_class(*self.args, **self.kwargs)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CommentPanelPlaceholder(PanelPlaceholder):
|
|
28
|
+
def __init__(self):
|
|
29
|
+
super().__init__(
|
|
30
|
+
"wagtail.admin.panels.CommentPanel",
|
|
31
|
+
[],
|
|
32
|
+
{},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def construct(self):
|
|
36
|
+
if getattr(settings, "WAGTAILADMIN_COMMENTS_ENABLED", True):
|
|
37
|
+
return super().construct()
|
wagtail/models/sites.py
CHANGED
|
@@ -160,7 +160,8 @@ class Site(models.Model):
|
|
|
160
160
|
|
|
161
161
|
@staticmethod
|
|
162
162
|
def _find_for_request(request):
|
|
163
|
-
|
|
163
|
+
# Use `_get_raw_host` to avoid ALLOWED_HOSTS checks
|
|
164
|
+
hostname = split_domain_port(request._get_raw_host())[0]
|
|
164
165
|
port = request.get_port()
|
|
165
166
|
site = None
|
|
166
167
|
try:
|
|
@@ -206,15 +207,15 @@ class Site(models.Model):
|
|
|
206
207
|
def get_site_root_paths():
|
|
207
208
|
"""
|
|
208
209
|
Return a list of `SiteRootPath` instances, most specific path
|
|
209
|
-
first - used to translate url_paths into actual URLs with hostnames
|
|
210
|
+
first - used to translate url_paths into actual URLs with hostnames.
|
|
210
211
|
|
|
211
212
|
Each root path is an instance of the `SiteRootPath` named tuple,
|
|
212
213
|
and have the following attributes:
|
|
213
214
|
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
217
|
-
-
|
|
215
|
+
- ``site_id`` - The ID of the Site record
|
|
216
|
+
- ``root_path`` - The internal URL path of the site's home page (for example '/home/')
|
|
217
|
+
- ``root_url`` - The scheme/domain name of the site (for example 'https://www.example.com/')
|
|
218
|
+
- ``language_code`` - The language code of the site (for example 'en')
|
|
218
219
|
"""
|
|
219
220
|
result = cache.get(
|
|
220
221
|
SITE_ROOT_PATHS_CACHE_KEY, version=SITE_ROOT_PATHS_CACHE_VERSION
|
|
@@ -166,7 +166,7 @@ class PagePermissionPolicy(OwnershipPermissionPolicy):
|
|
|
166
166
|
return Page.objects.filter(depth=1)
|
|
167
167
|
else:
|
|
168
168
|
codenames = self._get_permission_codenames(
|
|
169
|
-
{"add", "change", "publish", "lock"}
|
|
169
|
+
{"add", "change", "publish", "lock", "unlock", "bulk_delete"}
|
|
170
170
|
)
|
|
171
171
|
return [
|
|
172
172
|
perm.page
|
|
@@ -195,7 +195,7 @@ class PagePermissionPolicy(OwnershipPermissionPolicy):
|
|
|
195
195
|
return base_queryset
|
|
196
196
|
|
|
197
197
|
explorable_pages = self.instances_user_has_any_permission_for(
|
|
198
|
-
user, {"add", "change", "publish", "lock"}
|
|
198
|
+
user, {"add", "change", "publish", "lock", "unlock", "bulk_delete"}
|
|
199
199
|
)
|
|
200
200
|
|
|
201
201
|
# For all pages with specific permissions, add their ancestors as
|
|
@@ -156,6 +156,10 @@ STORAGES = {
|
|
|
156
156
|
},
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
# Django sets a maximum of 1000 fields per form by default, but particularly complex page models
|
|
160
|
+
# can exceed this limit within Wagtail's page editor.
|
|
161
|
+
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10_000
|
|
162
|
+
|
|
159
163
|
|
|
160
164
|
# Wagtail settings
|
|
161
165
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
Django>=5.1,<5.2
|
|
2
|
-
wagtail
|
|
2
|
+
wagtail==6.4rc1
|
wagtail/query.py
CHANGED
|
@@ -147,11 +147,17 @@ class SpecificQuerySetMixin:
|
|
|
147
147
|
super().__init__(*args, **kwargs)
|
|
148
148
|
# set by PageQuerySet.defer_streamfields()
|
|
149
149
|
self._defer_streamfields = False
|
|
150
|
+
self._specific_select_related_fields = ()
|
|
151
|
+
self._specific_prefetch_related_lookups = ()
|
|
150
152
|
|
|
151
153
|
def _clone(self):
|
|
152
154
|
"""Ensure clones inherit custom attribute values."""
|
|
153
155
|
clone = super()._clone()
|
|
154
156
|
clone._defer_streamfields = self._defer_streamfields
|
|
157
|
+
clone._specific_select_related_fields = self._specific_select_related_fields
|
|
158
|
+
clone._specific_prefetch_related_lookups = (
|
|
159
|
+
self._specific_prefetch_related_lookups
|
|
160
|
+
)
|
|
155
161
|
return clone
|
|
156
162
|
|
|
157
163
|
def specific(self, defer=False):
|
|
@@ -179,6 +185,137 @@ class SpecificQuerySetMixin:
|
|
|
179
185
|
(SpecificIterable, DeferredSpecificIterable),
|
|
180
186
|
)
|
|
181
187
|
|
|
188
|
+
def select_related(self, *fields, for_specific_subqueries: bool = False):
|
|
189
|
+
"""
|
|
190
|
+
Overrides Django's native :meth:`~django.db.models.query.QuerySet.select_related`
|
|
191
|
+
to allow related objects to be fetched by the subqueries made when a specific
|
|
192
|
+
queryset is evaluated.
|
|
193
|
+
|
|
194
|
+
When ``for_specific_subqueries`` is ``False`` (the default), the method functions
|
|
195
|
+
exactly like the original method. However, when ``True``, ``fields`` are
|
|
196
|
+
**required**, and must match names of ForeignKey fields on all specific models
|
|
197
|
+
that might be included in the result (which can include fields inherited from
|
|
198
|
+
concrete parents). Unlike when ``for_specific_subqueries`` is ``False``, no
|
|
199
|
+
validation is applied to ``fields`` when the method is called. Rather, that when
|
|
200
|
+
the method is called. Instead, that validation is applied for each individual
|
|
201
|
+
subquery when the queryset is evaluated. This difference in behaviour should be
|
|
202
|
+
taken into account when experimenting with ``for_specific_subqueries=True`` .
|
|
203
|
+
|
|
204
|
+
As with Django's native implementation, you chain multiple applications of
|
|
205
|
+
``select_related()`` with ``for_specific_subqueries=True`` to progressively add
|
|
206
|
+
to the list of fields to be fetched. For example:
|
|
207
|
+
|
|
208
|
+
.. code-block:: python
|
|
209
|
+
|
|
210
|
+
# Fetch 'author' when retrieving specific page data
|
|
211
|
+
queryset = Page.objects.specific().select_related("author", for_specific_subqueries=True)
|
|
212
|
+
|
|
213
|
+
# We're rendering cards with images, so fetch the listing image too
|
|
214
|
+
queryset = queryset.select_related("listing_image", for_specific_subqueries=True)
|
|
215
|
+
|
|
216
|
+
# Fetch some key taxonomy data too
|
|
217
|
+
queryset = queryset.select_related("topic", "target_audience", for_specific_subqueries=True)
|
|
218
|
+
|
|
219
|
+
As with Django's native implementation, ``None`` can be supplied in place of
|
|
220
|
+
``fields`` to negate a previous application of ``select_related()``. By default,
|
|
221
|
+
this will only work for cases where ``select_related()`` was called without
|
|
222
|
+
``for_specific_subqueries``, or with ``for_specific_subqueries=False``. However,
|
|
223
|
+
you can use ``for_specific_subqueries=True`` to negate subquery-specific
|
|
224
|
+
applications too. For example:
|
|
225
|
+
|
|
226
|
+
.. code-block:: python
|
|
227
|
+
|
|
228
|
+
# Fetch 'author' and 'listing_image' when retrieving specific page data
|
|
229
|
+
queryset = Page.objects.specific().select_related(
|
|
230
|
+
"author",
|
|
231
|
+
"listing_image",
|
|
232
|
+
for_specific_subqueries=True
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# I've changed my mind. Do not fetch any additional data
|
|
236
|
+
queryset = queryset.select_related(None, for_specific_subqueries=True)
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
if not for_specific_subqueries:
|
|
240
|
+
return super().select_related(*fields)
|
|
241
|
+
if not fields:
|
|
242
|
+
raise ValueError(
|
|
243
|
+
"'fields' must be specified when calling select_related() with for_specific_subqueries=True"
|
|
244
|
+
)
|
|
245
|
+
clone = self._chain()
|
|
246
|
+
if fields == (None,):
|
|
247
|
+
clone._specific_select_related_fields = ()
|
|
248
|
+
else:
|
|
249
|
+
clone._specific_select_related_fields = (
|
|
250
|
+
self._specific_select_related_fields + fields
|
|
251
|
+
)
|
|
252
|
+
return clone
|
|
253
|
+
|
|
254
|
+
def prefetch_related(self, *lookups, for_specific_subqueries: bool = False):
|
|
255
|
+
"""
|
|
256
|
+
Overrides Django's native :meth:`~django.db.models.query.QuerySet.prefetch_related`
|
|
257
|
+
implementation to allow related objects to be fetched alongside the subqueries made
|
|
258
|
+
when a specific queryset is evaluated.
|
|
259
|
+
|
|
260
|
+
When ``for_specific_subqueries`` is ``False`` (the default), the method functions
|
|
261
|
+
exactly like the original method. However, when ``True``, ``lookups`` are
|
|
262
|
+
**required**, and must match names of related fields on all specific models that
|
|
263
|
+
might be included in the result (which can include relationships inherited from
|
|
264
|
+
concrete parents). Unlike when ``for_specific_subqueries`` is ``False``, no
|
|
265
|
+
validation is applied to ``lookups`` when the method is called. Instead, that
|
|
266
|
+
validation is applied for each individual subquery when the queryset is
|
|
267
|
+
evaluated. This difference in behaviour should be taken into account when
|
|
268
|
+
experimenting with ``for_specific_subqueries=True``.
|
|
269
|
+
|
|
270
|
+
As with Django's native implementation, you chain multiple applications of
|
|
271
|
+
``prefetch_related()`` with ``for_specific_subqueries=True`` to progressively
|
|
272
|
+
add to the list of lookups to be made. For example:
|
|
273
|
+
|
|
274
|
+
.. code-block:: python
|
|
275
|
+
|
|
276
|
+
# Fetch 'contributors' when retrieving specific page data
|
|
277
|
+
queryset = Page.objects.specific().prefetch_related("contributors", for_specific_subqueries=True)
|
|
278
|
+
|
|
279
|
+
# We're rendering cards with images, so prefetch listing image renditions too
|
|
280
|
+
queryset = queryset.prefetch_related("listing_image__renditions", for_specific_subqueries=True)
|
|
281
|
+
|
|
282
|
+
# Fetch some key taxonomy data also
|
|
283
|
+
queryset = queryset.prefetch_related("tags", for_specific_subqueries=True)
|
|
284
|
+
|
|
285
|
+
As with Django's native implementation, ``None`` can be supplied in place of
|
|
286
|
+
``lookups`` to negate a previous application of ``prefetch_related()``. By default,
|
|
287
|
+
this will only work for cases where ``prefetch_related()`` was called without
|
|
288
|
+
``for_specific_subqueries``, or with ``for_specific_subqueries=False``. However,
|
|
289
|
+
you can use ``for_specific_subqueries=True`` to negate subquery-specific
|
|
290
|
+
applications too. For example:
|
|
291
|
+
|
|
292
|
+
.. code-block:: python
|
|
293
|
+
|
|
294
|
+
# Fetch 'contributors' and 'listing_image' renditions when retrieving specific page data
|
|
295
|
+
queryset = Page.objects.specific().prefetch_related(
|
|
296
|
+
"contributors",
|
|
297
|
+
"listing_image__renditions",
|
|
298
|
+
for_specific_subqueries=True
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# I've changed my mind. Do not make any additional queries
|
|
302
|
+
queryset = queryset.prefetch_related(None, for_specific_subqueries=True)
|
|
303
|
+
"""
|
|
304
|
+
if not for_specific_subqueries:
|
|
305
|
+
return super().prefetch_related(*lookups)
|
|
306
|
+
if not lookups:
|
|
307
|
+
raise ValueError(
|
|
308
|
+
"'lookups' must be provided when calling prefetch_related() with for_specific_subqueries=True"
|
|
309
|
+
)
|
|
310
|
+
clone = self._chain()
|
|
311
|
+
if lookups == (None,):
|
|
312
|
+
clone._specific_prefetch_related_lookups = ()
|
|
313
|
+
else:
|
|
314
|
+
clone._specific_prefetch_related_lookups = (
|
|
315
|
+
self._specific_prefetch_related_lookups + lookups
|
|
316
|
+
)
|
|
317
|
+
return clone
|
|
318
|
+
|
|
182
319
|
|
|
183
320
|
class PageQuerySet(SearchableQuerySetMixin, SpecificQuerySetMixin, TreeQuerySet):
|
|
184
321
|
def live_q(self):
|
|
@@ -561,6 +698,14 @@ class SpecificIterable(ModelIterable):
|
|
|
561
698
|
model = content_types[content_type].model_class() or qs.model
|
|
562
699
|
items = model.objects.filter(pk__in=pks)
|
|
563
700
|
|
|
701
|
+
if qs._specific_select_related_fields:
|
|
702
|
+
items = items.select_related(*qs._specific_select_related_fields)
|
|
703
|
+
|
|
704
|
+
if qs._specific_prefetch_related_lookups:
|
|
705
|
+
items = items.prefetch_related(
|
|
706
|
+
*qs._specific_prefetch_related_lookups
|
|
707
|
+
)
|
|
708
|
+
|
|
564
709
|
if qs._defer_streamfields and hasattr(items, "defer_streamfields"):
|
|
565
710
|
items = items.defer_streamfields()
|
|
566
711
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
from collections import OrderedDict
|
|
3
3
|
|
|
4
|
-
from django.db import
|
|
4
|
+
from django.db import (
|
|
5
|
+
NotSupportedError,
|
|
6
|
+
connections,
|
|
7
|
+
router,
|
|
8
|
+
transaction,
|
|
9
|
+
)
|
|
5
10
|
from django.db.models import Case, When
|
|
6
11
|
from django.db.models.aggregates import Avg, Count
|
|
7
12
|
from django.db.models.constants import LOOKUP_SEP
|
|
@@ -151,17 +156,22 @@ class ObjectIndexer:
|
|
|
151
156
|
|
|
152
157
|
|
|
153
158
|
class Index:
|
|
154
|
-
def __init__(self, backend
|
|
159
|
+
def __init__(self, backend):
|
|
155
160
|
self.backend = backend
|
|
156
161
|
self.name = self.backend.index_name
|
|
157
|
-
|
|
158
|
-
self.
|
|
159
|
-
|
|
162
|
+
|
|
163
|
+
self.read_connection = connections[router.db_for_read(IndexEntry)]
|
|
164
|
+
self.write_connection = connections[router.db_for_write(IndexEntry)]
|
|
165
|
+
|
|
166
|
+
if (
|
|
167
|
+
self.read_connection.vendor != "mysql"
|
|
168
|
+
or self.write_connection.vendor != "mysql"
|
|
169
|
+
):
|
|
160
170
|
raise NotSupportedError(
|
|
161
|
-
"You must select a MySQL database
|
|
171
|
+
"You must select a MySQL database to use MySQL search."
|
|
162
172
|
)
|
|
163
173
|
|
|
164
|
-
self.entries = IndexEntry._default_manager.
|
|
174
|
+
self.entries = IndexEntry._default_manager.all()
|
|
165
175
|
|
|
166
176
|
def add_model(self, model):
|
|
167
177
|
pass
|
|
@@ -201,11 +211,9 @@ class Index:
|
|
|
201
211
|
).update(title_norm=lavg / F("title_length"))
|
|
202
212
|
|
|
203
213
|
def delete_stale_model_entries(self, model):
|
|
204
|
-
existing_pks = (
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
.values("object_id")
|
|
208
|
-
)
|
|
214
|
+
existing_pks = model._default_manager.annotate(
|
|
215
|
+
object_id=Cast("pk", TextField())
|
|
216
|
+
).values("object_id")
|
|
209
217
|
content_types_pks = get_descendants_content_types_pks(model)
|
|
210
218
|
stale_entries = self.entries.filter(
|
|
211
219
|
content_type_id__in=content_types_pks
|
|
@@ -276,7 +284,7 @@ class Index:
|
|
|
276
284
|
update_method(content_type_pk, indexers)
|
|
277
285
|
|
|
278
286
|
def delete_item(self, item):
|
|
279
|
-
item.index_entries.all()._raw_delete(using=self.
|
|
287
|
+
item.index_entries.all()._raw_delete(using=self.write_connection.alias)
|
|
280
288
|
|
|
281
289
|
def __str__(self):
|
|
282
290
|
return self.name
|
|
@@ -610,7 +618,7 @@ class MySQLSearchRebuilder:
|
|
|
610
618
|
class MySQLSearchAtomicRebuilder(MySQLSearchRebuilder):
|
|
611
619
|
def __init__(self, index):
|
|
612
620
|
super().__init__(index)
|
|
613
|
-
self.transaction = transaction.atomic(using=index.
|
|
621
|
+
self.transaction = transaction.atomic(using=index.write_connection.alias)
|
|
614
622
|
self.transaction_opened = False
|
|
615
623
|
|
|
616
624
|
def start(self):
|
|
@@ -650,11 +658,11 @@ class MySQLSearchBackend(BaseSearchBackend):
|
|
|
650
658
|
if params.get("ATOMIC_REBUILD", False):
|
|
651
659
|
self.rebuilder_class = self.atomic_rebuilder_class
|
|
652
660
|
|
|
653
|
-
def get_index_for_model(self, model
|
|
654
|
-
return Index(self
|
|
661
|
+
def get_index_for_model(self, model):
|
|
662
|
+
return Index(self)
|
|
655
663
|
|
|
656
664
|
def get_index_for_object(self, obj):
|
|
657
|
-
return self.get_index_for_model(obj._meta.model
|
|
665
|
+
return self.get_index_for_model(obj._meta.model)
|
|
658
666
|
|
|
659
667
|
def reset_index(self):
|
|
660
668
|
for connection in [
|
|
@@ -3,7 +3,12 @@ from collections import OrderedDict
|
|
|
3
3
|
from functools import reduce
|
|
4
4
|
|
|
5
5
|
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
|
|
6
|
-
from django.db import
|
|
6
|
+
from django.db import (
|
|
7
|
+
NotSupportedError,
|
|
8
|
+
connections,
|
|
9
|
+
router,
|
|
10
|
+
transaction,
|
|
11
|
+
)
|
|
7
12
|
from django.db.models import Avg, Count, F, Manager, Q, TextField, Value
|
|
8
13
|
from django.db.models.constants import LOOKUP_SEP
|
|
9
14
|
from django.db.models.functions import Cast, Length
|
|
@@ -163,20 +168,22 @@ class ObjectIndexer:
|
|
|
163
168
|
|
|
164
169
|
|
|
165
170
|
class Index:
|
|
166
|
-
def __init__(self, backend
|
|
171
|
+
def __init__(self, backend):
|
|
167
172
|
self.backend = backend
|
|
168
173
|
self.name = self.backend.index_name
|
|
169
|
-
|
|
170
|
-
self.
|
|
171
|
-
|
|
174
|
+
|
|
175
|
+
self.read_connection = connections[router.db_for_read(IndexEntry)]
|
|
176
|
+
self.write_connection = connections[router.db_for_write(IndexEntry)]
|
|
177
|
+
|
|
178
|
+
if (
|
|
179
|
+
self.read_connection.vendor != "postgresql"
|
|
180
|
+
or self.write_connection.vendor != "postgresql"
|
|
181
|
+
):
|
|
172
182
|
raise NotSupportedError(
|
|
173
|
-
"You must select a PostgreSQL database
|
|
183
|
+
"You must select a PostgreSQL database to use PostgreSQL search."
|
|
174
184
|
)
|
|
175
185
|
|
|
176
|
-
|
|
177
|
-
self._enable_upsert = self.connection.pg_version >= 90500
|
|
178
|
-
|
|
179
|
-
self.entries = IndexEntry._default_manager.using(self.db_alias)
|
|
186
|
+
self.entries = IndexEntry._default_manager.all()
|
|
180
187
|
|
|
181
188
|
def add_model(self, model):
|
|
182
189
|
pass
|
|
@@ -216,11 +223,9 @@ class Index:
|
|
|
216
223
|
).update(title_norm=lavg / F("title_length"))
|
|
217
224
|
|
|
218
225
|
def delete_stale_model_entries(self, model):
|
|
219
|
-
existing_pks = (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
.values("object_id")
|
|
223
|
-
)
|
|
226
|
+
existing_pks = model._default_manager.annotate(
|
|
227
|
+
object_id=Cast("pk", TextField())
|
|
228
|
+
).values("object_id")
|
|
224
229
|
content_types_pks = get_descendants_content_types_pks(model)
|
|
225
230
|
stale_entries = self.entries.filter(
|
|
226
231
|
content_type_id__in=content_types_pks
|
|
@@ -237,8 +242,21 @@ class Index:
|
|
|
237
242
|
def add_item(self, obj):
|
|
238
243
|
self.add_items(obj._meta.model, [obj])
|
|
239
244
|
|
|
240
|
-
def
|
|
241
|
-
|
|
245
|
+
def add_items(self, model, objs):
|
|
246
|
+
search_fields = model.get_search_fields()
|
|
247
|
+
if not search_fields:
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
indexers = [ObjectIndexer(obj, self.backend) for obj in objs]
|
|
251
|
+
|
|
252
|
+
# TODO: Delete unindexed objects while dealing with proxy models.
|
|
253
|
+
if not indexers:
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
content_type_pk = get_content_type_pk(model)
|
|
257
|
+
compiler = InsertQuery(IndexEntry).get_compiler(
|
|
258
|
+
connection=self.write_connection
|
|
259
|
+
)
|
|
242
260
|
title_sql = []
|
|
243
261
|
autocomplete_sql = []
|
|
244
262
|
body_sql = []
|
|
@@ -251,7 +269,7 @@ class Index:
|
|
|
251
269
|
value = compiler.prepare_value(
|
|
252
270
|
IndexEntry._meta.get_field("title"), indexer.title
|
|
253
271
|
)
|
|
254
|
-
sql, params = value.as_sql(compiler, self.
|
|
272
|
+
sql, params = value.as_sql(compiler, self.write_connection)
|
|
255
273
|
title_sql.append(sql)
|
|
256
274
|
data_params.extend(params)
|
|
257
275
|
|
|
@@ -259,7 +277,7 @@ class Index:
|
|
|
259
277
|
value = compiler.prepare_value(
|
|
260
278
|
IndexEntry._meta.get_field("autocomplete"), indexer.autocomplete
|
|
261
279
|
)
|
|
262
|
-
sql, params = value.as_sql(compiler, self.
|
|
280
|
+
sql, params = value.as_sql(compiler, self.write_connection)
|
|
263
281
|
autocomplete_sql.append(sql)
|
|
264
282
|
data_params.extend(params)
|
|
265
283
|
|
|
@@ -267,7 +285,7 @@ class Index:
|
|
|
267
285
|
value = compiler.prepare_value(
|
|
268
286
|
IndexEntry._meta.get_field("body"), indexer.body
|
|
269
287
|
)
|
|
270
|
-
sql, params = value.as_sql(compiler, self.
|
|
288
|
+
sql, params = value.as_sql(compiler, self.write_connection)
|
|
271
289
|
body_sql.append(sql)
|
|
272
290
|
data_params.extend(params)
|
|
273
291
|
|
|
@@ -278,7 +296,7 @@ class Index:
|
|
|
278
296
|
]
|
|
279
297
|
)
|
|
280
298
|
|
|
281
|
-
with self.
|
|
299
|
+
with self.write_connection.cursor() as cursor:
|
|
282
300
|
cursor.execute(
|
|
283
301
|
"""
|
|
284
302
|
INSERT INTO %s (content_type_id, object_id, title, autocomplete, body, title_norm)
|
|
@@ -295,65 +313,8 @@ class Index:
|
|
|
295
313
|
|
|
296
314
|
self._refresh_title_norms()
|
|
297
315
|
|
|
298
|
-
def add_items_update_then_create(self, content_type_pk, indexers):
|
|
299
|
-
ids_and_data = {}
|
|
300
|
-
for indexer in indexers:
|
|
301
|
-
ids_and_data[indexer.id] = (
|
|
302
|
-
indexer.title,
|
|
303
|
-
indexer.autocomplete,
|
|
304
|
-
indexer.body,
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
index_entries_for_ct = self.entries.filter(content_type_id=content_type_pk)
|
|
308
|
-
indexed_ids = frozenset(
|
|
309
|
-
index_entries_for_ct.filter(object_id__in=ids_and_data.keys()).values_list(
|
|
310
|
-
"object_id", flat=True
|
|
311
|
-
)
|
|
312
|
-
)
|
|
313
|
-
for indexed_id in indexed_ids:
|
|
314
|
-
title, autocomplete, body = ids_and_data[indexed_id]
|
|
315
|
-
index_entries_for_ct.filter(object_id=indexed_id).update(
|
|
316
|
-
title=title, autocomplete=autocomplete, body=body
|
|
317
|
-
)
|
|
318
|
-
|
|
319
|
-
to_be_created = []
|
|
320
|
-
for object_id in ids_and_data.keys():
|
|
321
|
-
if object_id not in indexed_ids:
|
|
322
|
-
title, autocomplete, body = ids_and_data[object_id]
|
|
323
|
-
to_be_created.append(
|
|
324
|
-
IndexEntry(
|
|
325
|
-
content_type_id=content_type_pk,
|
|
326
|
-
object_id=object_id,
|
|
327
|
-
title=title,
|
|
328
|
-
autocomplete=autocomplete,
|
|
329
|
-
body=body,
|
|
330
|
-
)
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
self.entries.bulk_create(to_be_created)
|
|
334
|
-
|
|
335
|
-
self._refresh_title_norms()
|
|
336
|
-
|
|
337
|
-
def add_items(self, model, objs):
|
|
338
|
-
search_fields = model.get_search_fields()
|
|
339
|
-
if not search_fields:
|
|
340
|
-
return
|
|
341
|
-
|
|
342
|
-
indexers = [ObjectIndexer(obj, self.backend) for obj in objs]
|
|
343
|
-
|
|
344
|
-
# TODO: Delete unindexed objects while dealing with proxy models.
|
|
345
|
-
if indexers:
|
|
346
|
-
content_type_pk = get_content_type_pk(model)
|
|
347
|
-
|
|
348
|
-
update_method = (
|
|
349
|
-
self.add_items_upsert
|
|
350
|
-
if self._enable_upsert
|
|
351
|
-
else self.add_items_update_then_create
|
|
352
|
-
)
|
|
353
|
-
update_method(content_type_pk, indexers)
|
|
354
|
-
|
|
355
316
|
def delete_item(self, item):
|
|
356
|
-
item.index_entries.all()._raw_delete(using=self.
|
|
317
|
+
item.index_entries.all()._raw_delete(using=self.write_connection.alias)
|
|
357
318
|
|
|
358
319
|
def __str__(self):
|
|
359
320
|
return self.name
|
|
@@ -709,7 +670,7 @@ class PostgresSearchRebuilder:
|
|
|
709
670
|
class PostgresSearchAtomicRebuilder(PostgresSearchRebuilder):
|
|
710
671
|
def __init__(self, index):
|
|
711
672
|
super().__init__(index)
|
|
712
|
-
self.transaction = transaction.atomic(using=index.
|
|
673
|
+
self.transaction = transaction.atomic(using=index.write_connection.alias)
|
|
713
674
|
self.transaction_opened = False
|
|
714
675
|
|
|
715
676
|
def start(self):
|
|
@@ -750,11 +711,11 @@ class PostgresSearchBackend(BaseSearchBackend):
|
|
|
750
711
|
if params.get("ATOMIC_REBUILD", False):
|
|
751
712
|
self.rebuilder_class = self.atomic_rebuilder_class
|
|
752
713
|
|
|
753
|
-
def get_index_for_model(self, model
|
|
754
|
-
return Index(self
|
|
714
|
+
def get_index_for_model(self, model):
|
|
715
|
+
return Index(self)
|
|
755
716
|
|
|
756
717
|
def get_index_for_object(self, obj):
|
|
757
|
-
return self.get_index_for_model(obj._meta.model
|
|
718
|
+
return self.get_index_for_model(obj._meta.model)
|
|
758
719
|
|
|
759
720
|
def reset_index(self):
|
|
760
721
|
for connection in [
|