Skip to content

Chapter 3: URL Routing System

3.1 URL Configuration Basics

URLconf Concept

URLconf (URL configuration) is the configuration file for URL patterns in Django, defining the mapping relationship between URL paths and view functions. Django uses URLconf to decide how to handle each incoming URL request.

URL Processing Flow

User requests URL

Django checks ROOT_URLCONF setting

Load URLconf module

Check each URL pattern in urlpatterns in order

Find first matching pattern

Call corresponding view function

Return HttpResponse object

path() Function Details

Django 2.0 introduced the path() function, providing a more concise and type-safe URL configuration method.

Basic Syntax

python
from django.urls import path
from . import views

path(route, view, kwargs=None, name=None)

Parameter description:

  • route: URL pattern string
  • view: View function or class view's as_view() method
  • kwargs: Extra parameter dictionary passed to view
  • name: URL pattern name, used for reverse resolution

Basic Examples

python
# blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    # Static URLs
    path('', views.home, name='home'),
    path('about/', views.about, name='about'),
    path('contact/', views.contact, name='contact'),
    
    # URLs with parameters
    path('article/<int:id>/', views.article_detail, name='article_detail'),
    path('category/<str:name>/', views.category_detail, name='category_detail'),
    path('tag/<slug:slug>/', views.tag_detail, name='tag_detail'),
    
    # Class views
    path('articles/', views.ArticleListView.as_view(), name='article_list'),
    path('search/', views.SearchView.as_view(), name='search'),
]

Built-in Path Converters

Django provides multiple built-in path converters:

python
# 1. str - Matches non-empty strings except path separators (default)
path('category/<str:name>/', views.category_detail)
# Matches: /category/technology/
# Doesn't match: /category/ or /category/tech/sub/

# 2. int - Matches zero or positive integers
path('article/<int:id>/', views.article_detail)
# Matches: /article/123/
# Doesn't match: /article/abc/ or /article/-1/

# 3. slug - Matches slug format strings (letters, numbers, hyphens, underscores)
path('post/<slug:slug>/', views.post_detail)
# Matches: /post/my-first-post/
# Doesn't match: /post/my post/ or /post/my@post/

# 4. uuid - Matches UUID format
path('user/<uuid:user_id>/', views.user_profile)
# Matches: /user/550e8400-e29b-41d4-a716-446655440000/

# 5. path - Matches non-empty strings including path separators
path('file/<path:file_path>/', views.serve_file)
# Matches: /file/documents/2023/report.pdf

Practical Application Examples

python
# blog/urls.py
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    # Home page
    path('', views.home, name='home'),
    
    # Article related
    path('articles/', views.article_list, name='article_list'),
    path('article/<int:id>/', views.article_detail, name='article_detail'),
    path('article/<slug:slug>/', views.article_detail_by_slug, name='article_detail_by_slug'),
    
    # Categories and tags
    path('category/<slug:category_slug>/', views.category_detail, name='category_detail'),
    path('tag/<slug:tag_slug>/', views.tag_detail, name='tag_detail'),
    
    # Archives
    path('archive/<int:year>/', views.year_archive, name='year_archive'),
    path('archive/<int:year>/<int:month>/', views.month_archive, name='month_archive'),
    
    # Search and RSS
    path('search/', views.search, name='search'),
    path('rss/', views.rss_feed, name='rss_feed'),
    
    # User related
    path('author/<str:username>/', views.author_detail, name='author_detail'),
]

Corresponding view functions:

python
# blog/views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import Article, Category, Tag
from django.contrib.auth.models import User

def home(request):
    """Home page view"""
    latest_articles = Article.objects.filter(status='published')[:5]
    return render(request, 'blog/home.html', {'articles': latest_articles})

def article_detail(request, id):
    """Get article details by ID"""
    article = get_object_or_404(Article, id=id, status='published')
    return render(request, 'blog/article_detail.html', {'article': article})

def article_detail_by_slug(request, slug):
    """Get article details by slug"""
    article = get_object_or_404(Article, slug=slug, status='published')
    return render(request, 'blog/article_detail.html', {'article': article})

def category_detail(request, category_slug):
    """Category detail page"""
    category = get_object_or_404(Category, slug=category_slug)
    articles = Article.objects.filter(category=category, status='published')
    context = {
        'category': category,
        'articles': articles,
    }
    return render(request, 'blog/category_detail.html', context)

def year_archive(request, year):
    """Yearly archive"""
    articles = Article.objects.filter(
        publish_date__year=year,
        status='published'
    )
    context = {
        'year': year,
        'articles': articles,
    }
    return render(request, 'blog/year_archive.html', context)

