URL Patterns & Namespaces

Difficulty: Beginner

Django's URL dispatcher maps URL patterns to view functions or classes. The URL configuration is defined in Python modules conventionally named urls.py. Each Django project has a root URL configuration (specified by ROOT_URLCONF in settings.py), and each app can have its own urls.py that is included from the root.

URL patterns are defined using the path() function, which takes a route string, a view, and an optional name. The route string uses angle brackets for URL parameters: <int:pk> captures an integer and passes it to the view as the 'pk' keyword argument. Path converters include 'int' (integer), 'str' (non-empty string excluding /), 'slug' (slug string), 'uuid' (UUID), and 'path' (non-empty string including /). You can also create custom path converters for specialized URL formats.

The include() function is used to reference other URL configurations, enabling modular URL routing. The root urls.py typically includes app-level URL files, creating a hierarchical URL structure. This keeps URL patterns organized and prevents the root configuration from becoming unwieldy.

URL namespacing is critical for large projects. The app_name variable in an app's urls.py sets the application namespace. When you reverse a URL, you prefix it with the namespace: reverse('blog:article-detail', kwargs={'pk': 1}). In templates, you use the {% url %} tag: {% url 'blog:article-detail' pk=article.pk %}. Namespacing prevents name collisions when different apps use the same URL names (like 'detail' or 'list').

The reverse() function converts a URL name back to a URL string. This is the counterpart to the URL dispatcher - while the dispatcher maps URLs to views, reverse() maps view names to URLs. Use reverse() in views and get_absolute_url() model methods. Use the {% url %} template tag in templates. Never hardcode URLs in your code; always use named URLs and reverse them.

For complex URL patterns that cannot be expressed with path(), Django provides re_path() which accepts regular expressions. However, path() with converters handles the vast majority of cases, and regular expressions are only needed for unusual URL formats.

The order of URL patterns matters. Django processes them top to bottom and uses the first match. Place more specific patterns before more general ones. For example, put path('articles/create/', ...) before path('articles/<slug:slug>/', ...) to avoid 'create' being captured as a slug.

Code examples

Root and App URL Configuration

# project/urls.py (root)
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include('blog.urls')),
    path('shop/', include('shop.urls')),
    path('api/', include('api.urls')),
]

# blog/urls.py (app)
from django.urls import path
from . import views

app_name = 'blog'
urlpatterns = [
    path('', views.post_list, name='post-list'),
    path('<int:pk>/', views.post_detail, name='post-detail'),
    path('<int:pk>/edit/', views.post_edit, name='post-edit'),
    path('create/', views.post_create, name='post-create'),
    path('category/<slug:slug>/', views.category_detail, name='category-detail'),
]

The root urls.py delegates to app-level URL files using include(). The app_name sets the namespace. URL parameters use angle brackets with type converters: <int:pk> captures integers, <slug:slug> captures slug strings.

URL Parameter Types

from django.urls import path
from . import views

urlpatterns = [
    # int: matches positive integers
    path('articles/<int:pk>/', views.article_detail),

    # str: matches any non-empty string (excluding /)
    path('users/<str:username>/', views.user_profile),

    # slug: matches slug strings (letters, numbers, hyphens, underscores)
    path('categories/<slug:slug>/', views.category_detail),

    # uuid: matches UUID strings
    path('orders/<uuid:order_id>/', views.order_detail),

    # path: matches any non-empty string (including /)
    path('docs/<path:doc_path>/', views.doc_page),

    # Multiple parameters
    path('<int:year>/<int:month>/<slug:slug>/',
         views.article_archive),
]

Path converters validate and convert URL segments. <int:pk> only matches digits and passes an integer to the view. <slug:slug> matches URL-safe strings. Multiple parameters can be combined in one pattern.

Reversing URLs

from django.urls import reverse
from django.shortcuts import redirect

# In a view function
def some_view(request):
    # Reverse a named URL
    url = reverse('blog:post-list')
    # '/blog/'

    # Reverse with keyword arguments
    url = reverse('blog:post-detail', kwargs={'pk': 42})
    # '/blog/42/'

    # Reverse with args (positional)
    url = reverse('blog:post-detail', args=[42])
    # '/blog/42/'

    # Redirect using named URL
    return redirect('blog:post-detail', pk=42)

# In a model (get_absolute_url)
class Article(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)

    def get_absolute_url(self):
        return reverse('blog:post-detail', kwargs={'pk': self.pk})

# In templates:
# <a href="{% url 'blog:post-detail' pk=article.pk %}">{{ article.title }}</a>
# <a href="{{ article.get_absolute_url }}">{{ article.title }}</a>

reverse() converts a named URL to a URL string. redirect() can accept a named URL pattern directly. get_absolute_url() is a model convention that returns the canonical URL for an instance. Templates use the {% url %} tag.

URL Pattern Ordering

from django.urls import path
from . import views

app_name = 'blog'

# CORRECT ordering - specific patterns first
urlpatterns = [
    path('articles/create/', views.article_create, name='article-create'),
    path('articles/featured/', views.featured_articles, name='featured'),
    path('articles/<slug:slug>/', views.article_detail, name='article-detail'),
    path('articles/', views.article_list, name='article-list'),
]

# WRONG ordering - 'create' would be captured by <slug:slug>
# urlpatterns = [
#     path('articles/<slug:slug>/', views.article_detail),  # 'create' matches <slug:slug>!
#     path('articles/create/', views.article_create),        # Never reached
# ]

URL patterns are matched top to bottom. Place specific literal patterns (like 'create/' and 'featured/') before parameterized patterns (like '<slug:slug>/') to ensure correct routing.

Key points

Concepts covered

path, re_path, include, app_name, reverse, URL Parameters