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).