Django Admin as the Real Manual-Entry Tool for Teams & Fixtures
Premier League Predictor: Django & MySQL
Chapter 3 · Django Admin as the Real Manual-Entry Tool for Teams & Fixtures
Chapter 2 built the schema; this chapter turns Team, Season, SeasonTeam, Gameweek, and Fixture into real, working admin tooling — the actual real work happening for a project this admin-entry-heavy.
Team Admin: Search, Not Scroll
search_fields does two real jobs at once: it powers the admin's own search box on TeamAdmin's list page, and — as covered below — it's the exact thing that lets other admin screens offer a real, searchable autocomplete for picking an existing team instead of a plain dropdown.
SeasonTeam as an Inline: A Real Django-Specific Payoff
plpredict-fastapi1's own Chapter 3 had to hand-write real POST/DELETE routes just to add or remove a team from a season. Django's admin gets the identical job largely for free, via a TabularInline nested directly on the Season edit page:
Opening a Season in the admin now shows every one of its current SeasonTeam rows as editable table rows right there on the page, with one blank row ready for the next addition — adding or removing a team is a normal save on the Season page itself, not a separate screen.
autocomplete_fields = ('team',) turns the inline's plain foreign-key dropdown into a real, searchable, type-ahead widget — genuinely necessary once Team holds dozens of historical clubs, not just this season's 20. This isn't automatic in the abstract: Django's own admin requires the target model's admin (TeamAdmin) to define search_fields before autocomplete_fields can use it anywhere else — exactly why TeamAdmin's own search_fields, defined above for its own list page, turns out to matter well beyond that one page.
max_num = 20 stops a 21st SeasonTeam row from ever being saved. Checked directly against Django's own documented formset behavior: max_num by itself only limits how many extra, blank forms the inline displays for adding new rows — it does not, on its own, reject a submission that somehow includes more rows than that. Real enforcement on save requires the separate validate_max = True flag above, mirroring the identical validate_max option Django's own formset factories expose. Without it, this "cap" is cosmetic — the same underlying lesson plpredict-fastapi1's own Chapter 3 delivered with an explicit COUNT() query before every insert, arrived at here from the opposite direction: a built-in-looking limit that turns out to need one more explicit setting before it's actually real.
Fixtures via the Admin: Real, Working, and Genuinely Slower
Fixture gets the same inline treatment, nested on Gameweek instead:
Fixture's two foreign keys to Team don't need Chapter 2's own related_name disambiguation here — that clash only ever concerned the reverse accessor on Team, and this inline is nested under Gameweek, the model with exactly one foreign key pointing at it. Each row simply renders two separate autocomplete widgets, one per team.
Where This Course Is Headed
The fast click-to-pair fixture-entry UI, built outside the admin, creating real Fixture rows against this chapter's own schema and reusing Team records exactly as they already exist (Chapter 4); recording predictions (Chapter 5); entering results (Chapter 6); both league tables (Chapters 7-8); and promotion/relegation, which reuses this chapter's own SeasonTeamInline directly at the season boundary (Chapter 9).
Hands-On Exercises
Explain why autocomplete_fields = ('team',) on SeasonTeamInline requires TeamAdmin to define search_fields, and what would happen if TeamAdmin's search_fields were removed.
📄 View solutionExplain, precisely, what max_num = 20 alone actually controls on SeasonTeamInline, and what validate_max = True adds that max_num by itself does not provide.
📄 View solutionRegister the admin classes from this chapter, create a superuser (python manage.py createsuperuser), open a real Season in the admin, add teams via the SeasonTeamInline until you reach the 20-team cap, and confirm attempting to add a 21st is genuinely rejected on save, not just visually limited.
📄 View solutionChapter 3 Quick Reference
- TeamAdmin — search_fields powers both its own list-page search box and every autocomplete_fields reference to Team elsewhere in the admin
- SeasonTeamInline — a real TabularInline on SeasonAdmin, getting the "add/remove a team from a season" job largely for free compared to the FastAPI sibling's own hand-written routes
- Verified gotcha — max_num = 20 alone only limits displayed blank rows; validate_max = True is what actually enforces the cap on save
- FixtureInline — a real, working way to enter a gameweek's fixtures through the admin, genuinely slower than what Chapter 4 builds next
- No fk_name needed — FixtureInline is nested under Gameweek, which has only one foreign key pointing at it; the related_name clash from Chapter 2 was specific to Team
- Next chapter: The fast click-to-pair fixture-entry UI, built outside the admin