Django's URL dispatcher is powerful and flexible. Let's explore advanced patterns and best practices.
Naming URLs
Always name your URLs for easy referencing:
python# polls/urls.py urlpatterns = [ path('', views.index, name='index'), path('<int:pk>/', views.detail, name='detail'), ]
Use names in templates and code:
python# In views from django.urls import reverse url = reverse('detail', args=[5]) # '/polls/5/' url = reverse('detail', kwargs={'pk': 5}) # '/polls/5/'
html<!-- In templates --> <a href="{% url 'detail' question.id %}">View Details</a>
App Namespacing
When you have multiple apps, use namespaces:
python# polls/urls.py from django.urls import path from . import views app_name = 'polls' # Add namespace urlpatterns = [ path('', views.index, name='index'), path('<int:pk>/', views.detail, name='detail'), ]
Now reference with namespace:
pythonreverse('polls:detail', args=[5])
html<a href="{% url 'polls:detail' question.id %}">View</a>
Including Other URLconfs
Organize URLs across apps:
python# mysite/urls.py from django.contrib import admin from django.urls import include, path urlpatterns = [ path('admin/', admin.site.urls), path('polls/', include('polls.urls')), path('blog/', include('blog.urls')), path('api/', include('api.urls')), ]
Regular Expression Patterns
For complex patterns, use re_path:
pythonfrom django.urls import path, re_path from . import views urlpatterns = [ # Standard path path('articles/<int:year>/', views.year_archive), # Regex pattern for 4-digit year re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), # Regex for valid usernames re_path(r'^user/(?P<username>[\w.@+-]+)/$', views.user_profile), ]
Passing Extra Options
Pass extra context to views:
pythonurlpatterns = [ path( 'blog/<int:year>/', views.year_archive, {'foo': 'bar'}, # Extra context name='year_archive' ), ] # View receives: request, year, foo='bar' def year_archive(request, year, foo): pass
Custom Path Converters
Create your own converters:
python# converters.py class FourDigitYearConverter: regex = '[0-9]{4}' def to_python(self, value): return int(value) def to_url(self, value): return '%04d' % value # urls.py from django.urls import path, register_converter from . import converters, views register_converter(converters.FourDigitYearConverter, 'yyyy') urlpatterns = [ path('articles/<yyyy:year>/', views.year_archive), ]
URL Best Practices
- Use meaningful names:
article-detailnotview1 - Be consistent: Pick a pattern and stick with it
- Use namespaces: Prevent name collisions between apps
- Keep URLs clean: No file extensions, no query params for navigation
- Use hyphens:
my-articlenotmy_articleormyArticle
python# Good URL design /articles/ /articles/2024/ /articles/2024/my-first-post/ /users/johndoe/ # Bad URL design /showArticle.php?id=123 /articles_list/ /get_user?username=johndoe
Common Pitfalls
- Forgetting
app_namewhen using namespaces: Withoutapp_namein your app'surls.py, Django raises aNoReverseMatcherror when you use{% url 'polls:detail' %}. - Using
reverse()at class level: Usereverse_lazy()instead ofreverse()in class attributes (likesuccess_url) because URLs are not loaded when the class is defined. - Hardcoding URLs in templates: Always use
{% url %}tags instead of hardcoding paths like/polls/5/. Hardcoded URLs break when URL patterns change.
Best Practices
- Always name your URL patterns: Use
name='detail'in everypath()call so you can reference URLs by name instead of path. - Use app namespaces: Set
app_namein each app'surls.pyto prevent name collisions between apps. - Keep URLs RESTful and readable: Use patterns like
/articles/2024/my-post/instead of/get_article?id=123.
Summary
- Name every URL pattern with
name='...'for reverse lookups - Use
app_namein app-levelurls.pyto create namespaces (e.g.,polls:detail) - Use
reverse()in views and{% url %}in templates to generate URLs dynamically include()organizes URLs by delegating to app-levelurls.pyfiles- For complex patterns, use
re_path()with regular expressions or create custom path converters
Code Examples
python
# polls/urls.py
from django.urls import path
from . import views
app_name = 'polls' # Namespace
urlpatterns = [
path('', views.index, name='index'),
path('<int:pk>/', views.detail, name='detail'),
]
# Usage: reverse('polls:detail', args=[5]) -> '/polls/5/'