# myproject/urls.py — the project's root URL configuration
urlpatterns = [
path("admin/", admin.site.urls),
path("orders/", include("orders.urls")), # delegate everything under /orders/ to the app
]
# orders/urls.py — the app's own URL configuration
urlpatterns = [
path("", views.order_list, name="order-list"),
path("<int:pk>/", views.order_detail, name="order-detail"),
path("<int:pk>/edit/", views.order_edit, name="order-edit"),
]
include() is what lets an app own its own URL namespace, keeping the root urls.py a short table of contents rather than one giant flat list. Path converters (<int:pk>, <slug:slug>, <str:name>) both extract the value from the URL and validate/convert its type — <int:pk> on /orders/abc/ doesn't match at all (falls through to a 404) rather than passing the string "abc" through to the view.
reverse() — never hardcode a URL string# In a template
<a href="{% url 'order-detail' order.pk %}">View order</a>
# In Python code
from django.urls import reverse
url = reverse("order-detail", args=[order.pk])
Referencing a URL by its name (set in path(..., name="order-detail")) rather than hardcoding the string /orders/5/ means the actual URL structure can change in one place (urls.py) without breaking every template and redirect that links to it — this indirection is the entire point of naming URL patterns, and hardcoded URL strings scattered through templates are a common code-review flag.