Typorithm · easy

Named routes, converters and reverse

pythondjangoindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Named routes, converters and reverse

0%

Concept and contract

Generate paths from named routes while keeping format checks separate from object existence and authorization.

URL configuration maps a path pattern to a callable view and can name that route for later use. A path converter describes accepted path syntax and converts the captured text into a Python value.

That conversion does not prove a database object exists or that a user may see it. This lesson keeps the view deliberately small so route identity and the captured argument remain visible.

Mechanism and worked example

The route captures role_id with the int converter, and reverse uses the named route to build a path.

The route captures role_id with the int converter, and reverse uses the named route to build a path. The function then sends a real test-client request and returns the generated path alongside the JSON value observed by the view.

Its explicit exact-integer check rejects booleans at this local helper boundary. A nonnegative zero remains valid route syntax here; any stricter domain identity rule belongs to a later lookup.

Complete worked example

python

from bootstrap_lab import django_setup
django_setup(__name__)

from django.http import JsonResponse
from django.test import Client
from django.urls import path, reverse

def detail(request, role_id):
    return JsonResponse({"id": role_id})

urlpatterns = [path("roles/<int:role_id>/", detail, name="role-detail")]

def route(role_id):
    if type(role_id) is not int or role_id < 0:
        raise ValueError("nonnegative integer required")
    url = reverse("role-detail", args=[role_id])
    response = Client().get(url)
    return {"path": url, "id": response.json()["id"]}
Reasoning
The route captures role_id with the int converter, and reverse uses the named route to build a path. The function then sends a real test-client request and returns the generated path alongside the JSON value observed by the view.

Counterexamples and common mistakes

The counterexample hardcodes a different path string instead of reversing the route name.

The counterexample hardcodes a different path string instead of reversing the route name. The generated URL and the registered route can then drift apart when configuration changes.

An unknown or negative identifier should not be silently repaired into another object’s URL. Also avoid treating an int converter as an authorization check: an attacker can supply a perfectly valid integer for an object owned by somebody else.

Counterexample: a runnable framework mistake

python

from bootstrap_lab import django_setup
django_setup(__name__)

from django.http import JsonResponse
from django.test import Client
from django.urls import path, reverse

def detail(request, role_id):
    return JsonResponse({"id": role_id})

urlpatterns = [path("roles/<int:role_id>/", detail, name="role-detail")]

def route(role_id):
    if type(role_id) is not int or role_id < 0:
        raise ValueError("nonnegative integer required")
    url = f"/different/{role_id}/"
    response = Client().get(url)
    return {"path": url, "id": response.json()["id"]}
Reasoning
The counterexample hardcodes a different path string instead of reversing the route name. The generated URL and the registered route can then drift apart when configuration changes.

Independent practice: one bounded framework change

Move this route under an api prefix and keep its name unchanged.

Move this route under an api prefix and keep its name unchanged. Update the URL configuration, then let reverse generate the new path automatically.

The view and its returned identifier should not change. Start with one identifier, then a larger value and finally an invalid boolean input. This small route move demonstrates why callers should depend on route identity rather than duplicate a path spelling.

Step 1: Move the route under /api/ without changing its name. Step 2: Keep reverse and the view independent of the old path spelling.

Practice starter: solve before reading the solution

python

from bootstrap_lab import django_setup
django_setup(__name__)

from django.http import JsonResponse
from django.test import Client
from django.urls import path, reverse

def detail(request, role_id):
    return JsonResponse({"id": role_id})

urlpatterns = [path("roles/<int:role_id>/", detail, name="role-detail")]

def route(role_id):
    # Remove this placeholder, then adapt the retained baseline:
    # Move the route under /api/ without changing its name.
    raise NotImplementedError
    if type(role_id) is not int or role_id < 0:
        raise ValueError("nonnegative integer required")
    url = reverse("role-detail", args=[role_id])
    response = Client().get(url)
    return {"path": url, "id": response.json()["id"]}
Reasoning
Move the route under /api/ without changing its name. Keep reverse and the view independent of the old path spelling.
  • Hint 1: Change the registered pattern rather than constructing URLs manually.
  • Hint 2: Check both the new path and the identifier received by the view.

Solution, tests and trade-offs

The acceptance cases compare both the generated path and the value received by the view.

The acceptance cases compare both the generated path and the value received by the view. Checking only the path text could miss a route that never resolves correctly, while checking only the returned identifier could hide an incorrect public URL.

The practice preserves validation at the helper boundary. These are routing checks on a controlled endpoint, not evidence that a real role exists or is visible to the current user.

Practice solution and acceptance checks

python

from bootstrap_lab import django_setup
django_setup(__name__)

from django.http import JsonResponse
from django.test import Client
from django.urls import path, reverse

def detail(request, role_id):
    return JsonResponse({"id": role_id})

urlpatterns = [path("api/roles/<int:role_id>/", detail, name="role-detail")]

def route(role_id):
    if type(role_id) is not int or role_id < 0:
        raise ValueError("nonnegative integer required")
    url = reverse("role-detail", args=[role_id])
    response = Client().get(url)
    return {"path": url, "id": response.json()["id"]}
Reasoning
The acceptance cases compare both the generated path and the value received by the view. Checking only the path text could miss a route that never resolves correctly, while checking only the returned identifier could hide an incorrect public URL.

Interview reasoning and next step

Explain how a route name, its captured arguments and the HTTP method describe different parts of a request.

Explain how a route name, its captured arguments and the HTTP method describe different parts of a request. Namespaces become useful when multiple apps expose similarly named routes, and query parameters require separate encoding.

A future application can change its path layout while keeping internal route names stable. The following lesson adds method restrictions so a syntactically valid path does not accidentally accept every operation.

Optional stretch — after the core task works: Introduce an application namespace and test a second route with the same local name.

Computer practice: https://applydjinn.com/typorithm/python-frameworks-lab-20261105.zip. Install the pinned dependencies in an isolated environment, run verify.py --track django, then edit the retained exercise baseline and check it with --practice dj-routing. The lab uses owned in-memory databases, local framework clients and tiny CPU tensors. It never reads the working ApplyDjinn database, contacts production feeds or downloads models.

Generate paths from named routes while keeping format checks separate from object existence and authorization.