skip to content

In Django, what does django.urls.resolve() return for a path, and what can its ResolverMatch tell you?

level: middleimportance: should knowfreq 36%

answer

  1. the handler's own lookup, without a request
  2. raises instead of returning None
  3. route template joined across includes
  4. func.view_class for class-based views

basics

~20 s

resolve() matches a path against the URLconf and returns a ResolverMatch naming the view, its args and kwargs, url_name, view_name, namespaces and the route template; an unmatched path raises Resolver404. Django stores the same object on request.resolver_match.

solid answer

~30 s

`resolve(path, urlconf=None)` runs the URL matching the handler runs, without a request. It returns a `ResolverMatch` with `func`, `args` and `kwargs` (split into `captured_kwargs` and `extra_kwargs`), `url_name`, `view_name` including the namespace, `app_name`, `namespace`, the `route` template joined across includes, and `tried`. If nothing matches it raises `Resolver404`, a subclass of `Http404`, never `None`. The handler stores the same object as `request.resolver_match` before the view runs. Typical uses: routing tests (`resolve('/shop/7/').func.view_class`), marking the active nav item by `url_name`, and using `route` as a low-cardinality label in logs and metrics.

go deeper

for a junior

Recall that resolve() turns a path into the view that would serve it, and that request.resolver_match holds the same information inside a view.

for a middle

Explain the ResolverMatch attributes, the Resolver404 error, func.view_class for class-based views, and when resolver_match is populated.

for a senior

Use it well in production: route templates as metric labels, routing tests that pin views, and explicit urlconf= when a project serves several URLconfs.

for a principal

Consider route templates as the stable identity of an endpoint across logs, metrics and tests, and what that means when URL layouts change.

## What `resolve()` does `django.urls.resolve(path, urlconf=None)` runs the same matching the request handler runs, without a request. Given a path such as `"/shop/7/"`, it walks the URLconf and returns a **`ResolverMatch`** describing the view that would serve it. When nothing matches, it raises **`Resolver404`**, a subclass of `Http404`, so an unmatched path in a view that calls `resolve()` naturally becomes a 404. It never returns `None`. Pass a path, not a full URL: no scheme, no host, no query string. `"/shop/7/?ref=mail"` fails to match a route ending in `<int:pk>/`, because the query string is not part of what URL patterns see. The handler does exactly this on every request, against `request.path_info`, and stores the result as **`request.resolver_match`**. It is set after the request phase of middleware and before `process_view()` and the view run, so it is `None` in code that runs before resolution. ## What a `ResolverMatch` carries The examples below assume `shop/urls.py` sets `app_name = "shop"`, names its detail pattern `"detail"`, and is mounted with `include()` at `shop/`. | Attribute | Holds | Example for `/shop/7/` | |---|---|---| | `func` | the view callable (for a class-based view, the function `as_view()` returned) | `OrderDetail.as_view()` result | | `args`, `kwargs` | what the view will be called with | `()`, `{"pk": 7}` | | `captured_kwargs` | only the values captured from the path | `{"pk": 7}` | | `extra_kwargs` | only the extra `kwargs=` given to `path()` | `{}` | | `url_name` | the pattern's `name=` | `"detail"` | | `route` | the route template, joined across includes | `"shop/<int:pk>/"` | | `app_name` / `namespace` | application and instance namespace | `"shop"` | | `view_name` | namespaced name, or the view's dotted path if unnamed | `"shop:detail"` | | `tried` | the patterns tried before the match | a list of pattern lists | A `ResolverMatch` also unpacks as a triple: `func, args, kwargs = resolve("/shop/7/")`. ## Where it earns its keep 1. **Routing tests.** Assert that a path is wired to the intended view without calling it. For a class-based view, `func` is the wrapper function, and the class sits on it as `func.view_class`: ```python from django.test import SimpleTestCase from django.urls import resolve from shop.views import OrderDetail class ShopRoutingTests(SimpleTestCase): def test_detail_route(self): match = resolve("/shop/7/") self.assertIs(match.func.view_class, OrderDetail) self.assertEqual(match.kwargs, {"pk": 7}) self.assertEqual(match.view_name, "shop:detail") ``` 2. **Active navigation.** A template or context processor can compare `request.resolver_match.url_name` or `view_name` with a menu entry, instead of string-matching `request.path`, which breaks as soon as a URL prefix changes. 3. **Low-cardinality labels.** For request logs and metrics, `route` groups `/shop/7/` and `/shop/8/` under one label, `shop/<int:pk>/`, where the raw path would create one series per object. 4. **Checking a path before redirecting to it.** `resolve()` can confirm that a same-site path maps to a real view, catching `Resolver404` to fall back to a default page. ## Edges worth knowing - **Unnamed patterns.** `url_name` is `None` when the pattern has no `name=`; `view_name` then falls back to the view's dotted Python path. - **Which URLconf.** Without `urlconf=`, `resolve()` uses the URLconf active for the current thread: the request's `request.urlconf` during a request that set one, otherwise `ROOT_URLCONF`. Pass `urlconf=` explicitly outside a request when the project serves more than one. - **It is not a permission check.** A successful `resolve()` says a view exists, not that the current user may see it. - **Not the reverse direction.** Turning a name back into a path is `reverse()`'s job, with its own `NoReverseMatch` error. - **Converted values.** `kwargs` holds what the converters produced: `<int:pk>` yields the integer `7`, not the string `"7"`, which matters when a test compares them. - **Extra kwargs win.** When a `kwargs=` entry on `path()` has the same name as a captured value, the extra value is what the view receives; `captured_kwargs` still shows what the URL contained. - **`tried` is the debug trail.** It is the same list the technical 404 page prints, useful when a test needs to explain why a path matched an earlier pattern than expected. ## A mental model Think of `resolve()` and `request.resolver_match` as one fact seen from two sides: outside a request you ask the URLconf the question yourself, inside a request Django has already asked it and left the answer on the request. Both answer "which view, with which arguments, under which name and route", and neither says anything about whether the view will succeed.

  • When is request.resolver_match available in Django, and when is it None?
    The handler resolves the URL after middleware's request phase and assigns the result to `request.resolver_match` before `process_view()` hooks and the view run. So views, templates, `process_view()` and the response phase see it; code in a middleware's `__call__` before it calls `get_response` sees `None`.
  • Why is ResolverMatch.route better than request.path as a metrics label in Django?
    `route` is the pattern template, such as `shop/<int:pk>/`, joined across includes, so every product page shares one label. The raw path creates a new label value per object, which bloats metrics storage and makes per-endpoint latency impossible to read.

saying these in an interview costs you the question

  • resolve() returns None when the path does not match.
  • ResolverMatch.func is the class itself for a class-based view.
  • ResolverMatch.route is the concrete path that was requested.
  • A full URL with a query string can be passed to resolve().
  • request.resolver_match is already set when a middleware's __call__ starts.