URL Routing
The taskflow app exists and is registered, but Django has no idea yet which web
addresses should reach it — that's the job of URL routing. In this lesson we connect the
project's root routing file to the taskflow app's own routes, so visiting a URL
like /tasks/ actually reaches TaskFlow's code.
The Project's Root urls.py
Recall from Project Structure Deep Dive that
ROOT_URLCONF points Django at taskflow_project/urls.py for every
incoming request. Fresh out of startproject, that file looks like this:
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
Every entry in the urlpatterns list matches an incoming URL against a pattern and,
on a match, hands the request off to something — here, Django's built-in admin site. We
need to add an entry that sends anything starting with tasks/ into the
taskflow app.
Delegating to an App with include()
Rather than listing every one of TaskFlow's individual routes directly inside the project's
urls.py, Django's include() function lets us delegate an entire prefix
to the app's own URL configuration file:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('tasks/', include('taskflow.urls')),
]
Reading this line by line: any URL beginning with tasks/ gets handed off to
whatever urlpatterns are defined inside taskflow/urls.py, with the
tasks/ prefix already consumed. This keeps the project's root file short and stable
even as TaskFlow grows dozens of routes, and it keeps every route related to tasks physically
grouped inside the taskflow app itself, right next to the views that handle them.
Creating the App's Own urls.py
As mentioned in the previous lesson, startapp does not generate a
urls.py file automatically — we create it ourselves. Add a new file at
taskflow/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.task_list, name='task-list'),
]
This single entry says: an empty path (which, remember, sits after the tasks/
prefix already consumed by the project's urls.py — so the full URL is actually
/tasks/) should be handled by a view function named task_list, imported
from this app's own views.py. We'll write that task_list view itself in
Function-Based Views, the lesson after next.
path() vs. re_path(): Why Modern Django Prefers path()
Older Django code (and some tutorials still floating around) route URLs using regular
expressions via a function called re_path(). Modern Django favors path()
for the overwhelming majority of routes, because it uses simple, readable converters
(like <int:pk> or <str:slug>) instead of regex syntax. We'll
use these converters to build routes like a single task's detail page in the very next lesson,
Dynamic URLs & Parameters.
re_path() still exists and is still valid Django, reserved for the smaller set of
cases that genuinely need full regular expression matching — an unusual URL shape that
path()'s converters can't express. For TaskFlow, and for the vast majority of real
projects, path() covers essentially everything you'll need.
Naming URL Patterns with name=
Notice the name='task-list' argument in the app's urls.py above. Giving
a URL pattern a name lets the rest of the codebase — views, and especially templates —
refer to that URL by name instead of hardcoding its actual path. This matters more than
it might sound: if we ever changed TaskFlow's task list from /tasks/ to something
like /tasks/all/, every template using a hardcoded href="/tasks/" link
would silently break, while every template referencing it by the name task-list
would keep working without a single change. We'll put this to direct use with the
{% url %} template tag once we reach
Django Template Language.
App Namespacing for Larger Projects
As a project grows to include multiple apps, two different apps could end up with a
same-named URL pattern — both taskflow and a hypothetical billing
app might each want a pattern named list. Django solves this with an
app namespace, declared right inside the app's own urls.py:
from django.urls import path
from . import views
app_name = 'taskflow'
urlpatterns = [
path('', views.task_list, name='task-list'),
]
With app_name set, the same URL is referenced elsewhere as
taskflow:task-list, keeping it unambiguous no matter how many other apps TaskFlow
eventually grows to include. For a single-app project this isn't strictly necessary, but it's a
cheap habit to build early, and one you'll see used throughout larger real-world Django codebases.