Open-Source Wikis

/

Django

/

Systems

/

URL routing

django/django

URL routing

django/urls/ resolves an incoming request path to a view callable and supports the inverse — given a view name, produce the URL that maps to it. The implementation is a tree of resolvers and patterns, with named groups, path converters, and namespaces layered on top.

Purpose

Map URLs to views without coupling either side to a specific routing format. The framework supports two pattern styles — the modern path()/re_path() and the legacy url() (regex-only). Both compile to the same internal pattern tree.

Directory layout

django/urls/
├── __init__.py
├── base.py          # resolve(), reverse(), translate_url(), set_script_prefix()
├── conf.py          # path(), re_path(), include() — what users put in urls.py
├── converters.py    # path converter classes (int, str, slug, uuid, path)
├── exceptions.py    # NoReverseMatch, Resolver404
├── resolvers.py     # URLPattern, URLResolver, ResolverMatch (the engine)
└── utils.py         # get_callable, get_mod_func helpers

Key abstractions

Type File Role
URLPattern resolvers.py A single (pattern, view, name) triple
URLResolver resolvers.py A node holding a list of patterns/sub-resolvers + namespace info
RoutePattern resolvers.py The path()-style pattern matcher
RegexPattern resolvers.py The re_path()/url()-style pattern matcher
LocalePrefixPattern resolvers.py Locale-prefixed pattern wrapper for i18n
ResolverMatch resolvers.py The resolved (view, args, kwargs, url_name, app_names, namespaces) tuple
path(), re_path(), include() conf.py Public constructors users call in urls.py
IntConverter, UUIDConverter, SlugConverter, StringConverter, PathConverter converters.py Built-in path converters

How it works

graph TD
    Request["request.path_info<br/>e.g., '/articles/2025/12/'"]
    RootConf["ROOT_URLCONF<br/>(settings)"]
    RootResolver["URLResolver"]
    Walk["walk children:<br/>URLPattern / sub-URLResolver"]
    Match["ResolverMatch"]
    View["view(request, *args, **kwargs)"]

    Request --> RootResolver
    RootConf --> RootResolver
    RootResolver --> Walk
    Walk -->|match| Match
    Match --> View

Resolution

resolve(path) (in django/urls/base.py) walks the resolver tree:

  1. Start at the root URLResolver — built from settings.ROOT_URLCONF and cached.
  2. Try each pattern's match(path) method. A match returns the consumed prefix and any captured args/kwargs.
  3. If a sub-resolver matches a prefix, recurse with the remainder of the path.
  4. If a URLPattern matches the entire path, build a ResolverMatch with merged args/kwargs and return it.
  5. If nothing matches, raise Resolver404.

The resolver tree is cached after the first call. Reverse lookups consult the same tree but walk it in reverse.

Patterns

path() accepts a route string with <converter:name> placeholders. Internally it's parsed by RoutePattern into a regex — <int:year> becomes (?P<year>[0-9]+), <slug:slug> becomes (?P<slug>[-a-zA-Z0-9_]+), etc. The conversion is two-way: matched values run through to_python(); reverse calls run through to_url().

re_path() (and the legacy url()) take a raw regex. Capture groups become positional or named arguments. There's no path-converter pipeline — values come back as strings.

include() (in conf.py) splices another URLconf module's urlpatterns into the resolver tree. It also takes optional namespace and app_name arguments for namespace resolution.

Reverse lookup

reverse(viewname, args=..., kwargs=...) walks the same tree and emits a URL string. The tree is indexed by url_name, so reverse lookup is O(1) for a name plus O(depth) to assemble the path.

Namespaced names ('admin:index', 'myapp:detail') are resolved by splitting on : and traversing nested namespaces.

Path converters

Built-in converters in django/urls/converters.py:

Converter regex to_python
int [0-9]+ int(value)
str [^/]+ str (the default)
slug [-a-zA-Z0-9_]+ str
uuid [0-9a-f]{8}-... uuid.UUID(value)
path .+ str

Custom converters register via register_converter(MyConverter, "myname").

set_script_prefix

SCRIPT_NAME (the WSGI prefix) is handled by a thread-local prefix in django/urls/base.py. django.setup() calls set_script_prefix(). reverse() prepends this prefix; resolve() expects request.path_info (which excludes the prefix).

Integration points

  • Middleware chain (http) calls resolve() after the middleware's process_request phase.
  • Class-based views (django/views/generic/base.py) — View.as_view() returns a callable that the resolver matches on.
  • Namespaces — used by every contrib app and by user apps to avoid name collisions.
  • i18n (django/utils/translation/) — LocalePrefixPattern wraps the URLconf when i18n_patterns() is used.
  • Templates ({% url %} tag) and forms — both call reverse().
  • AdminAdminSite.get_urls() builds a separate URLconf attached at the admin's mount point.

Entry points for modification

  • New path converter: subclass with regex, to_python, to_url. Register via register_converter().
  • Custom resolver behaviour: subclass URLResolver or URLPattern (rare). The pattern matchers in resolvers.py are extension points.
  • URLconf reloading: django/utils/autoreload.py calls clear_url_caches() (django/urls/base.py) on every reload to invalidate the resolver tree.

Key source files

File Purpose
django/urls/resolvers.py URLPattern, URLResolver, ResolverMatch, pattern classes (~1,000 lines)
django/urls/base.py resolve, reverse, script prefix, URL caches
django/urls/conf.py path(), re_path(), include()
django/urls/converters.py Built-in path converters
django/urls/exceptions.py NoReverseMatch, Resolver404

Where to read tests

  • tests/urlpatterns/path()/re_path() matching.
  • tests/urls/ — historical legacy patterns.
  • tests/test_client_regress/ — end-to-end with reverse calls.
  • tests/i18n/patterns/i18n_patterns and locale prefixes.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

URL routing – Django wiki | Factory