The migration path for a club arriving with a list of children from a federation export and no parent records. Children import without logins, each into a family of their own -- that shape *is* the "nobody is responsible for this child" state, so there's no unclaimed flag to drift out of step with reality, and a family drops off the worklist by itself the moment a parent joins it. `family_role=child` with a blank `family_group` asks for that; any other lone role is still a mistake in the file. Verification is a human decision, deliberately. A parent submits a public form with the child's name and date of birth as free text -- no search, no autocomplete, and the same response whether or not the child was found, because the page needs no login and anything that resolved the child would turn it into a way to enumerate the club's children. An admin matches it from a queue against a shortlist that only ever contains children with nobody on file, so approving can never quietly re-parent a child who already has one. The alternatives were worse. A claim code needs a delivery channel the club may not have and is a bearer token besides. Matching on name plus birthday hands out someone else's child to whoever guesses a birthday. The club is the only party that actually knows its own families. That form is also the registration: open self-registration is now closed (shadowing account_signup rather than removing the route, so the URL name allauth's templates reverse still resolves). The account is created on approval, not on submission, so a public form can't fill the user table. An approved parent lands as a guardian -- login and family link, no membership, no fee -- gets a password-reset link, and a minimal "my family" page. One bug worth recording: families_awaiting_a_parent first used annotate(Count(..., filter=...)) over a queryset already filtered on the same join, so Django reused that join for the counts and a parent with no ClubMembership of their own -- exactly what a newly linked guardian is -- went uncounted, leaving the family unclaimed forever. Exists subqueries avoid it. A test pins both directions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
34 lines
1.1 KiB
Python
34 lines
1.1 KiB
Python
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.shortcuts import redirect, render
|
|
from django.views.generic import TemplateView
|
|
|
|
|
|
class ClubHomeView(LoginRequiredMixin, TemplateView):
|
|
"""Placeholder landing page for a club subdomain — where members land after
|
|
signing in, until the club-facing site is built."""
|
|
|
|
template_name = "club/home.html"
|
|
|
|
|
|
def root(request):
|
|
"""``/`` means different things per tenant.
|
|
|
|
This is why allauth needs no login-redirect adapter: LOGIN_REDIRECT_URL is "/",
|
|
and "/" resolves itself — a club subdomain lands on the club, the base domain
|
|
hands off to the platform control panel.
|
|
"""
|
|
if request.club is None:
|
|
return redirect("controlpanel:dashboard")
|
|
|
|
return ClubHomeView.as_view()(request)
|
|
|
|
|
|
def signup_closed(request):
|
|
"""Self-registration is closed -- see rosterchief/urls.py.
|
|
|
|
Shadows allauth's own signup route rather than removing it, so the
|
|
`account_signup` URL name every allauth template reverses still resolves and
|
|
the login page doesn't 500 looking for it.
|
|
"""
|
|
return render(request, "account/signup_closed.html", status=403)
|