team/season aren't TeamMembershipForm fields (the view sets them from the URL, not user input), so Django's own validate_unique() silently excludes both of them -- and with them, the whole unique_jersey_number_per_team_per_season constraint. A clashing jersey number reached the database unrejected and came back as a raw IntegrityError. form.clean() now checks it by hand, same as PositionForm already does for its own check constraint; the four roster/staff save paths also get a try/except IntegrityError backstop so any future gap degrades to an error banner instead of a 500.
12 KiB
12 KiB