Premier League Predictor: Django & MySQL — Chapter 12, Exercise 3 ==================================================== TASK Mount the app behind a real nginx location /predictor/ block with FORCE_SCRIPT_NAME set but BASE_PATH not yet applied to any hand-written path, confirm a SeasonTeamInline save-and-continue redirect inside the admin works correctly, then confirm season_default_gameweek genuinely 404s at the wrong URL until the BASE_PATH fix from this chapter is applied to it too. SOLUTION Setup: nginx's own location /predictor/ block proxies to gunicorn on 127.0.0.1:8000, stripping the /predictor/ prefix before forwarding (per this chapter's own nginx config). Django's settings have DJANGO_BASE_PATH=/predictor set, so FORCE_SCRIPT_NAME = '/predictor' is active. season_default_gameweek is still running its original, unfixed Chapter 10 code at this point — no settings.BASE_PATH applied to it yet. 1. Visit https://example.com/predictor/admin/ and log in. This page loads correctly — Django's own admin login and every subsequent admin page is reached through the proxy without issue, since nginx is forwarding /predictor/admin/... straight through (stripped to /admin/...) and Django, seeing FORCE_SCRIPT_NAME='/predictor', correctly prefixes every link it renders on the page. 2. Open a real Season in the admin and edit its SeasonTeamInline — add or remove a team, then click "Save and continue editing." The admin's own save-and-continue redirect (built internally via reverse()) correctly sends the browser back to https://example.com/predictor/admin/predictor/season/1/change/ — the real edit page, with the /predictor/ prefix intact — and the page loads successfully with the just-made change visible. This confirms FORCE_SCRIPT_NAME alone is already sufficient for every admin-generated link, exactly as this chapter's own finding-box describes. 3. Now trigger season_default_gameweek — for example by using the season selector dropdown from Chapter 10, which calls /predictor/seasons/1/default-gameweek/. nginx correctly proxies this through to Django (stripped to /seasons/1/default-gameweek/), and the still-unfixed view runs its original code: return redirect(f'/gameweeks/{first_gameweek.id}/fixtures/') — producing a real HTTP 302 response with Location: /gameweeks/7/fixtures/ (no /predictor/ prefix at all). 4. The browser follows that redirect and requests https://example.com/gameweeks/7/fixtures/. nginx has no location block matching /gameweeks/..., only / and /predictor/, so this request falls through to the location / block — Astro's own static site — and returns Astro's real 404 page instead of the fixture- entry page, confirming the redirect genuinely breaks even though FORCE_SCRIPT_NAME is fully configured and already working correctly for the admin in step 2. 5. Apply this chapter's own fix — changing the redirect() line to return redirect(f'{settings.BASE_PATH}/gameweeks/{first_gameweek.id}/fixtures/') — and repeat step 3. The Location header now reads /predictor/gameweeks/7/fixtures/, the browser requests the correct prefixed URL, nginx's own location /predictor/ block matches it, and the real fixture-entry page for gameweek 7 loads successfully. WHY THIS WORKS AS AN ANSWER ---------------------------- It runs the real, deliberately incomplete configuration described in the task (FORCE_SCRIPT_NAME set, BASE_PATH not yet applied to hand-written code) and shows the admin working correctly while season_default_gameweek genuinely fails with a real, observable wrong URL and a real 404 from the wrong site, then confirms applying this chapter's own fix specifically resolves that one remaining case — demonstrating the two-mechanism split the chapter describes is real, not just a claim.