Remove RefereeLevel.ordering now inherits_from covers the same need
A separate manually-kept number for "which tier is higher" is redundant now that the inheritance chain already expresses it, and risked drifting out of sync with it. Levels list/sort by name only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
@@ -56,7 +56,7 @@ class GroupForm(forms.ModelForm):
|
|||||||
class RefereeLevelForm(forms.ModelForm):
|
class RefereeLevelForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RefereeLevel
|
model = RefereeLevel
|
||||||
fields = ["name", "ordering", "teams", "inherits_from"]
|
fields = ["name", "teams", "inherits_from"]
|
||||||
widgets = {"teams": forms.SelectMultiple(attrs={"data-searchable": "true", "data-search-placeholder": _("Type a team to search...")})}
|
widgets = {"teams": forms.SelectMultiple(attrs={"data-searchable": "true", "data-search-placeholder": _("Type a team to search...")})}
|
||||||
|
|
||||||
def __init__(self, *args, club=None, **kwargs):
|
def __init__(self, *args, club=None, **kwargs):
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Ordering" %}</th>
|
|
||||||
<th>{% trans "Name" %}</th>
|
<th>{% trans "Name" %}</th>
|
||||||
<th>{% trans "Inherits from" %}</th>
|
<th>{% trans "Inherits from" %}</th>
|
||||||
<th>{% trans "Qualifies for" %}</th>
|
<th>{% trans "Qualifies for" %}</th>
|
||||||
@@ -25,7 +24,6 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for level in levels %}
|
{% for level in levels %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="font-mono text-muted">{{ level.ordering }}</td>
|
|
||||||
<td class="font-semibold text-ink">{{ level.name }}</td>
|
<td class="font-semibold text-ink">{{ level.name }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if level.inherits_from %}
|
{% if level.inherits_from %}
|
||||||
@@ -52,7 +50,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5" class="text-center text-muted">{% trans "No referee levels yet." %}</td>
|
<td colspan="4" class="text-center text-muted">{% trans "No referee levels yet." %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -1288,14 +1288,14 @@ class RefereeLevelManagementTests(ManagementTestBase):
|
|||||||
def test_create_is_admin_only(self):
|
def test_create_is_admin_only(self):
|
||||||
self.client.force_login(self.make_non_admin_coach())
|
self.client.force_login(self.make_non_admin_coach())
|
||||||
|
|
||||||
response = self.club_post("referee_level_create", {"name": "Regional", "ordering": 0, "teams": []})
|
response = self.club_post("referee_level_create", {"name": "Regional", "teams": []})
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
def test_admin_can_create_a_level_with_teams(self):
|
def test_admin_can_create_a_level_with_teams(self):
|
||||||
self.client.force_login(self.admin_user)
|
self.client.force_login(self.admin_user)
|
||||||
|
|
||||||
response = self.club_post("referee_level_create", {"name": "Regional", "ordering": 0, "teams": [str(self.team.pk), str(self.other_team.pk)]})
|
response = self.club_post("referee_level_create", {"name": "Regional", "teams": [str(self.team.pk), str(self.other_team.pk)]})
|
||||||
|
|
||||||
level = RefereeLevel.objects.get(club=self.club, name="Regional")
|
level = RefereeLevel.objects.get(club=self.club, name="Regional")
|
||||||
self.assertRedirects(response, reverse("management:referee_level_list"))
|
self.assertRedirects(response, reverse("management:referee_level_list"))
|
||||||
@@ -1306,7 +1306,7 @@ class RefereeLevelManagementTests(ManagementTestBase):
|
|||||||
level.teams.add(self.team)
|
level.teams.add(self.team)
|
||||||
self.client.force_login(self.admin_user)
|
self.client.force_login(self.admin_user)
|
||||||
|
|
||||||
self.club_post("referee_level_update", {"name": "Regional", "ordering": 0, "teams": [str(self.other_team.pk)]}, level.pk)
|
self.club_post("referee_level_update", {"name": "Regional", "teams": [str(self.other_team.pk)]}, level.pk)
|
||||||
|
|
||||||
self.assertEqual(set(level.teams.all()), {self.other_team})
|
self.assertEqual(set(level.teams.all()), {self.other_team})
|
||||||
|
|
||||||
@@ -1324,7 +1324,7 @@ class RefereeLevelManagementTests(ManagementTestBase):
|
|||||||
regional.teams.add(self.team)
|
regional.teams.add(self.team)
|
||||||
self.client.force_login(self.admin_user)
|
self.client.force_login(self.admin_user)
|
||||||
|
|
||||||
response = self.club_post("referee_level_create", {"name": "National", "ordering": 0, "teams": [str(self.other_team.pk)], "inherits_from": str(regional.pk)})
|
response = self.club_post("referee_level_create", {"name": "National", "teams": [str(self.other_team.pk)], "inherits_from": str(regional.pk)})
|
||||||
|
|
||||||
national = RefereeLevel.objects.get(club=self.club, name="National")
|
national = RefereeLevel.objects.get(club=self.club, name="National")
|
||||||
self.assertRedirects(response, reverse("management:referee_level_list"))
|
self.assertRedirects(response, reverse("management:referee_level_list"))
|
||||||
@@ -1335,7 +1335,7 @@ class RefereeLevelManagementTests(ManagementTestBase):
|
|||||||
level = RefereeLevel.objects.create(club=self.club, name="Regional")
|
level = RefereeLevel.objects.create(club=self.club, name="Regional")
|
||||||
self.client.force_login(self.admin_user)
|
self.client.force_login(self.admin_user)
|
||||||
|
|
||||||
self.club_post("referee_level_update", {"name": "Regional", "ordering": 0, "teams": [], "inherits_from": str(level.pk)}, level.pk)
|
self.club_post("referee_level_update", {"name": "Regional", "teams": [], "inherits_from": str(level.pk)}, level.pk)
|
||||||
|
|
||||||
level.refresh_from_db()
|
level.refresh_from_db()
|
||||||
self.assertIsNone(level.inherits_from)
|
self.assertIsNone(level.inherits_from)
|
||||||
|
|||||||
@@ -72,11 +72,11 @@ class StaffAssignmentAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(RefereeLevel)
|
@admin.register(RefereeLevel)
|
||||||
class RefereeLevelAdmin(admin.ModelAdmin):
|
class RefereeLevelAdmin(admin.ModelAdmin):
|
||||||
list_display = ["name", "club", "ordering", "inherits_from", "team_list"]
|
list_display = ["name", "club", "inherits_from", "team_list"]
|
||||||
list_filter = ["club"]
|
list_filter = ["club"]
|
||||||
search_fields = ["name"]
|
search_fields = ["name"]
|
||||||
autocomplete_fields = ["teams", "inherits_from"]
|
autocomplete_fields = ["teams", "inherits_from"]
|
||||||
ordering = ["club", "ordering", "name"]
|
ordering = ["club", "name"]
|
||||||
|
|
||||||
@admin.display(description=_("teams"))
|
@admin.display(description=_("teams"))
|
||||||
def team_list(self, obj):
|
def team_list(self, obj):
|
||||||
|
|||||||
21
teams/migrations/0012_alter_refereelevel_options_and_more.py
Normal file
21
teams/migrations/0012_alter_refereelevel_options_and_more.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 6.0.6 on 2026-08-20 21:02
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('teams', '0011_refereelevel_inherits_from'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='refereelevel',
|
||||||
|
options={'ordering': ['name'], 'verbose_name': 'referee level', 'verbose_name_plural': 'referee levels'},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='refereelevel',
|
||||||
|
name='ordering',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -121,22 +121,23 @@ class TeamMembership(UUIDModel):
|
|||||||
|
|
||||||
class RefereeLevel(ClubScopedModel):
|
class RefereeLevel(ClubScopedModel):
|
||||||
"""A club-defined referee qualification tier (e.g. "Regional", "National")
|
"""A club-defined referee qualification tier (e.g. "Regional", "National")
|
||||||
-- admin-managed, like Position, so a club can name and reorder its own
|
-- admin-managed, like Position, so a club can name its own levels rather
|
||||||
levels rather than picking from a fixed list. Owns which teams it
|
than picking from a fixed list. Owns which teams it qualifies a referee
|
||||||
qualifies a referee for: eligibility is a property of the *level*, not of
|
for: eligibility is a property of the *level*, not of the individual
|
||||||
the individual referee -- a club typically has a handful of levels, each
|
referee -- a club typically has a handful of levels, each unlocking a
|
||||||
unlocking a tier of teams/competitions, rather than hand-picking teams per
|
tier of teams/competitions, rather than hand-picking teams per referee.
|
||||||
referee.
|
|
||||||
|
|
||||||
`inherits_from` chains levels together so a higher tier doesn't need every
|
`inherits_from` chains levels together so a higher tier doesn't need every
|
||||||
lower tier's team re-added by hand: a "National" referee is automatically
|
lower tier's team re-added by hand: a "National" referee is automatically
|
||||||
eligible for everything "Regional" (its inherits_from) covers, and so on
|
eligible for everything "Regional" (its inherits_from) covers, and so on
|
||||||
down the chain -- see eligible_team_ids, the single definition every
|
down the chain -- see eligible_team_ids, the single definition every
|
||||||
consumer (RefereeProfile.eligible_teams, events.services.referees) reads
|
consumer (RefereeProfile.eligible_teams, events.services.referees) reads
|
||||||
through."""
|
through. That chain is also what expresses which tier is "higher" now --
|
||||||
|
there used to be a manual `ordering` field for this too, dropped once
|
||||||
|
inherits_from covered the same need without a second, independently-kept
|
||||||
|
number to keep in sync with it."""
|
||||||
|
|
||||||
name = models.CharField(_("name"), max_length=255)
|
name = models.CharField(_("name"), max_length=255)
|
||||||
ordering = models.PositiveSmallIntegerField(_("ordering"), default=0, help_text=_("Lower numbers are listed first. Levels with the same number are ordered by name."))
|
|
||||||
teams = models.ManyToManyField(Team, related_name="referee_levels", blank=True, verbose_name=_("qualifies for"), help_text=_("Members holding this level can be assigned to referee these teams' home games."))
|
teams = models.ManyToManyField(Team, related_name="referee_levels", blank=True, verbose_name=_("qualifies for"), help_text=_("Members holding this level can be assigned to referee these teams' home games."))
|
||||||
inherits_from = models.ForeignKey(
|
inherits_from = models.ForeignKey(
|
||||||
"self",
|
"self",
|
||||||
@@ -151,7 +152,7 @@ class RefereeLevel(ClubScopedModel):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("referee level")
|
verbose_name = _("referee level")
|
||||||
verbose_name_plural = _("referee levels")
|
verbose_name_plural = _("referee levels")
|
||||||
ordering = ["ordering", "name"]
|
ordering = ["name"]
|
||||||
constraints = [
|
constraints = [
|
||||||
models.UniqueConstraint(fields=["club", "name"], name="unique_referee_level_name_per_club"),
|
models.UniqueConstraint(fields=["club", "name"], name="unique_referee_level_name_per_club"),
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user