def month_archive(request, year, month):
    """Monthly archive"""
    articles = Article.objects.filter(
        publish_date__year=year,
        publish_date__month=month,
        status='published'
    )
    context = {
        'year': year,
        'month': month,
        'articles': articles,
    }
    return render(request, 'blog/month_archive.html', context)

def author_detail(request, username):
    """Author detail page"""
    author = get_object_or_404(User, username=username)
    articles = Article.objects.filter(author=author, status='published')
    context = {
        'author': author,
        'articles': articles,
    }
    return render(request, 'blog/author_detail.html', context)

Path Parameter Capture

Parameter Passing Mechanism

Parameters captured in URLs are passed to view functions as keyword arguments:

python
# URL configuration
path('article/<int:year>/<int:month>/<int:day>/<slug:slug>/', 
     views.article_detail, name='article_detail')

# View function
def article_detail(request, year, month, day, slug):
    """Article detail view"""
    # year, month, day, slug parameters automatically extracted from URL
    article = get_object_or_404(
        Article,
        slug=slug,
        publish_date__year=year,
        publish_date__month=month,
        publish_date__day=day,
        status='published'
    )
    return render(request, 'blog/article_detail.html', {'article': article})

Parameter Validation and Processing

python
# Complex URL parameter processing
def article_detail(request, year, month, day, slug):
    """Article detail view with parameter validation"""
    # Validate date parameters
    try:
        from datetime import date
        target_date = date(year, month, day)
    except ValueError:
        raise Http404("Invalid date")
    
    # Query article
    article = get_object_or_404(
        Article,
        slug=slug,
        publish_date__date=target_date,
        status='published'
    )
    
    context = {
        'article': article,
        'archive_date': target_date,
    }
    return render(request, 'blog/article_detail.html', context)

Optional Parameters

python
# Handle optional parameters with default values
path('articles/', views.article_list, name='article_list'),
path('articles/page/<int:page>/', views.article_list, name='article_list_page'),

def article_list(request, page=1):
    """Article list view with pagination support"""
    from django.core.paginator import Paginator
    
    articles = Article.objects.filter(status='published')
    paginator = Paginator(articles, 10)  # 10 articles per page
    
    try:
        articles = paginator.page(page)
    except:
        articles = paginator.page(1)
    
    return render(request, 'blog/article_list.html', {'articles': articles})

Regular Expression Paths

For complex URL patterns, Django also supports using regular expressions:

python
from django.urls import re_path
from . import views

urlpatterns = [
    # Use named groups
    re_path(r'^article/(?P<year>[0-9]{4})/$', views.year_archive, name='year_archive'),
    re_path(r'^article/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', 
            views.month_archive, name='month_archive'),
    
    # Complex pattern matching
    re_path(r'^api/v(?P<version>[12])/article/(?P<id>[0-9]+)/$', 
            views.api_article_detail, name='api_article_detail'),
    
    # File download
    re_path(r'^download/(?P<filename>[\w\-_\.]+\.(pdf|doc|txt))/$', 
            views.download_file, name='download_file'),
]

Regular Expression Examples

python
# More complex regular expression URL patterns
urlpatterns = [
    # Match email format usernames
    re_path(r'^user/(?P<email>[\w\.-]+@[\w\.-]+\.\w+)/$', 
            views.user_by_email, name='user_by_email'),
    
    # Match specific format product codes
    re_path(r'^product/(?P<code>[A-Z]{2}-\d{4}-[A-Z])/$', 
            views.product_detail, name='product_detail'),
    
    # Match multi-level category paths
    re_path(r'^category/(?P<path>.+)/$', 
            views.nested_category, name='nested_category'),
]

def user_by_email(request, email):
    """Find user by email"""
    user = get_object_or_404(User, email=email)
    return render(request, 'users/profile.html', {'user': user})

def product_detail(request, code):
    """Product detail page"""
    # Code format: AA-1234-B
    product = get_object_or_404(Product, code=code)
    return render(request, 'products/detail.html', {'product': product})

def nested_category(request, path):
    """Handle multi-level category paths"""
    # Path could be: tech/web/django or lifestyle/travel
    categories = path.split('/')
    # Handle nested category logic
    return render(request, 'blog/nested_category.html', {'path': path})

URL Configuration Best Practices

1. Semantic URL Design

python
# Good URL design
urlpatterns = [
    path('', views.home, name='home'),
    path('articles/', views.article_list, name='article_list'),
    path('articles/<slug:slug>/', views.article_detail, name='article_detail'),
    path('categories/<slug:slug>/', views.category_detail, name='category_detail'),
    path('authors/<str:username>/', views.author_profile, name='author_profile'),
    path('archive/<int:year>/<int:month>/', views.monthly_archive, name='monthly_archive'),
]

