Named URLs and reverse()
The cookbook links to recipe pages from the home page, the search results, emails and a redirect after saving a form. If each link is typed as "/recipes/" + slug + "/", then the day someone moves recipes to a new URL, you hunt through the whole project for strings. Named URLs make the pattern list the only place an address is written.
The rule
Name the pattern once; build every link from the name.
Give the app a namespace and each pattern a name:
# recipes/urls.py
app_name = "recipes"
urlpatterns = [
path("", views.recipe_list, name="list"),
path("<slug:slug>/", views.recipe_detail, name="detail"),
]The full name is namespace:name, here recipes:detail. The namespace keeps it apart from a detail in another app, such as reviews:detail.
In templates, the url tag builds the address:
<a href="{% url 'recipes:detail' recipe.slug %}">{{ recipe.title }}</a>
<a href="{% url 'recipes:detail' slug=recipe.slug %}">same link, by keyword</a>In Python, reverse() does the same job:
from django.shortcuts import redirect
from django.urls import reverse
reverse("recipes:list") # "/recipes/"
reverse("recipes:detail", kwargs={"slug": "tomato-soup"}) # "/recipes/tomato-soup/"
return redirect("recipes:detail", slug=recipe.slug) # after saving a formMatching goes URL → view. Reversing goes the other way: name + arguments → URL. It includes the prefix from include(), so the app never needs to know where it is mounted.
One wrinkle: a class attribute is evaluated when the module is imported, before the URL patterns are loaded. There, use reverse_lazy("recipes:list"), which waits until the value is actually used.
If the name is misspelled or a required argument is missing, Django raises NoReverseMatch. That is a feature: a broken link fails loudly during development instead of silently sending readers to a 404.
The shortcut
Whenever you are about to type a / inside a link or redirect, stop and write the name instead. A hard-coded path in a template or view is a bug waiting for the next URL change.
Change one route
path("view/<slug:slug>/", views.recipe_detail, name="detail").
Drills
Answer fast — trust the rule.
With app_name = "recipes" and name="detail", how do you refer to the pattern?
What does reverse("recipes:detail", kwargs={"slug": "ragu"}) return?
A template uses {% url 'recipes:detail' %} but the pattern needs a slug. What happens?
Where should you use reverse_lazy() instead of reverse()?
Remember
- Name the pattern once; build every link from the name.
{% url %}in templates,reverse()orredirect()in Python,reverse_lazy()in class attributes.- Trap: typing
/into a link instead of a name.
