The Fast Fixture-Entry UI Outside the Admin: Click-to-Pair Teams Into a Gameweek
Premier League Predictor: Django & MySQL
Chapter 4 · The Fast Fixture-Entry UI Outside the Admin: Click-to-Pair Teams Into a Gameweek
Chapter 3's own admin inline genuinely works, but means two searches per fixture, ten times a week. This chapter builds the real, faster alternative: 20 always-visible team buttons, clicked directly into place — the same underlying Fixture data, a purpose-built interface on top.
Rendering the Team Grid Server-Side, Not Fetched
plpredict-fastapi1's own vanilla-JS frontend always fetched the season's teams from a JSON route the moment the page loaded. Django's own template engine can do that job directly, server-side, before the page is even sent to the browser:
{% for team in teams %} writes every button directly into the HTML the server sends — by the time a browser paints this page, the 20 team buttons already exist, with no separate JavaScript request needed just to populate them. The FastAPI/vanilla-JS sibling's own loadTeams() function exists specifically because that course has no template engine standing between its data and its HTML; here, that same job is Django's own template rendering, done once, server-side, before the response is even sent.
The Real New Concern: CSRF
Adding a fixture is still a real state-changing request — this page's own JavaScript needs to POST to a Django view. That's the first genuinely new requirement this course introduces that its FastAPI sibling never had to think about at all:
plpredict-fastapi1's own fixture-entry chapter never mentions CSRF, because FastAPI has no built-in cross-site request forgery protection at all — nothing stops a POST from succeeding regardless of where it originated. Django is the opposite default: CsrfViewMiddleware, present in every fresh Django project's own MIDDLEWARE setting, rejects any POST/PUT/PATCH/DELETE request that doesn't include a valid CSRF token — including a JavaScript fetch() call, exactly like the one this chapter's own Add Fixture button needs to make.
@ensure_csrf_cookie on the view above guarantees the browser actually has a real CSRF token to send, even though this page renders no traditional HTML <form> for Django to attach one to automatically. The token then has to be read from the cookie and included on every fetch call by hand:
Creating a Fixture: A Real JSON View, With the Same Server-Side Guard
@require_POST is Django's own small, real decorator for rejecting any other HTTP method with a clean 405 — no need for a separate view per method the way a bare function view would otherwise need to branch on request.method by hand.
'X-CSRFToken': csrftoken on the fetch call, Django's own CsrfViewMiddleware rejects the request outright with a real 403 Forbidden, before create_fixture's own code ever runs — a genuinely different failure mode from the duplicate-team 409 above, and a real, common early mistake when adding AJAX to an otherwise server-rendered Django page for the first time.
usedTeamIds starts empty on every page load, seeded only by fixtures actually added during the current visit — a stale reload could show a team as available that's genuinely already fixtured. create_fixture's own server-side check, querying every real Fixture row for the gameweek, is what actually prevents a bad duplicate; the client-side disabling above is convenience, exactly the same honest distinction plpredict-fastapi1's own Chapter 4 already established.
Where This Course Is Headed
Recording all four prediction sources against each fixture this chapter creates, and the real MySQL-specific answer to the partial-unique-index gap flagged back in Chapter 1 (Chapter 5); entering results (Chapter 6); the real league table (Chapter 7); the prediction league table (Chapter 8); promotion and relegation (Chapter 9); and a real gameweek/season selector, plus styling, to replace this chapter's own single-gameweek page (Chapter 10).
Hands-On Exercises
Explain why fixture_entry never needs a JavaScript fetch call just to display the initial 20 team buttons, tracing the reason back to what Django's template engine does that plpredict-fastapi1's own vanilla-JS frontend has no equivalent for.
📄 View solutionExplain why @ensure_csrf_cookie is needed on fixture_entry even though that view renders no traditional HTML form, and what would happen to the Add Fixture button's own fetch call if the X-CSRFToken header were left out entirely.
📄 View solutionBuild the full click-to-pair page yourself against a real gameweek, enter at least 3 fixtures, then deliberately remove the X-CSRFToken header from addFixture() and confirm the real, different error you get back compared to attempting to reuse an already-fixtured team.
📄 View solutionChapter 4 Quick Reference
- Team grid — rendered server-side via {% for team in teams %}, no client-side fetch needed to populate it, unlike the FastAPI sibling
- @ensure_csrf_cookie — guarantees a CSRF token exists for a page with no traditional form to attach one to automatically
- CSRF — a genuinely new concern this course introduces; FastAPI ships none of this by default, Django's CsrfViewMiddleware rejects an unprotected POST outright
- X-CSRFToken header — required on every fetch() that changes state; missing it fails with a real 403, a different failure mode from the duplicate-team 409
- @require_POST — Django's own small decorator for a POST-only view, no manual request.method branching
- Same real authority split as the FastAPI sibling — server-side duplicate-team check is the real rule; client-side disabling is convenience
- Next chapter: Predictions — and a genuinely MySQL-specific answer to Chapter 1's own flagged gap