# Avoid URL design
urlpatterns = [
    path('p/<int:id>/', views.post_detail),           # Unclear path
    path('cat/<int:id>/', views.category_detail),     # Unclear abbreviation
    path('u/<int:id>/', views.user_profile),          # Use ID instead of username
    path('page.php?id=<int:id>', views.detail),       # URLs mimicking other technologies
]

2. URL Hierarchy Structure

python
# Clear hierarchy structure
urlpatterns = [
    # Blog related
    path('blog/', include([
        path('', views.blog_home, name='blog_home'),
        path('posts/', views.post_list, name='post_list'),
        path('posts/<slug:slug>/', views.post_detail, name='post_detail'),
        path('categories/', views.category_list, name='category_list'),
        path('categories/<slug:slug>/', views.category_detail, name='category_detail'),
    ])),
    
    # User related
    path('users/', include([
        path('', views.user_list, name='user_list'),
        path('<str:username>/', views.user_profile, name='user_profile'),
        path('<str:username>/posts/', views.user_posts, name='user_posts'),
    ])),
]

3. Parameter Validation

python
# Custom path converter
class YearConverter:
    regex = r'[0-9]{4}'
    
    def to_python(self, value):
        return int(value)
    
    def to_url(self, value):
        return str(value)

# Register converter
from django.urls import register_converter
register_converter(YearConverter, 'year')

# Use custom converter
urlpatterns = [
    path('archive/<year:year>/', views.year_archive, name='year_archive'),
]

4. Error Handling

python
def article_detail(request, slug):
    """Article detail view with error handling"""
    try:
        article = Article.objects.get(slug=slug, status='published')
    except Article.DoesNotExist:
        # Log access
        import logging
        logger = logging.getLogger(__name__)
        logger.warning(f'Article not found: {slug}')
        
        # Return 404 page
        raise Http404("Article does not exist")
    except Article.MultipleObjectsReturned:
        # Handle duplicate slug case
        article = Article.objects.filter(slug=slug, status='published').first()
    
    return render(request, 'blog/article_detail.html', {'article': article})

Debugging URL Configuration

1. URL Debugging Techniques

python
# Add debug URLs in development environment
from django.conf import settings

if settings.DEBUG:
    # Show all URL patterns
    def show_urls(request):
        from django.urls import get_resolver
        resolver = get_resolver()
        url_patterns = []
        
        def collect_urls(patterns, prefix=''):
            for pattern in patterns:
                if hasattr(pattern, 'pattern'):
                    url_patterns.append(prefix + str(pattern.pattern))
                elif hasattr(pattern, 'url_patterns'):
                    collect_urls(pattern.url_patterns, prefix + str(pattern.pattern))
        
        collect_urls(resolver.url_patterns)
        return render(request, 'debug/urls.html', {'urls': url_patterns})
    
    urlpatterns += [
        path('debug/urls/', show_urls, name='debug_urls'),
    ]

2. URL Testing

python
# tests.py
from django.test import TestCase
from django.urls import reverse, resolve
from django.contrib.auth.models import User
from .models import Article
from . import views

class URLTests(TestCase):
    def setUp(self):
        self.user = User.objects.create_user('testuser', 'test@example.com', 'pass')
        self.article = Article.objects.create(
            title='Test Article',
            slug='test-article',
            content='Test content',
            author=self.user,
            status='published'
        )
    
    def test_home_url(self):
        """Test home page URL"""
        url = reverse('blog:home')
        self.assertEqual(url, '/')
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
    
    def test_article_detail_url(self):
        """Test article detail URL"""
        url = reverse('blog:article_detail', kwargs={'slug': 'test-article'})
        self.assertEqual(url, '/articles/test-article/')
        
        # Test URL resolution
        resolver = resolve(url)
        self.assertEqual(resolver.func, views.article_detail)
        self.assertEqual(resolver.kwargs, {'slug': 'test-article'})
    
    def test_invalid_urls(self):
        """Test invalid URLs return 404"""
        response = self.client.get('/articles/nonexistent/')
        self.assertEqual(response.status_code, 404)

Summary

URL configuration is the entry point for Django applications. Reasonable URL design can:

  1. ✅ Provide clear URL structure
  2. ✅ Support SEO optimization
  3. ✅ Facilitate user memory and sharing
  4. ✅ Convenient development and maintenance

Key points:

  • Use semantic URL paths
  • Reasonably use path parameters
  • Maintain URL structure consistency
  • Add appropriate error handling
  • Name URL patterns for reverse resolution

Next Article

We will learn advanced URL features, including named URLs, reverse resolution, and URL namespaces.

3.2 Django URL Advanced Configuration →

Directory

Return to Course Directory

Released under the [BY-NC-ND License](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.en).