Premier League Predictor: Django & MySQL — Chapter 12, Exercise 1 ==================================================== TASK Write the fixed version of season_default_gameweek using settings.BASE_PATH, then show the exact Location header value the unfixed version and the fixed version would each send to the browser when mounted at /predictor/ — confirming FORCE_SCRIPT_NAME alone changes neither one. SOLUTION The fixed view: def season_default_gameweek(request, season_id): season = get_object_or_404(Season, pk=season_id) first_gameweek = Gameweek.objects.filter(season=season).order_by('number').first() if not first_gameweek: return JsonResponse({'error': 'This season has no gameweeks yet'}, status=404) return redirect(f'{settings.BASE_PATH}/gameweeks/{first_gameweek.id}/fixtures/') Assume this app is deployed with FORCE_SCRIPT_NAME = '/predictor' set in settings.py (per this chapter), and gameweek 7 is the real first gameweek of the requested season. Unfixed version (the original Chapter 10 code): return redirect(f'/gameweeks/{first_gameweek.id}/fixtures/') This builds the literal Python string '/gameweeds/7/fixtures/' by hand and hands it directly to redirect(), which wraps it in an HttpResponseRedirect with no further processing. The response Django sends back to the browser has: Location: /gameweeks/7/fixtures/ This is unaffected by FORCE_SCRIPT_NAME, because FORCE_SCRIPT_NAME only ever influences Django's own URL-building functions (reverse(), {% url %}) — it has no mechanism for inspecting or rewriting a plain string literal that a view constructs by hand. The browser, receiving this Location header, requests https://example.com/gameweeks/7/fixtures/ — missing the /predictor prefix entirely, which nginx has no location block for, so it falls through to the Astro site's own catch-all route instead of reaching the predictor. Fixed version (using settings.BASE_PATH): return redirect(f'{settings.BASE_PATH}/gameweeks/{first_gameweek.id}/fixtures/') With BASE_PATH = '/predictor' (set via the same DJANGO_BASE_PATH environment variable that also derives FORCE_SCRIPT_NAME), this builds the string '/predictor/gameweeks/7/fixtures/' explicitly, and the response's Location header becomes: Location: /predictor/gameweeks/7/fixtures/ The browser now correctly requests https://example.com/predictor/gameweeks/7/fixtures/, which matches nginx's own location /predictor/ block and is correctly proxied through to Django. FORCE_SCRIPT_NAME's own value is identical in both cases above — the difference in the actual Location header sent to the browser comes entirely from whether the view's own code explicitly includes settings.BASE_PATH in the string it builds, confirming FORCE_SCRIPT_NAME by itself changes nothing about either version's real output. WHY THIS WORKS AS AN ANSWER ---------------------------- It writes the real fixed code, then traces the literal Location header value both the unfixed and fixed versions actually produce, showing concretely that FORCE_SCRIPT_NAME's presence or absence makes no difference to either header value — the difference comes entirely from whether BASE_PATH is explicitly included in the hand-built string.