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 objectpath() Function Details
Django 2.0 introduced the path() function, providing a more concise and type-safe URL configuration method.
Basic Syntax
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
# 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:
# 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.pdfPractical Application Examples
# 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:
# 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:
# 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
# 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
# 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:
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
# 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
# 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
# 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
# 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
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
# 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
# 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:
- ✅ Provide clear URL structure
- ✅ Support SEO optimization
- ✅ Facilitate user memory and sharing
- ✅ 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 →