skip to content

In Django, what can redirect() take after a job application is submitted, and how does it decide whether a string is a view name?

level: middleimportance: should knowfreq 42%

answer

  1. three kinds of target
  2. objects with a canonical URL
  3. try reversing first
  4. what makes a string look like a URL
  5. a misspelled dotted name

basics

~20 s

redirect() takes a model instance (via get_absolute_url()), a view name with arguments (reversed), or a URL. Strings are reversed first; on failure, one containing '/' or '.' is used as a URL, otherwise NoReverseMatch propagates.

solid answer

~40 s

`redirect()` builds its target with `resolve_url()`. An object with `get_absolute_url()`, such as the saved `JobApplication`, uses that method, and extra arguments are ignored. A lazy string is converted first, and a string starting with `./` or `../` is returned as a relative URL. Anything else is passed to `reverse()` with the positional and keyword arguments, so `redirect('application-detail', pk=application.pk)` works. If reversing fails and the target is a callable, or a string with neither `/` nor `.`, `NoReverseMatch` is raised; a string with a slash or dot is assumed to be a URL and used verbatim. That fallback hides typos: `redirect('jobs.application_done')` sends users to a relative path instead of failing. The response is a temporary redirect unless `permanent=True`. Never pass unvalidated user input such as `?next=` straight to `redirect()`.

go deeper

for a junior

Recall that redirect() accepts a model with get_absolute_url(), a route name with arguments, or a URL, and that it is how a view answers a successful POST.

for a middle

Explain resolve_url()'s order: object, lazy value, relative URL, reverse(), then the slash-or-dot fallback that decides whether a failure raises or passes through.

for a senior

Catch the silent fallback for dotted strings, keep query strings out of redirect() kwargs, and never redirect to unvalidated user-supplied targets.

for a principal

Standardise redirects on route names and get_absolute_url() so URL changes stay in URLconfs and models, not scattered through view code.

## Post, then redirect After a candidate submits a job application, the view should answer the POST with a **redirect** rather than rendering a page, so refreshing the browser does not resubmit the form. `django.shortcuts.redirect()` is the shortcut for that response. Its interesting part is how it turns its first argument into a URL. ## What redirect() accepts ```python from django.shortcuts import redirect redirect(application) # model instance redirect('application-detail', pk=application.pk) # route name plus arguments redirect('/jobs/') # absolute path redirect('https://careers.example.com/thanks/') # full URL redirect('../') # relative URL ``` The target is resolved by `django.shortcuts.resolve_url()` in this order: 1. **An object with `get_absolute_url()`**: that method's return value is used, and any extra arguments are ignored. 2. **A lazy string**, such as one from `reverse_lazy()`: converted to a normal string first. 3. **A string starting with `./` or `../`**: returned unchanged as a relative URL. 4. **Everything else** is passed to `reverse()` together with the positional and keyword arguments. 5. **If reversing fails**: a callable, or a string containing neither `/` nor `.`, re-raises `NoReverseMatch`; any other string is assumed to be a URL and used as is. ## Consequences worth knowing | Call | Outcome | |---|---| | `redirect('application-detail', pk=17)` | reversed to `/applications/17/` | | `redirect('application-detial', pk=17)` (typo) | `NoReverseMatch`, since the string has no `/` or `.` | | `redirect('jobs.application_done')` (not a route name) | used as a relative URL `jobs.application_done`, and the browser gets a 404 | | `redirect(application, pk=99)` | `get_absolute_url()` wins and `pk=99` is ignored | | `redirect('job-list', page=2)` | `page` is a URL keyword; fails unless the route has a `page` parameter | The third row is the classic trap. Old Django projects reversed by dotted view paths; a leftover string like that no longer reverses, but because it contains a dot, `redirect()` quietly treats it as a URL instead of raising. ## Arguments are URL arguments Every extra positional or keyword argument goes to `reverse()` as a route parameter. `redirect()`'s own options are keyword-only parameters such as `permanent`, `preserve_request` and `max_length`; there is no `query`, `fragment` or `current_app`. For anything more, build the string first: ```python from django.urls import reverse url = reverse('application-detail', kwargs={'pk': application.pk}, fragment='status') return redirect(url) ``` ## Status and safety - **Temporary by default.** `redirect()` returns a temporary redirect; `permanent=True` switches to a permanent one. After a form submission, temporary is what you want. - **Do not redirect to raw user input.** `redirect(request.GET['next'])` lets an attacker send users to any site. Validate such targets with Django's host checks before redirecting. - **Prefer names and objects over literal paths.** They follow URLconf changes, and a wrong name fails loudly with `NoReverseMatch` instead of producing a broken link. ## redirect() or HttpResponseRedirect? | Approach | Resolves names | Accepts objects | Typical use | |---|---|---|---| | `redirect('application-detail', pk=17)` | yes | yes | views answering a form POST | | `HttpResponseRedirect(reverse('application-detail', kwargs={'pk': 17}))` | only through the explicit `reverse()` | no | when you want no fallback guessing | | `HttpResponseRedirect('/applications/17/')` | no | no | fixed external or legacy targets | `redirect()` is a convenience: it saves the explicit `reverse()` call and accepts model instances. The explicit form is sometimes preferred in code reviews precisely because it cannot silently treat a mistyped dotted name as a URL: `reverse()` either returns a path or raises. ## A complete job-application view ```python from django.shortcuts import get_object_or_404, redirect, render from .forms import ApplicationForm from .models import Job def job_apply(request, pk): job = get_object_or_404(Job, pk=pk) form = ApplicationForm(request.POST or None, request.FILES or None) if request.method == 'POST' and form.is_valid(): application = form.save(commit=False) application.job = job application.save() return redirect(application) # uses JobApplication.get_absolute_url() return render(request, 'jobs/apply.html', {'form': form, 'job': job}) ``` Passing the saved object keeps the view short and puts the application's canonical URL in one place, the model's `get_absolute_url()`.

  • Why does Django's redirect('jobs.application_done') not raise an error when no route has that name?
    `resolve_url()` first tries `reverse()`. When that fails, it re-raises only for callables or strings with neither `/` nor `.`. This string contains a dot, so Django assumes it is a URL and returns it, and the browser follows a relative path that 404s. Use route names without dots, or test that redirects land on real pages.
  • What does Django's redirect(application) need from the model?
    A `get_absolute_url()` method, which `redirect()` calls to get the target. It usually returns `reverse('application-detail', kwargs={'pk': self.pk})`. Without the method, the instance is passed to `reverse()`, which fails with `NoReverseMatch`.

saying these in an interview costs you the question

  • redirect() only accepts full URLs, never route names.
  • Extra keyword arguments to redirect() are appended as query-string parameters.
  • A string redirect() cannot reverse always raises NoReverseMatch.
  • redirect(obj, pk=5) reverses using pk=5 instead of get_absolute_url().
  • redirect() returns a permanent redirect unless told otherwise.