Make invalid search input explicit in UI and API

This commit is contained in:
Alfredo Di Stasio
2026-03-10 15:53:55 +01:00
parent 92c804a474
commit 2586f15ae8
6 changed files with 150 additions and 17 deletions

View File

@ -1,5 +1,7 @@
from rest_framework import generics
from rest_framework import status
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.throttling import AnonRateThrottle, UserRateThrottle
from apps.competitions.models import Competition, Season
@ -39,17 +41,35 @@ class PlayerSearchApiView(ReadOnlyBaseAPIView, generics.ListAPIView):
serializer_class = PlayerListSerializer
pagination_class = ApiPagination
def get_search_form(self):
if not hasattr(self, "_search_form"):
self._search_form = PlayerSearchForm(self.request.query_params)
return self._search_form
def _validation_error_response(self):
form = self.get_search_form()
return Response(
{
"detail": "Invalid search parameters.",
"errors": form.errors.get_json_data(escape_html=True),
},
status=status.HTTP_400_BAD_REQUEST,
)
def list(self, request, *args, **kwargs):
form = self.get_search_form()
if form.is_bound and not form.is_valid():
return self._validation_error_response()
return super().list(request, *args, **kwargs)
def get_queryset(self):
form = PlayerSearchForm(self.request.query_params or None)
form = self.get_search_form()
queryset = base_player_queryset()
if form.is_valid():
queryset = filter_players(queryset, form.cleaned_data)
sort_key = form.cleaned_data.get("sort", "name_asc")
if sort_key in METRIC_SORT_KEYS:
queryset = annotate_player_metrics(queryset, form.cleaned_data)
queryset = apply_sorting(queryset, sort_key)
else:
queryset = queryset.order_by("full_name", "id")
queryset = filter_players(queryset, form.cleaned_data)
sort_key = form.cleaned_data.get("sort", "name_asc")
if sort_key in METRIC_SORT_KEYS:
queryset = annotate_player_metrics(queryset, form.cleaned_data)
queryset = apply_sorting(queryset, sort_key)
return queryset

View File

@ -33,7 +33,7 @@ class PlayerSearchView(ListView):
def get_form(self):
if not hasattr(self, "_search_form"):
self._search_form = PlayerSearchForm(self.request.GET or None)
self._search_form = PlayerSearchForm(self.request.GET)
return self._search_form
def get_paginate_by(self, queryset):
@ -44,20 +44,23 @@ class PlayerSearchView(ListView):
def get_queryset(self):
form = self.get_form()
form_valid = form.is_valid()
if form.is_bound and not form_valid:
return Player.objects.none()
queryset = base_player_queryset()
if form.is_valid():
queryset = filter_players(queryset, form.cleaned_data)
queryset = annotate_player_metrics(queryset, form.cleaned_data)
queryset = apply_sorting(queryset, form.cleaned_data.get("sort", "name_asc"))
else:
queryset = annotate_player_metrics(queryset).order_by("full_name", "id")
queryset = filter_players(queryset, form.cleaned_data)
queryset = annotate_player_metrics(queryset, form.cleaned_data)
queryset = apply_sorting(queryset, form.cleaned_data.get("sort", "name_asc"))
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["search_form"] = self.get_form()
search_form = self.get_form()
context["search_form"] = search_form
context["search_has_errors"] = search_form.is_bound and bool(search_form.errors)
context["favorite_player_ids"] = set()
if self.request.user.is_authenticated:
player_ids = [player.id for player in context["players"]]