Move organization detail queryset optimizations to model; add tests for organization detail queryset
All checks were successful
Deploy to Server / deploy (push) Successful in 30s

This commit is contained in:
Joakim Hellsén 2026-04-12 05:19:24 +02:00
commit 1790bac2e0
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
3 changed files with 96 additions and 11 deletions

View file

@ -75,6 +75,27 @@ class Organization(auto_prefetch.Model):
"""Return organizations with only fields needed by the org list page.""" """Return organizations with only fields needed by the org list page."""
return cls.objects.only("twitch_id", "name").order_by("name") return cls.objects.only("twitch_id", "name").order_by("name")
@classmethod
def for_detail_view(cls) -> models.QuerySet[Organization]:
"""Return organizations with only fields and relations needed by detail page."""
return cls.objects.only(
"twitch_id",
"name",
"added_at",
"updated_at",
).prefetch_related(
models.Prefetch(
"games",
queryset=Game.objects.only(
"twitch_id",
"name",
"display_name",
"slug",
).order_by("display_name"),
to_attr="games_for_detail",
),
)
def feed_description(self: Organization) -> str: def feed_description(self: Organization) -> str:
"""Return a description of the organization for RSS feeds.""" """Return a description of the organization for RSS feeds."""
name: str = self.name or "Unknown Organization" name: str = self.name or "Unknown Organization"

View file

@ -2397,6 +2397,73 @@ class TestChannelListView:
assert response.status_code == 200 assert response.status_code == 200
assert "organization" in response.context assert "organization" in response.context
@pytest.mark.django_db
def test_organization_detail_queryset_only_selects_rendered_fields(self) -> None:
"""Organization detail queryset should defer non-rendered organization fields."""
org: Organization = Organization.objects.create(
twitch_id="org_detail_fields",
name="Org Detail Fields",
)
Game.objects.create(
twitch_id="org_detail_game_fields",
name="Org Detail Game Fields",
display_name="Org Detail Game Fields",
).owners.add(org)
fetched_org: Organization | None = (
Organization.for_detail_view().filter(twitch_id=org.twitch_id).first()
)
assert fetched_org is not None
deferred_fields: set[str] = fetched_org.get_deferred_fields()
assert "twitch_id" not in deferred_fields
assert "name" not in deferred_fields
assert "added_at" not in deferred_fields
assert "updated_at" not in deferred_fields
@pytest.mark.django_db
def test_organization_detail_prefetched_games_do_not_trigger_extra_queries(
self,
) -> None:
"""Organization detail should prefetch games used by the template."""
org: Organization = Organization.objects.create(
twitch_id="org_detail_prefetch",
name="Org Detail Prefetch",
)
game: Game = Game.objects.create(
twitch_id="org_detail_prefetch_game",
name="Org Detail Prefetch Game",
display_name="Org Detail Prefetch Game",
slug="org-detail-prefetch-game",
)
game.owners.add(org)
fetched_org: Organization = Organization.for_detail_view().get(
twitch_id=org.twitch_id,
)
games_for_detail: list[Game] = list(
getattr(fetched_org, "games_for_detail", []),
)
assert len(games_for_detail) == 1
with CaptureQueriesContext(connection) as queries:
game_row: Game = games_for_detail[0]
_ = game_row.twitch_id
_ = game_row.display_name
_ = game_row.name
_ = str(game_row)
select_queries: list[str] = [
query_info["sql"]
for query_info in queries.captured_queries
if query_info["sql"].lstrip().upper().startswith("SELECT")
]
assert not select_queries, (
"Organization detail prefetched games triggered unexpected SELECT queries. "
f"Queries: {select_queries}"
)
@pytest.mark.django_db @pytest.mark.django_db
def test_channel_detail_view(self, client: Client, db: None) -> None: def test_channel_detail_view(self, client: Client, db: None) -> None:
"""Test channel detail view returns 200 and has channel in context.""" """Test channel detail view returns 200 and has channel in context."""

View file

@ -316,21 +316,18 @@ def organization_detail_view(request: HttpRequest, twitch_id: str) -> HttpRespon
Returns: Returns:
HttpResponse: The rendered organization detail page. HttpResponse: The rendered organization detail page.
Raises:
Http404: If the organization is not found.
""" """
try: organization: Organization = get_object_or_404(
organization: Organization = Organization.objects.get(twitch_id=twitch_id) Organization.for_detail_view(),
except Organization.DoesNotExist as exc: twitch_id=twitch_id,
msg = "No organization found matching the query" )
raise Http404(msg) from exc
games: QuerySet[Game] = organization.games.all() # pyright: ignore[reportAttributeAccessIssue] games: list[Game] = list(getattr(organization, "games_for_detail", []))
org_name: str = organization.name or organization.twitch_id org_name: str = organization.name or organization.twitch_id
games_count: int = games.count() games_count: int = len(games)
s: Literal["", "s"] = "" if games_count == 1 else "s" noun: str = "game" if games_count == 1 else "games"
org_description: str = f"{org_name} has {games_count} game{s}." org_description: str = f"{org_name} has {games_count} {noun}."
url: str = build_absolute_uri( url: str = build_absolute_uri(
reverse("twitch:organization_detail", args=[organization.twitch_id]), reverse("twitch:organization_detail", args=[organization.twitch_id]),