Configuring URL Routing in Django
URL Configuration
URL routing in Django maps URL patterns to view functions. This mapping is defined in a URL configuration file.
# Basic format
from django.conf.urls import url
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^author/', views.author),
]
Key points:
- Once a route matches, subsequent patterns are not evaluated.
- For a URL like "127.0.0.1:8000/author", Django first tries to match without a trailing slash, then with one.
- Exact matching uses "^" and "$", e.g., "^author/$".
- A homepage route can be added with url(r"^$", views.home).
- A catch-all route uses url(r"", views.error).
Named and Unnamed Group Matching
Unnamed Groups
# urls.py
url(r'^author/([0-9]{4})/$', views.author),
# views.py
def author(request, year_value):
pass
# The value from parentheses is passed as a positional argument to year_value.
Named Groups
# urls.py
url(r'^author/(?P<year>\d+)/$', views.author),
# views.py
def author(request, year):
pass
# The value is passed as a keyword argument named year.
Important: Named and unnamed groups cannot be mixed in the same pattern. Both support multiple parameters.
URL Matching Behavior
URL matching does not consider request methods or query parameters. For example, "127.0.0.1:8080/login/?name=sun&pwd=123" only matches "login/".
Parameter Types
Parameters captured from groups are always strings. For instance, accessing "127.0.0.1:8080/book/2016" yields year='2016' as a string.
Default Values in Views
# urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^blog/$', views.page),
url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]
# views.py
def page(request, num="1"):
pass
# Accessing 127.0.0.1:8080/blog/ gives num='1'.
# Accessing 127.0.0.1:8080/blog/4/ gives num='4'.
Passing Extra Arguments to Views
# urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^blog/(?P<year>[0-9]{4})/$', views.archive, {'extra_param': 'value'}),
]
# views.py
def archive(request, year, extra_param):
pass
# Accessing 127.0.0.1:8080/blog/2014 executes archive(request, year='2014', extra_param="value").
URL Reverse Resolution
Assigning names to URLs allows reverse lookup, enabling dynamic URL generation without hardcoding paths.
Basic Reverse Resolution
# urls.py
url(r"^testadd213123/$", views.testadd, name="testadd")
# Backend reverse
from django.shortcuts import reverse
result = reverse("testadd") # Returns "/testadd213123/"
# Frontend reverse in templates
<a href="{% url 'testadd' %}">Add</a>
# Renders as <a href="/testadd213123/">Add</a>
Reverse with Unnamed Groups
# urls.py
url(r"^testadd213123/(\d+)$", views.testadd, name="testadd")
# Backend reverse
def testadd(request, identifier):
result = reverse("testadd", args=(identifier,))
# Frontend reverse
<a href="{% url 'testadd' 123 %}">Add</a>
# Renders as <a href="/testadd213123/123/">Add</a>
Reverse with Named Groups
# urls.py
url(r"^testadd213123/(?P<year>\d+)$", views.testadd, name="testadd")
# Backend reverse methods
def testadd(request, year):
result1 = reverse("testadd", args=(year,))
result2 = reverse("testadd", kwargs={"year": year})
# Frontend reverse
<a href="{% url 'testadd' year %}">Add</a>
# Renders as <a href="/testadd213123/2012/">Add</a>
URL Dispatching
Central routing can delegate to app-specific URL configurations for better organization.
# Main urls.py
from django.conf.urls import include
urlpatterns = [
url(r'^app01/', include("app01.urls")),
url(r'^app02/', include("app02.urls")),
]
# app01/urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^index/', views.index, name="index")
]
# app02/urls.py
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r'^index/', views.index, name="index")
]
To avoid conflicts in reverse resolution when apps have identical URL names:
- Use namespaces:
url(r'^app01/', include("app01.urls", namespace='app01')), url(r'^app02/', include("app02.urls", namespace='app02')) # Reverse with reverse('app01:index') - Use distinct names:
# In app01/urls.py: name="app01_index" # In app02/urls.py: name="app02_index"
Pseudo-Static URLs
Adding ".html" suffixes to URLs can make them appear static, potentially improving SEO by mimicking static file paths while still being processed dynamically.
Python Virtual Environments
Virtual environments isolate project dependencies, ensuring each project uses its specific interpreter and modules. This improves efficiency and prevents conflicts.
Django 1.x vs 2.x Differences
Key Chenges
- In Django 1.x,
urlis used; in 2.x,re_pathserves a similar role for regex patterns. - Django 2.x introduces
pathfor simpler, non-regex URL matching. - Parameters in Django 1.x are always strings; Django 2.x can convert types.
- Foreign key creation in Django 2.x requires
on_delete=models.CASCADE.
Django 2.x Path Converters
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('index/', views.index, name="index"),
re_path(r"^edit/(\d+)/$", views.edit),
]
Built-in converters in path:
str: Matches any non-empty string except "/".int: Matches zero or positive integers.slug: Matches ASCII letters, numbers, hyphens, and underscores.uuid: Matches formatted UUIDs.path: Matches any non-empty string, including "/".
Example:
from django.urls import path
from app01 import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
]
Custom Conevrters
class FourDigitYearConverter:
regex = '[0-9]{4}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%04d' % value
# Register and use
from django.urls import register_converter, path
from app01 import views
register_converter(FourDigitYearConverter, 'yyyy')
urlpatterns = [
path('articles/<yyyy:year>/', views.year_archive),
]
# views.py
def year_archive(request, year):
pass # year is an integer