~10 min read • Updated Mar 10, 2026
1. Introduction: Why URL Design Matters
A clean, readable URL structure is a hallmark of a well‑designed web application. Django gives you full control over your URL patterns without imposing framework‑specific constraints.
URL patterns are defined in a Python module called a URLconf (URL configuration). A URLconf maps URL path expressions to Python view functions or class‑based views.
2. How Django Processes a Request
When a user requests a page, Django follows this algorithm:
- Determine the root URLconf module (usually from
ROOT_URLCONF, unless overridden by middleware). - Load the module and locate the
urlpatternslist. - Iterate through URL patterns in order and find the first match.
- Call the associated view with:
- the
HttpRequestobject - positional arguments (from unnamed URL captures)
- keyword arguments (from named URL captures)
- the
- If no pattern matches, Django returns an appropriate error view (e.g., 404).
3. Example URLconf
from django.urls import path
from . 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),
path("articles/<int:year>/<int:month>/<slug:slug>/", views.article_detail),
]
Notes:
- Use angle brackets to capture values from the URL.
- Converters like
<int:year>ensure type conversion. - No leading slash is needed—Django adds it automatically.
- Patterns are matched in order, so special cases should appear first.
Example requests:
/articles/2005/03/→ callsviews.month_archive(request, year=2005, month=3)/articles/2003/→ matches the first pattern, not the second/articles/2003→ no match (missing trailing slash)/articles/2003/03/building-a-django-site/→ callsviews.article_detail(...)
4. Built‑in Path Converters
| Converter | Description |
|---|---|
| str | Any non‑empty string except “/” (default) |
| int | Zero or positive integer → converted to int |
| slug | Letters, numbers, hyphens, underscores |
| uuid | Formatted UUID → converted to UUID instance |
| path | Any string including “/” |
5. Creating Custom Path Converters
If built‑in converters aren’t enough, you can define your own.
A custom converter must include:
- regex — a string defining the match pattern
- to_python() — converts the matched string to a Python object
- to_url() — converts the Python object back to a string for URL reversing
Example: Four‑digit year converter
class FourDigitYearConverter:
regex = "[0-9]{4}"
def to_python(self, value):
return int(value)
def to_url(self, value):
return "%04d" % value
Registering the converter:
from django.urls import path, register_converter
from . import converters, views
register_converter(converters.FourDigitYearConverter, "yyyy")
urlpatterns = [
path("articles/2003/", views.special_case_2003),
path("articles/<yyyy:year>/", views.year_archive),
]
6. Why URLconf Is Powerful
- It’s pure Python—dynamic, flexible, and composable.
- You can nest URLconfs for modular apps.
- You can internationalize URLs based on active language.
- You can create clean, stable, human‑friendly URLs.
Conclusion
Django’s URL dispatcher is one of its most elegant and flexible features. By defining URLconfs, using path converters, and optionally creating custom converters, you can build clean, expressive, and maintainable URL structures for any application.
1. Why Use Regular Expressions in URL Patterns?
Django’s path() function covers most URL routing needs, but when you require more complex matching rules, re_path() allows you to define URL patterns using full regular expressions.
2. Named Regex Groups
Python’s syntax for named regex groups is:
(?Ppattern)
This captures a value and passes it to the view as a keyword argument.
3. Example URLconf Using re_path()
urlpatterns = [
path("articles/2003/", views.special_case_2003),
re_path(r"^articles/(?P[0-9]{4})/$", views.year_archive),
re_path(r"^articles/(?P[0-9]{4})/(?P[0-9]{2})/$", views.month_archive),
re_path(
r"^articles/(?P[0-9]{4})/(?P[0-9]{2})/(?P[\w-]+)/$",
views.article_detail,
),
]
Differences from path()
- Regex gives stricter control (e.g., year must be exactly four digits).
- All captured values are passed as strings.
- Switching between
path()andre_path()may require updating your views.
4. Unnamed Regex Groups
([0-9]{4})
This is allowed but not recommended because:
- It’s easy to mismatch positional arguments.
- If mixed with named groups, unnamed groups are ignored.
5. Nested Arguments
Regex allows nested groups, but they can create unnecessary coupling between URLs and views.
Bad example:
re_path(r"^blog/(page-([0-9]+)/)?$", blog_articles)
Good example:
re_path(r"^comments/(?:page-(?P[0-9]+)/)?$", comments)
Guideline:
- Capture only what the view needs.
- Use
(?:...)for non‑capturing groups.
6. What URLconf Matches Against
Django matches only the path portion of the URL, not:
- GET parameters
- POST data
- Domain name
- HTTP method
Example:
For:
https://example.com/myapp/?page=3
URLconf matches only:
myapp/
7. Default View Arguments
You can define default values in your view:
urlpatterns = [
path("blog/", views.page),
path("blog/page/", views.page),
]
def page(request, num=1):
...
8. Performance
Django compiles regex patterns once and caches them, so performance remains efficient.
9. Error Handling
You can customize error views in your root URLconf:
handler400handler403handler404handler500
These must be set in the root URLconf, not in included URLconfs.
10. Including Other URLconfs
You can modularize your routing using include():
urlpatterns = [
path("community/", include("aggregator.urls")),
path("contact/", include("contact.urls")),
]
Django strips the matched prefix and passes the remaining path to the included URLconf.
Including a list of patterns:
extra_patterns = [
path("reports/", credit_views.report),
path("reports//", credit_views.report),
path("charge/", credit_views.charge),
]
urlpatterns = [
path("credit/", include(extra_patterns)),
]
11. Captured Parameters Passed to Included URLconfs
Captured parameters propagate into included URLconfs:
path("/blog/", include("foo.urls.blog"))
12. Passing Extra Options to Views
You can pass extra keyword arguments to views:
path("blog//", views.year_archive, {"foo": "bar"})
Result:
views.year_archive(request, year=2005, foo="bar")
Conflict resolution:
If both URL capture and extra kwargs define the same key, the extra kwargs override the captured value.
Conclusion
Using re_path() gives you full regex power for defining complex URL patterns.
By understanding named groups, nested arguments, include(), error handling, and parameter passing, you can build flexible and expressive routing structures for any Django project.
1. Passing Extra Options to include()
Django allows you to pass extra keyword arguments to include(), and these arguments will be passed to every URL pattern inside the included URLconf.
Example: Two Equivalent URLconfs
Set One:
# main.py
urlpatterns = [
path("blog/", include("inner"), {"blog_id": 3}),
]
# inner.py
urlpatterns = [
path("archive/", views.archive),
path("about/", views.about),
]
Set Two:
# main.py
urlpatterns = [
path("blog/", include("inner")),
]
# inner.py
urlpatterns = [
path("archive/", views.archive, {"blog_id": 3}),
path("about/", views.about, {"blog_id": 3}),
]
Important: Extra options are passed to every view in the included URLconf, even if a view does not accept those arguments. Use this technique only when all views support the extra parameters.
2. Reverse Resolution of URLs
URL reversing allows you to generate URLs dynamically based on the view name and arguments, avoiding hardcoded URLs.
Django supports reversing in three places:
- Templates: using the
{% url %}tag - Python code: using
reverse() - Models: using
get_absolute_url()
Example URLconf
path("articles/<int:year>/", views.year_archive, name="news-year-archive")
Template usage:
2012 Archive
Python usage:
return HttpResponseRedirect(reverse("news-year-archive", args=(2006,)))
If the URL structure changes, only the URLconf needs updating — all reverse lookups continue to work.
3. Naming URL Patterns
Named URL patterns are essential for URL reversing. You can use any string as a name.
Best Practices:
- Use unique names to avoid collisions.
- Prefix names with the app name (e.g.,
blog-archive). - You may intentionally override names (e.g., custom login view named
login). - You can reuse names if the argument signatures differ.
4. URL Namespaces
Namespaces allow you to distinguish between identical URL names in different applications or different instances of the same app.
Two Types of Namespaces:
| Namespace | Description |
|---|---|
| Application namespace | Identifies the app (e.g., 'admin') |
| Instance namespace | Identifies a specific instance of the app |
Example:
The admin index page is referenced as:
'admin:index'
Namespaces can be nested:
'sports:polls:index'
This means:
- Top-level namespace: sports
- Nested namespace: polls
- URL name: index
5. Why Namespaces Matter
- They prevent naming conflicts across apps.
- They allow multiple instances of the same app (e.g., multiple admin sites).
- They make URL reversing predictable and scalable.
Conclusion
Advanced URL routing features in Django — including extra options for include(), URL reversing, named URL patterns, and namespaces — provide powerful tools for building clean, maintainable, and scalable URL architectures.
By following these practices, you ensure your project remains DRY, flexible, and easy to evolve over time.
1. Introduction: Why Namespaced URL Reversing Matters
When multiple applications—or multiple instances of the same application—exist in a Django project, URL names can collide. Namespaces allow Django to uniquely identify URL patterns and resolve them correctly during URL reversing.
2. How Django Resolves a Namespaced URL
Given a namespaced URL like 'polls:index', Django splits it into:
- namespace: polls
- view name: index
Django’s resolution steps:
- Find the application namespace Django searches for all instances of the application namespace (polls).
- If a current application is defined
Django uses that specific instance.
You can set it using:
reverse(..., current_app="namespace")request.current_appin templates
- If no current application exists Django looks for a default instance—an instance whose instance namespace matches the application namespace.
- If no default instance exists Django uses the last registered instance.
- If the namespace does not match an application namespace Django tries to match it as an instance namespace.
- For nested namespaces These steps repeat for each namespace segment until only the view name remains.
3. Example: Two Instances of the polls Application
urls.py
urlpatterns = [
path("author-polls/", include("polls.urls", namespace="author-polls")),
path("publisher-polls/", include("polls.urls", namespace="publisher-polls")),
]
polls/urls.py
app_name = "polls"
urlpatterns = [
path("", views.IndexView.as_view(), name="index"),
path("<int:pk>/", views.DetailView.as_view(), name="detail"),
]
Resolution behavior:
- If the current instance is 'author-polls':
Result:reverse("polls:index", current_app="author-polls")/author-polls/ - In templates:
If the current_app is 'author-polls', it resolves to{% url 'polls:index' %}/author-polls/. - If no current instance exists Django uses the last registered instance → publisher-polls.
- 'author-polls:index' always resolves to the author-polls instance.
- If an instance named polls also existed, it would become the default instance.
4. Defining Application Namespaces in URLconfs
To enable namespacing, define app_name in the included URLconf:
app_name = "polls"
urlpatterns = [...]
Then include it normally:
path("polls/", include("polls.urls"))
This gives the included URLs an application namespace of polls.
5. Defining Namespaces Using a Tuple
You can also include a tuple containing URL patterns and an application namespace:
polls_patterns = (
[
path("", views.IndexView.as_view(), name="index"),
path("<int:pk>/", views.DetailView.as_view(), name="detail"),
],
"polls",
)
urlpatterns = [
path("polls/", include(polls_patterns)),
]
6. Instance Namespace
Use the namespace= argument in include() to define an instance namespace:
path("author-polls/", include("polls.urls", namespace="author-polls"))
If no instance namespace is provided, Django uses the application namespace as the instance namespace.
Conclusion
Django’s namespaced URL system provides a powerful way to manage complex routing scenarios, especially when multiple instances of the same application are deployed. By understanding application namespaces, instance namespaces, and Django’s resolution strategy, you can build scalable, conflict‑free URL architectures for large projects.
Written & researched by Dr. Shahin Siami