Skip to content

Commit a2df14e

Browse files
committed
refactor: reduce query fetch for determining header data
1 parent 12279e6 commit a2df14e

3 files changed

Lines changed: 73 additions & 7 deletions

File tree

core/context_processors.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,20 @@
99
from versions.models import Version
1010

1111

12+
def _get_header_version_data(request):
13+
"""Per-request shared accessor so `current_version` and `selected_version`
14+
share one DB fetch within a single request."""
15+
cached = getattr(request, "_header_version_data", None)
16+
if cached is not None:
17+
return cached
18+
data = Version.objects.get_header_dropdown_data()
19+
request._header_version_data = data
20+
return data
21+
22+
1223
def current_version(request):
1324
"""Custom context processor that adds the current release to the context"""
14-
return {"current_version": Version.objects.most_recent()}
25+
return {"current_version": _get_header_version_data(request).most_recent}
1526

1627

1728
def selected_version(request):
@@ -65,12 +76,17 @@ def selected_version(request):
6576
is_url_driven = bool(url_version_slug)
6677
cookie_slug = get_version_from_cookie(request)
6778

79+
header_data = _get_header_version_data(request)
80+
options = list(header_data.options)
81+
6882
resolved_slug = url_version_slug or cookie_slug
6983
version = None
7084
if resolved_slug and resolved_slug != LATEST_RELEASE_URL_PATH_STR:
71-
version = Version.objects.filter(slug=resolved_slug).first()
85+
version = next((v for v in options if v.slug == resolved_slug), None)
86+
if version is None:
87+
version = Version.objects.filter(slug=resolved_slug).first()
7288
if version is None:
73-
version = Version.objects.most_recent()
89+
version = header_data.most_recent
7490

7591
is_explicit_non_latest_url = bool(
7692
url_version_slug and url_version_slug != LATEST_RELEASE_URL_PATH_STR
@@ -84,9 +100,6 @@ def selected_version(request):
84100

85101
label = version.display_name if (is_explicit and version) else "Latest"
86102

87-
# Materialize: _annotate_option_hrefs mutates each option with a `.href`.
88-
options = list(Version.objects.get_dropdown_versions())
89-
90103
latest_href = ""
91104
if is_url_driven and resolver_match and resolver_match.view_name:
92105
latest_href = _annotate_option_hrefs(

news/tests/test_views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ def test_news_create_get(tp, regular_user, url_name, form_class):
419419
# assertGoodView expects a resolved URL
420420
# see https://github.com/revsys/django-test-plus/issues/202
421421
url = tp.reverse(url_name)
422-
tp.assertGoodView(url, test_query_count=4, verbose=True)
422+
tp.assertGoodView(url, test_query_count=5, verbose=True)
423423

424424
form = tp.get_context("form")
425425
assert isinstance(form, form_class)

versions/managers.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import NamedTuple
2+
13
from django.db import models
24
from django.db.models import Func, Value, Count, Q
35
from django.db.models.functions import Replace
@@ -9,6 +11,14 @@
911
)
1012

1113

14+
class HeaderVersionData(NamedTuple):
15+
"""Consolidated data needed to render the navbar version dropdown."""
16+
17+
options: list
18+
most_recent: "Version | None" # noqa: F821
19+
most_recent_beta: "Version | None" # noqa: F821
20+
21+
1222
class VersionQuerySet(models.QuerySet):
1323
def active(self):
1424
"""Return active versions"""
@@ -166,6 +176,49 @@ def should_show_beta(most_recent_beta):
166176

167177
return queryset.order_by(order_by)
168178

179+
def get_header_dropdown_data(self) -> HeaderVersionData:
180+
"""Single-query fetch for the navbar version dropdown.
181+
182+
Replaces the 3-4 queries from calling `most_recent()`,
183+
`most_recent_beta()`, and `get_dropdown_versions()` separately. Dropdown
184+
list matches `get_dropdown_versions()` for the header's use case only —
185+
no develop/master, no library filtering.
186+
"""
187+
name_exclusions = [
188+
"head",
189+
MASTER_RELEASE_URL_PATH_STR,
190+
DEVELOP_RELEASE_URL_PATH_STR,
191+
]
192+
versions = list(
193+
self.active()
194+
.filter(Q(full_release=True) | Q(beta=True))
195+
.exclude(name__in=name_exclusions)
196+
.defer("data")
197+
.order_by("-name")
198+
)
199+
200+
most_recent = next((v for v in versions if not v.beta and v.full_release), None)
201+
most_recent_beta = next((v for v in versions if v.beta), None)
202+
203+
include_beta = (
204+
most_recent_beta is not None
205+
and most_recent is not None
206+
and most_recent_beta.cleaned_version_parts
207+
> most_recent.cleaned_version_parts
208+
)
209+
if include_beta:
210+
options = [
211+
v for v in versions if not v.beta or v.name == most_recent_beta.name
212+
]
213+
else:
214+
options = [v for v in versions if not v.beta]
215+
216+
return HeaderVersionData(
217+
options=options,
218+
most_recent=most_recent,
219+
most_recent_beta=most_recent_beta,
220+
)
221+
169222

170223
class VersionFileQuerySet(models.QuerySet):
171224
def active(self):

0 commit comments

Comments
 (0)