Python Django Tutorial: Full-Featured Web App Part 2 - Applications and Routes
Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
A Django project can contain multiple apps, and each app can be routed independently through URL configuration.
Briefing
Django’s routing model lets a single project host multiple independent apps—then stitches them together through URL configuration—so adding a blog becomes mostly a matter of wiring URL patterns to view functions. The practical payoff is immediate: once the blog app defines its own views and URL patterns, the main project can direct requests like `/blog/` and `/blog/about/` to the right code, without duplicating logic across the site.
The workflow starts by creating a new app inside an existing Django project. A project is the top-level container (with `manage.py` and project settings), while each app owns its own functionality and file structure. Using `python manage.py startapp blog` generates a `blog/` directory with the standard Django app layout. Even before writing templates or complex logic, the app can already serve pages by defining a view.
Inside `blog/views.py`, a `home(request)` view is added. It returns an `HttpResponse` containing a simple HTML `<h1>` message (“blog-home”), establishing the basic pattern: each route maps to a Python function that accepts a `request` argument and returns a response. A second view, `about(request)`, is later added the same way, returning “blog about” in an `<h1>` tag.
Next comes URL routing within the app. A new `blog/urls.py` file defines URL patterns using Django’s `path()` helper. The blog homepage is mapped with an empty string path (`''`) to `views.home`, and it’s given the name `blog-home` to avoid collisions with other apps that might also define a generic `home` route. An additional pattern maps `about/` to `views.about` and names it `blog-about`. The app-level `urls.py` mirrors the project-level approach but only concerns itself with routes *inside* the blog.
To connect the app to the overall site, the project’s `urls.py` imports `include` and adds a pattern that forwards the `/blog/` prefix to the blog app’s URLconf: `path('blog/', include('blog.urls'))`. Django’s `include()` mechanism “chops off” the matched prefix (`blog/`) and passes only the remaining path to `blog.urls`. That’s why navigating to `/blog/about/` results in the `about` pattern matching inside `blog/urls.py`, which then calls `views.about`.
Testing in the browser confirms the behavior: hitting the site root initially returns a 404 until routes exist; `/blog/` returns the “blog home” HTML; `/blog/about/` returns the “blog about” HTML. The tutorial also emphasizes why trailing slashes matter: patterns like `blog/` and `about/` trigger Django’s automatic redirects so users don’t end up on inconsistent URLs.
Finally, the routing setup can be adjusted so the blog becomes the site’s home page. By changing the project-level URL pattern to include the blog URLs at an empty path, the root URL (`/`) serves `blog-home`, and `/about/` serves `blog-about`. The core takeaway is that routing is centralized and composable: change one line in the project URLconf to move an entire app’s routes, while the app’s internal views and URL patterns remain untouched.
Cornell Notes
A Django project can host multiple apps, and each app can define its own views and URL patterns. Creating a `blog` app adds a place to write view functions like `home(request)` and `about(request)` that return `HttpResponse` objects. The app’s `blog/urls.py` maps paths such as `''` (homepage) and `about/` to those views, using names like `blog-home` and `blog-about` to avoid collisions. The project-level `urls.py` then uses `include()` to route requests with a prefix (e.g., `path('blog/', include('blog.urls'))`) into the app’s URLconf. Changing the prefix in the project URLconf can make the blog the site’s root without modifying the blog app itself.
Why does Django use both a project-level `urls.py` and an app-level `urls.py`?
How does `include()` affect the path that the app-level URLconf receives?
What’s the role of naming URL patterns like `name='blog-home'`?
Why add trailing slashes to patterns such as `path('about/', ...)` and `path('blog/', ...)`?
How can the blog become the site’s root page without changing the blog app code?
Review Questions
- If a request goes to `/blog/about/`, list the exact sequence of URLconf checks from project-level to app-level and identify which view function runs.
- What would break (or change) if the blog homepage pattern in `blog/urls.py` were not `''` but something else? Explain the resulting URL you’d need to visit.
- How does adding `path('blog/', include('blog.urls'))` differ from adding `path('', include('blog.urls'))` in terms of the URLs users must type?
Key Points
- 1
A Django project can contain multiple apps, and each app can be routed independently through URL configuration.
- 2
Creating a `blog` app with `python manage.py startapp blog` sets up the app structure needed for views and URL patterns.
- 3
View functions like `home(request)` and `about(request)` return `HttpResponse` objects and act as the endpoints for routes.
- 4
App-level routing in `blog/urls.py` maps paths (e.g., `''` and `about/`) to view functions using `path()` and can assign unique names like `blog-home`.
- 5
Project-level routing uses `include()` to forward a URL prefix (e.g., `blog/`) into the app’s URLconf.
- 6
Django’s `include()` strips the matched prefix before matching the remaining path inside the included URL patterns.
- 7
Changing only the project-level prefix can move an entire app’s routes (including making the blog the site root) without altering the app’s views or internal URL patterns.