|
20 | 20 | from django.contrib.contenttypes.models import ContentType |
21 | 21 | from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied |
22 | 22 | from django.db import models |
23 | | -from django.db.models import OuterRef, Subquery |
| 23 | +from django.db.models import OuterRef, Prefetch, Subquery |
24 | 24 | from django.db.models.functions import Cast, Lower |
25 | 25 | from django.forms import MediaDefiningClass |
26 | 26 | from django.http import ( |
|
50 | 50 | get_current_site, |
51 | 51 | get_editable_url, |
52 | 52 | get_latest_admin_viewable_content, |
| 53 | + get_latest_content_from_cache, |
53 | 54 | get_object_live_url, |
54 | 55 | get_preview_url, |
55 | 56 | proxy_model, |
@@ -182,20 +183,29 @@ def _extra_grouping_fields(self): |
182 | 183 |
|
183 | 184 | def get_indicator_column(self, request): |
184 | 185 | def indicator(obj): |
| 186 | + versions = None |
185 | 187 | if self._extra_grouping_fields is not None: # Grouper Model |
186 | 188 | content_obj = get_latest_admin_viewable_content( |
187 | 189 | obj, |
188 | 190 | include_unpublished_archived=True, |
189 | 191 | **{field: getattr(self, field) for field in self._extra_grouping_fields}, |
190 | 192 | ) |
| 193 | + for prefetched in getattr(obj, "_prefetched_contents", []): |
| 194 | + prefetched._prefetched_versions[0].content = prefetched # Avoid fetching reverse |
| 195 | + versions = ( |
| 196 | + [content._prefetched_versions[0] for content in obj._prefetched_contents] |
| 197 | + if hasattr(obj, "_prefetched_contents") |
| 198 | + else None |
| 199 | + ) |
191 | 200 | else: # Content Model |
192 | 201 | content_obj = obj |
193 | | - status = content_indicator(content_obj) |
| 202 | + |
| 203 | + status = content_indicator(content_obj, versions) |
194 | 204 | menu = ( |
195 | 205 | content_indicator_menu( |
196 | 206 | request, |
197 | 207 | status, |
198 | | - content_obj._version, |
| 208 | + content_obj._versions, |
199 | 209 | back=f"{request.path_info}?{request.GET.urlencode()}", |
200 | 210 | ) |
201 | 211 | if status |
@@ -318,8 +328,37 @@ def get_queryset(self, request: HttpRequest) -> models.QuerySet: |
318 | 328 | # cast is necessary for mysql |
319 | 329 | content_modified=Cast(Subquery(contents.values("content_modified")[:1]), models.DateTimeField()), |
320 | 330 | ) |
| 331 | + # Prefetching is not implemented here; you may want to use Prefetch for related objects if needed. |
| 332 | + # To get the reverse name for self.grouper_field_name: |
| 333 | + # It's usually: <related_model>_set or the related_name defined on the ForeignKey. |
| 334 | + # For a ForeignKey field, you can get the reverse accessor name like this: |
| 335 | + reverse_name = self.content_model._meta.get_field(self.grouper_field_name).remote_field.get_accessor_name() |
| 336 | + qs = qs.prefetch_related( |
| 337 | + Prefetch( |
| 338 | + reverse_name, |
| 339 | + to_attr="_prefetched_contents", # Needed for state indicators |
| 340 | + queryset=self.content_model.admin_manager.filter(**self.current_content_filters) |
| 341 | + .prefetch_related(Prefetch("versions", to_attr="_prefetched_versions")) |
| 342 | + .order_by("-pk"), |
| 343 | + ) |
| 344 | + ) |
| 345 | + qs = qs.prefetch_related( |
| 346 | + Prefetch( |
| 347 | + reverse_name, |
| 348 | + to_attr="_current_contents", # Service for get_content_obj |
| 349 | + queryset=self.content_model.admin_manager.current_content(**self.current_content_filters) |
| 350 | + .prefetch_related(Prefetch("versions", to_attr="_prefetched_versions")) |
| 351 | + .order_by("-pk"), |
| 352 | + ) |
| 353 | + ) |
321 | 354 | return qs |
322 | 355 |
|
| 356 | + def get_content_obj(self, obj: models.Model) -> models.Model: |
| 357 | + """Returns the latest content object for the given grouper object.""" |
| 358 | + if obj is None or self._is_content_obj(obj) or not hasattr(obj, "_prefetched_contents"): |
| 359 | + return super().get_content_obj(obj) |
| 360 | + return get_latest_content_from_cache(obj._prefetched_contents, include_unpublished_archived=True) |
| 361 | + |
323 | 362 | @admin.display( |
324 | 363 | description=_("State"), |
325 | 364 | ordering="content_state", |
|
0 commit comments