From d2eced9a50455715d97e7cd3ad5a8fc56605c8fe Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Thu, 20 Aug 2026 22:47:23 +0200 Subject: [PATCH] Fix RefereeLevel.clean() rejecting a valid inherits_from on create club_id is still None mid-validation for a brand-new level -- creation assigns the club in form_valid(), after is_valid() already ran clean(). validate_club_scope only makes sense once club_id is actually set; skip it on create, where the form's own already-club-scoped inherits_from queryset is what prevents a cross-club pick anyway. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9 --- management/tests.py | 13 +++++++++++++ teams/models.py | 10 +++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/management/tests.py b/management/tests.py index 9e32661..0e0873f 100644 --- a/management/tests.py +++ b/management/tests.py @@ -2335,6 +2335,19 @@ class MemberRefereeEligibilityTests(ManagementTestBase): self.assertContains(response, "Ref Eree") + def test_team_page_lists_a_referee_eligible_via_inheritance(self): + # self.level ("Regional") already qualifies for self.team; a higher + # level that inherits from it (with no teams of its own) should still + # show up here -- see RefereeLevel.eligible_team_ids. + national = RefereeLevel.objects.create(club=self.club, name="National", inherits_from=self.level) + national_ref = Member.objects.create(first_name="Nat", last_name="Ional") + RefereeProfile.objects.create(member=national_ref, level=national, valid_until=self.future_date) + self.client.force_login(self.admin_user) + + response = self.club_get("team_detail", self.team.pk) + + self.assertContains(response, "Nat Ional") + def test_team_page_excludes_an_expired_referee(self): # "Ref Eree" alone also matches the (unrelated) add-player/assign-staff # dropdowns, which list every active club member regardless of referee diff --git a/teams/models.py b/teams/models.py index 4e35f15..54ca91a 100644 --- a/teams/models.py +++ b/teams/models.py @@ -160,7 +160,15 @@ class RefereeLevel(ClubScopedModel): return self.name def clean(self): - validate_club_scope(self, self.club_id, same_club_fields=("inherits_from",)) + # club_id is still None here for a brand-new level: RefereeLevelCreateView + # (like its siblings -- PositionCreateView, GroupCreateView, ...) assigns + # form.instance.club in form_valid(), *after* the form's is_valid() already + # ran clean() -- so there's nothing to compare against yet on create. The + # form's own inherits_from queryset (scoped to `club`) is what actually + # blocks a cross-club pick there; this still catches it on every other path + # (update, admin, direct .full_clean()) where club_id is already set. + if self.club_id is not None: + validate_club_scope(self, self.club_id, same_club_fields=("inherits_from",)) current = self.inherits_from seen = set() while current is not None: