skip to content

After a Django job-board view saves an application, redirecting to the application page raises NoReverseMatch — how do you read the error and find the cause?

level: seniorimportance: should knowfreq 45%

answer

  1. two different message shapes
  2. the arguments printed in the message
  3. patterns tried versus the values
  4. an object not yet saved
  5. namespace missing from the name

basics

~20 s

Read the NoReverseMatch message: 'not a valid view function or pattern name' means a wrong name or namespace; 'with keyword arguments ... not found. N pattern(s) tried' means the values do not fit, such as pk=None from an unsaved object.

solid answer

~40 s

`NoReverseMatch` has three shapes. *'X' is not a valid view function or pattern name* means no route has that name in this URLconf: a typo, a missing namespace prefix such as `jobs:`, or an app URLconf that was never included. *'x' is not a registered namespace* points at the namespace part. *Reverse for 'application-detail' with keyword arguments '{'pk': None}' not found. 1 pattern(s) tried: [...]* means the name exists but no pattern accepts those values; compare the printed arguments with each pattern. Here `pk` is `None` because the view reversed before `save()`, for example after `form.save(commit=False)`, and `str(None)` cannot match `[0-9]+`. Other value causes are wrong keyword names, an empty slug, or a converter's `to_url()` raising `ValueError`. Fix the call, then add a test that submits the form and follows the redirect.

go deeper

for a junior

Recall that NoReverseMatch means Django could not build a URL for that name with those arguments, and check the name and the values first.

for a middle

Explain the message shapes, name not found versus arguments not accepted, and how converters reject values such as None or an empty slug.

for a senior

Diagnose from the printed arguments and patterns in one pass, spot unsaved objects and missing namespaces, and add POST-path and render tests that keep it out of production.

for a principal

Make reusable apps safe to mount by namespacing their names and documenting their include(), so consuming projects cannot trigger NoReverseMatch by installing them differently.

## The failing view ```python from django.shortcuts import redirect, render from .forms import ApplicationForm def job_apply(request, 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_id = pk response = redirect('application-detail', pk=application.pk) # pk is None application.save() return response return render(request, 'jobs/apply.html', {'form': form}) ``` The route is `path('applications/<int:pk>/', views.application_detail, name='application-detail')`. The error reads: *Reverse for 'application-detail' with keyword arguments '{'pk': None}' not found. 1 pattern(s) tried: ['applications/(?P<pk>[0-9]+)/\\Z']* ## Reading the three message shapes | Message | What it means | Where to look | |---|---|---| | `Reverse for 'X' not found. 'X' is not a valid view function or pattern name.` | no route carries that name in the URLconf in use | spelling, `name=` in `urls.py`, the namespace prefix, whether the app's URLconf is included | | `'jobs' is not a registered namespace` | the part before `:` is unknown | `app_name` in the included module, the `include()` call | | `Reverse for 'X' with <arguments> not found. N pattern(s) tried: [...]` | routes with that name exist, but none accepts these values | the printed arguments against each printed pattern | The third shape is the most informative: Django prints the exact values it received and the regex of every candidate pattern, so the mismatch is usually visible by eye. ## Causes behind the third shape - **An unsaved object.** `form.save(commit=False)` or a freshly constructed model instance has `pk` `None`; the converter's `to_url()` turns it into `'None'`, which fails `[0-9]+`. Reverse after `save()`. - **Wrong keyword names.** The route says `<int:pk>` but the call passes `id=` or `job_id=`; keyword names must match exactly. - **Wrong number of positional arguments.** `args=[job.pk]` against a route with two parameters. - **Values that fail the converter.** An empty slug, a UUID as an uppercase string, or text for an `int` route. - **A custom converter's `to_url()` raising `ValueError`,** which makes that pattern ineligible. ## Causes behind the first two shapes 1. **A typo or renamed route.** Search the project for the old name; templates are the usual stragglers. 2. **A missing namespace.** The app sets `app_name = 'jobs'`, so the name must be `'jobs:application-detail'`. 3. **The app's URLconf is not included** in the root URLconf, common with reusable apps whose installation steps were skipped. 4. **A different URLconf is active.** A middleware that sets `request.urlconf` for some hosts limits reversing to that URLconf's names. ## A quick triage order 1. Read the message shape first; it tells you whether to look at names or at values. 2. For names, search the codebase for the name and check `name=`, `app_name` and the `include()` lines. 3. For values, compare each printed argument with the printed regex; `None`, empty strings and wrong keyword names stand out. 4. Reproduce in a shell with `reverse()` and the same arguments to confirm the fix before touching the view. ## Fixing and preventing it ```python if request.method == 'POST' and form.is_valid(): application = form.save(commit=False) application.job_id = pk application.save() return redirect('application-detail', pk=application.pk) ``` - **Reverse only from saved state.** Build the redirect after `save()`, or reverse from the model with `redirect(application)` once `get_absolute_url()` exists. - **Test the whole POST path.** A test that submits a valid application and asserts the `Location` header catches this error in CI instead of in production. - **Render every page in tests.** `{% url %}` failures raise during rendering, unless the `as var` form is used, so rendering each template once exercises every link. - **Reusable apps should namespace their names** and document the `include()` line, so projects cannot mount them in a way that breaks their own reversing.

  • Why does Django's {% url %} tag sometimes not raise NoReverseMatch when the name is wrong?
    With the `as var` form, `{% url 'name' as the_url %}`, a failed lookup stores an empty string instead of raising, which is intended for optional links. A typo then produces a missing link silently, so use that form only when the absence is expected.
  • How can a test catch NoReverseMatch in a Django redirect before release?
    Post valid data to the view with Django's test client and assert the response status and its `Location` header. If the reverse fails, the test sees the exception instead of a redirect. Pair it with tests that render each template, since `{% url %}` errors appear only at render time.

saying these in an interview costs you the question

  • NoReverseMatch always means the URL name is misspelled.
  • reverse() skips None values and builds the URL without them.
  • An unsaved model instance already has a primary key for reversing.
  • Keyword names passed to reverse() can differ from the converter names in the route.
  • {% url %} failures are silently rendered as empty links in every form of the tag.