Routes and pages
In one sentence
A route is a function that returns a page: when a visitor goes
somewhere on your site, Drafter runs the matching route function and
shows the Page it returns.
The idea
Marking a function with @route registers it as a route: a
function Drafter will call whenever it needs that part of your site.
Three rules govern how that works:
- The function's name names the page it builds. The route called
menubuilds themenupage, and it gets a matching web address. You never write the address yourself; the name is enough. indexis the front door. The route namedindexbuilds your site's main page, the one visitors see first. Every Drafter site needs one.- A route returns a
Page. ThePageholds a list of content: strings, buttons, images, form fields. Whatever the route returns is what the visitor sees next.
The distinction matters: the route is not the page, the way a recipe is
not a cake. The route is the recipe, and it bakes a fresh Page every
time it runs. That is why the same route can produce different pages as
your state changes.
Pages connect through their content. A Button("Play", "play")
or a Link("About this site", "about") names the route to go to.
Clicking runs that route, and the Page it returns replaces the current
one. Navigation is nothing more than functions naming other functions.
See it
Here is a three-page site. Every page can reach the other two, so you can walk in a circle:
from drafter import *
@route
def index() -> Page:
return Page([
"This is the first page.\n",
Button("To the second page", "second"),
Button("To the third page", "third")
])
@route
def second() -> Page:
return Page([
"This is the second page.\n",
Button("To the third page", "third"),
Button("Back to the start", "index")
])
@route
def third() -> Page:
return Page([
"This is the third page.\n",
Button("Back to the start", "index")
])
start_server()
These routes take no state because this site remembers nothing; a route
only needs the state parameter if it uses it. Notice there is no special
"go back" machinery: going back is a button pointing at the page you came
from. The browser's back button also works, and
State explains what it does to your data.
Drafter checks every connection before showing a page. If a button names a route that does not exist, you get an error saying so, naming the button and the missing page, instead of a dead button:
Link `Play` points to non-existent page `play`.
What this means for your code
- Name routes the way you would name pages:
menu,results,game_over. The name appears in the address bar, so make it presentable. - Every route returns a
Page; everyPagecontent item is in a list, even a single string. - Each button or link names its target route as a string:
Button("Play", "play"). - If several pages share content (a title, a footer), write a helper function that returns those components and call it from each route.
- A route that no button, link, or test reaches is dead code; delete it or connect it.
Where people get confused
- "Where do I write the URL?" You do not. The function name is the
address.
@routehandles the mapping. - "Can two routes share a name?" No; each name belongs to exactly one route. Defining two functions with the same name is also a Python mistake: the second definition quietly replaces the first.
- "My button does nothing": a button always does something; it either navigates or produces an error. If the page will not display, read the error text; it usually names a missing or misspelled route.
- "Which route runs first?"
indexalways runs first. The order you define routes in the file does not matter to visitors, only to readers of your code, so keepindexfirst for their sake.
Go deeper
- Add and connect pages has recipes for linking pages together.
- State explains what travels between your pages.
- Dynamic pages shows how one route can render many different pages.
- route (reference) and Page (reference) document exact behavior and parameters.