feat(games): honor ?sort= on list_games + eager-load platform (#68)

This commit is contained in:
2026-06-21 13:53:00 +02:00
parent 65d9f29a3e
commit 0fa1697310
2 changed files with 37 additions and 1 deletions
+8 -1
View File
@@ -1,5 +1,6 @@
from typing import Any from typing import Any
from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.db.models import Q from django.db.models import Q
@@ -48,6 +49,7 @@ from common.time import (
) )
from common.utils import build_dynamic_filter, paginate, safe_division, truncate from common.utils import build_dynamic_filter, paginate, safe_division, truncate
from games.filters import parse_game_filter from games.filters import parse_game_filter
from games.sorting import GAME_DEFAULT_SORT, GAME_SORTS, apply_sort, parse_find_filter
from games.forms import GameForm from games.forms import GameForm
from games.models import Game from games.models import Game
from games.views.general import use_custom_redirect from games.views.general import use_custom_redirect
@@ -56,7 +58,7 @@ from games.views.playevent import create_playevent_tabledata
@login_required @login_required
def list_games(request: HttpRequest, search_string: str = "") -> HttpResponse: def list_games(request: HttpRequest, search_string: str = "") -> HttpResponse:
games = Game.objects.order_by("-created_at") games = Game.objects.select_related("platform")
# ── Structured filter (Stash-style JSON) ── # ── Structured filter (Stash-style JSON) ──
filter_json = request.GET.get("filter", "") filter_json = request.GET.get("filter", "")
@@ -86,6 +88,11 @@ def list_games(request: HttpRequest, search_string: str = "") -> HttpResponse:
filters.append(Q(status=search_status)) filters.append(Q(status=search_status))
games = games.filter(build_dynamic_filter(filters, "|")) games = games.filter(build_dynamic_filter(filters, "|"))
sort = apply_sort(games, parse_find_filter(request), GAME_SORTS, GAME_DEFAULT_SORT)
games = sort.queryset
for key in sort.unknown:
messages.warning(request, f"Unknown sort field '{key}' was ignored.")
games, page_obj, elided_page_range = paginate(request, games) games, page_obj, elided_page_range = paginate(request, games)
data = { data = {
+29
View File
@@ -5,7 +5,9 @@ from zoneinfo import ZoneInfo
import pytest import pytest
from django.conf import settings from django.conf import settings
from django.contrib.messages import get_messages
from django.test import RequestFactory from django.test import RequestFactory
from django.urls import reverse
from games.filters import FindFilter from games.filters import FindFilter
from games.models import Game, Platform, Session from games.models import Game, Platform, Session
@@ -137,3 +139,30 @@ class TestSortMapShapes:
]: ]:
for token in default.split(","): for token in default.split(","):
assert token.lstrip("-") in sort_map assert token.lstrip("-") in sort_map
@pytest.fixture
def logged_client(client, django_user_model):
user = django_user_model.objects.create_user(username="u", password="p")
client.force_login(user)
return client
class TestListGamesSort:
def test_sort_param_orders_rows(self, logged_client, two_games):
alpha, beta = two_games
response = logged_client.get(reverse("games:list_games"), {"sort": "-name"})
assert response.status_code == 200
body = response.content.decode()
assert body.index("Beta") < body.index("Alpha")
def test_unknown_sort_emits_warning_message(self, logged_client, two_games):
response = logged_client.get(reverse("games:list_games"), {"sort": "bogus"})
assert response.status_code == 200
warnings = [str(m) for m in get_messages(response.wsgi_request)]
assert any("bogus" in w for w in warnings)
def test_valid_sort_emits_no_warning(self, logged_client, two_games):
response = logged_client.get(reverse("games:list_games"), {"sort": "name"})
warnings = [str(m) for m in get_messages(response.wsgi_request)]
assert warnings == []