Django / Request and data foundations / Lesson 2
URLs, converters, and route names
Map paths to views with explicit converters and stable names while separating routing from resource authorization.
On this page
Lesson goals
- A URLconf describes dispatch
- Resolve a path in an isolated URL module
- Review path changes as interface changes
A URLconf describes dispatch
A URL configuration maps incoming paths to view callables. Django examines patterns in order and dispatches the matching view with captured arguments. The query string is not part of the path pattern; a view reads query parameters separately. Keep route patterns focused on locating an operation rather than encoding every validation or permission rule. A successful match does not mean a database record exists or that the current caller may access it. Those are later boundaries. Organize related routes into an app URLconf and include it from the project so a reusable app does not hard-code its deployment prefix.
Converters establish limited input guarantees
A path converter such as int parses the matching path segment into an integer before invoking the view. A slug converter accepts a defined character shape, not a complete business identifier policy. A positive integer path can still identify a missing or unauthorized record. Choose converter and validation contracts deliberately and handle absence with an appropriate response, commonly a 404 for a missing resource. Do not turn converter success into trust for arbitrary database access. Trailing slash handling and redirect middleware can affect a request, so test the actual declared route instead of assuming every similar spelling reaches the same view with the same method.
Resolve a path in an isolated URL module
The first example declares its own URL patterns inside one complete program and resolves a known path. It does not import an application URLconf or contact a database. The resolved kwargs contain an integer because the converter performed that transformation. Calling the matched view still supplies the request argument explicitly in this demonstration. Resolution is a useful narrow test for pattern names and converter behavior, but it does not run middleware or prove a browser request works. A real application should also test unmatched paths and distinguish an unknown route from a known route whose resource is absent.
from django.conf import settings
settings.configure(
SECRET_KEY="synthetic-course-key",
USE_I18N=False, USE_TZ=True,
INSTALLED_APPS=[],
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
DEFAULT_AUTO_FIELD="django.db.models.AutoField",
ALLOWED_HOSTS=["testserver"],
)
import django
django.setup()
from django.http import HttpResponse
from django.urls import path, resolve
def detail(request, course_id):
return HttpResponse(str(course_id), content_type="text/plain")
urlpatterns = [path("courses/<int:course_id>/", detail, name="course-detail")]
match = resolve("/courses/7/", urlconf=__name__)
print(match.url_name)
print(match.kwargs["course_id"], type(match.kwargs["course_id"]).__name__)
course-detail
7 int
Resolution returns the named match and an integer argument. No record lookup or request middleware is implied.
Reverse names rather than copy path strings
The second example reverses a named route with a record id and includes an approved query value through Django 5.2’s reverse interface. A route name is a navigation contract that can survive a changed path prefix. Namespaces prevent unrelated apps from colliding on generic names such as detail. Pass arguments matching the route’s converter and required parameters; a missing or incompatible argument can raise NoReverseMatch. A reversed URL is not an authorization token. Templates, redirects, and tests should use the named contract while the server still validates identity and access when the resulting request arrives.
from django.conf import settings
settings.configure(
SECRET_KEY="synthetic-course-key",
USE_I18N=False, USE_TZ=True,
INSTALLED_APPS=[],
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
DEFAULT_AUTO_FIELD="django.db.models.AutoField",
ALLOWED_HOSTS=["testserver"],
)
import django
django.setup()
from django.http import HttpResponse
from django.urls import path, reverse
def detail(request, course_id):
return HttpResponse("Local route")
urlpatterns = [path("courses/<int:course_id>/", detail, name="course-detail")]
print(reverse("course-detail", kwargs={"course_id": 7}, urlconf=__name__, query={"view": "summary"}))
/courses/7/?view=summary
The name supplies the path and reverse encodes the query under the Django 5.2 API. The program performs no navigation.
Review path changes as interface changes
Test order-sensitive patterns, renamed views, namespaces, and links from the page that uses them. Avoid broad catch-all routes that accidentally hide a more specific endpoint. Keep external redirect destinations under an explicit allow policy instead of trusting a next query parameter; routing and safe redirect handling solve different questions. When a URL changes publicly, plan compatibility and redirect behavior, including whether preserving or changing the method is appropriate. This course’s routing programs only resolve and construct local paths. They do not establish deployment rewrites, proxy prefixes, or access controls, which need their own integration evidence.
Common errors and fixes
A valid route still returns a missing record
- Why it happens
- Path matching was confused with resource existence.
- How to fix it
- Look up the authorized resource and handle absence deliberately.
- Prevent it next time
- Test converter, existence, and permission separately.
A reverse call fails after a refactor
- Why it happens
- Its name, namespace, or arguments no longer match the pattern.
- How to fix it
- Compare the named route contract and provided kwargs.
- Prevent it next time
- Use named-route tests before publishing path changes.
A broad pattern shadows a specific route
- Why it happens
- Pattern ordering sends the path to an earlier match.
- How to fix it
- Reorder or narrow the competing patterns.
- Prevent it next time
- Include representative ambiguous paths in route tests.
Check your understanding
1. Does <int:course_id> authorize access to course 7?
Show worked solution
No. It establishes a parsed argument shape. The view must still locate the resource under its visibility scope and enforce the operation’s permission. A positive id can be absent or belong to someone else, so route tests and access tests need distinct assertions.
2. How can a reusable app avoid coupling links to /training/?
Show worked solution
Define app route names and an app namespace, include the URLconf at the chosen project prefix, and reverse the names from templates or code. The host can then move the prefix without rewriting every link, while tests confirm the resulting public URLs.
Key takeaways
- Routing selects a view, not a permission decision.
- Converters have bounded guarantees.
- Names and namespaces make navigation maintainable.