diff --git a/djangocms_versioning/cms_config.py b/djangocms_versioning/cms_config.py index 39dbc70a..ae864f2b 100644 --- a/djangocms_versioning/cms_config.py +++ b/djangocms_versioning/cms_config.py @@ -3,7 +3,6 @@ from cms.app_base import CMSAppConfig, CMSAppExtension from cms.extensions.models import BaseExtension from cms.models import PageContent, Placeholder -from cms.utils import get_language_from_request from cms.utils.i18n import get_language_list, get_language_tuple from cms.utils.plugins import copy_plugins_to_placeholder from cms.utils.urlutils import admin_reverse @@ -14,6 +13,7 @@ ObjectDoesNotExist, PermissionDenied, ) +from django.db.models import Prefetch from django.http import ( HttpResponse, HttpResponseBadRequest, @@ -23,7 +23,7 @@ from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ -from . import indicators, versionables +from . import indicators from .admin import VersioningAdminMixin from .constants import INDICATOR_DESCRIPTIONS from .datastructures import BaseVersionableItem, VersionableItem @@ -284,18 +284,8 @@ def get_readonly_fields(self, request, obj=None): return fields def get_queryset(self, request): - urls = ("cms_pagecontent_get_tree",) - queryset = super().get_queryset(request) - if request.resolver_match.url_name in urls: - versionable = versionables.for_content(queryset.model) - - # TODO: Improve the grouping filters to use anything defined in the - # apps versioning config extra_grouping_fields - grouping_filters = {} - if "language" in versionable.extra_grouping_fields: - grouping_filters["language"] = get_language_from_request(request) - - return queryset.filter(pk__in=versionable.distinct_groupers(**grouping_filters)) + queryset = super().get_queryset(request)\ + .prefetch_related(Prefetch("versions", to_attr="prefetched_versions")) return queryset # CAVEAT: @@ -361,7 +351,18 @@ def get_indicator_menu(cls, request, page_content): """Get the indicator menu for PageContent object taking into account the currently available versions""" menu_template = "admin/cms/page/tree/indicator_menu.html" - status = page_content.content_indicator() + if hasattr(page_content.page, "filtered_translations") and hasattr(page_content, "prefetched_versions"): + # get_tree has prefetched versions + versions = sorted( + [content.prefetched_versions[0] for content in page_content.page.filtered_translations], + key=lambda version: -version.pk, + ) + for content in page_content.page.filtered_translations: + content.__dict__["content"] = content + status = page_content.content_indicator(versions) + else: + # No prefetched versions available, get them ourselves + status = page_content.content_indicator() if not status or status == "empty": # pragma: no cover return super().get_indicator_menu(request, page_content) versions = page_content._version # Cache from .content_indicator() diff --git a/djangocms_versioning/helpers.py b/djangocms_versioning/helpers.py index 19abd78d..9afa7140 100644 --- a/djangocms_versioning/helpers.py +++ b/djangocms_versioning/helpers.py @@ -387,7 +387,11 @@ def content_is_unlocked_for_user(content: models.Model, user: settings.AUTH_USER """Check if lock doesn't exist or object is locked to provided user. """ try: - return version_is_unlocked_for_user(content.versions.first(), user) + if hasattr(content, "prefetched_versions"): + version = content.prefetched_versions[0] + else: + version = content.versions.first() + return version_is_unlocked_for_user(version, user) except AttributeError: return True @@ -425,16 +429,16 @@ def send_email( def get_latest_draft_version(version): - """Get latest draft version of version object and caches it - """ + """Get latest draft version of version object and caches it in the + content object""" from djangocms_versioning.constants import DRAFT from djangocms_versioning.models import Version - if not hasattr(version, "_latest_draft_version"): + if not hasattr(version.content, "_latest_draft_version"): drafts = ( Version.objects .filter_by_content_grouping_values(version.content) .filter(state=DRAFT) ) - version._latest_draft_version = drafts.first() - return version._latest_draft_version + version.content._latest_draft_version = drafts.first() + return version.content._latest_draft_version diff --git a/djangocms_versioning/indicators.py b/djangocms_versioning/indicators.py index 7424d97c..a23ebd13 100644 --- a/djangocms_versioning/indicators.py +++ b/djangocms_versioning/indicators.py @@ -1,5 +1,8 @@ +import typing + from cms.utils.urlutils import admin_reverse from django.contrib.auth import get_permission_codename +from django.db import models from django.utils.http import urlencode from django.utils.translation import gettext_lazy as _ @@ -87,16 +90,21 @@ def content_indicator_menu(request, status, versions, back=""): return menu -def content_indicator(content_obj): +def content_indicator( + content_obj: models.Model, + versions: typing.Optional[list[Version]] = None +) -> typing.Optional[str]: """Translates available versions into status to be reflected by the indicator. Function caches the result in the page_content object""" if not content_obj: return None # pragma: no cover elif not hasattr(content_obj, "_indicator_status"): - versions = Version.objects.filter_by_content_grouping_values( - content_obj - ).order_by("-pk") + if versions is None: + # Get all versions for the content object if not available + versions = Version.objects.filter_by_content_grouping_values( + content_obj + ).order_by("-pk") version_states = dict(VERSION_STATES) signature = { version.state: version diff --git a/djangocms_versioning/models.py b/djangocms_versioning/models.py index 0f4a3956..08ac8079 100644 --- a/djangocms_versioning/models.py +++ b/djangocms_versioning/models.py @@ -74,8 +74,8 @@ def filter_by_grouping_values(self, versionable, **kwargs): def filter_by_content_grouping_values(self, content): """Returns a list of Version objects for grouping values taken - from provided content object. In other words: - it uses the content instance property values as filter parameters + from provided content object. In other words: + it uses the content instance property values as filter parameters """ versionable = versionables.for_content(content) content_objects = versionable.for_content_grouping_values(content) diff --git a/djangocms_versioning/templatetags/djangocms_versioning.py b/djangocms_versioning/templatetags/djangocms_versioning.py index 2c07cd12..6b9bbcfe 100644 --- a/djangocms_versioning/templatetags/djangocms_versioning.py +++ b/djangocms_versioning/templatetags/djangocms_versioning.py @@ -13,7 +13,10 @@ def url_version_list(content): @register.filter def url_publish_version(content, user): - version = content.versions.first() + if hasattr(content, "prefetched_versions"): + version = content.prefetched_versions[0] + else: + version = content.versions.first() if version: if version.check_publish.as_bool(user) and version.can_be_published(): proxy_model = versionables.for_content(content).version_model_proxy @@ -25,7 +28,10 @@ def url_publish_version(content, user): @register.filter def url_new_draft(content, user): - version = content.versions.first() + if hasattr(content, "prefetched_versions"): + version = content.prefetched_versions[0] + else: + version = content.versions.first() if version: if version.state == constants.PUBLISHED: proxy_model = versionables.for_content(content).version_model_proxy @@ -37,7 +43,10 @@ def url_new_draft(content, user): @register.filter def url_revert_version(content, user): - version = content.versions.first() + if hasattr(content, "prefetched_versions"): + version = content.prefetched_versions[0] + else: + version = content.versions.first() if version: if version.check_revert.as_bool(user): proxy_model = versionables.for_content(content).version_model_proxy