route
@route registers a function with Drafter so that buttons, links, and
addresses can reach it. A route is not a page; it is a function that
builds and returns a Page each time someone arrives.
Syntax
@route
def some_name(state: State) -> Page: ...
@route("other-address")
def some_name(state: State) -> Page: ...
| Form | Meaning |
|---|---|
@route |
Register under the function's own name. def feed(...) becomes the address feed. |
@route("url") |
Register under a custom address instead of the function name. |
add_route(url, function) |
Do the same thing without decorator syntax. Rarely needed. |
The name becomes the address
The route registered from def feed(...) is what
Button("Feed", "feed") points at, and what the browser shows as the
page's address. The route named index is special: it builds the
front page shown when your app starts, and every app must have one.
from drafter import *
@route
def index() -> Page:
return Page([
"The front page.\n",
Button("Visit the other page", "second")
])
@route
def second() -> Page:
return Page([
"The route named second built this page.\n",
Button("Home", "index")
])
start_server()
How parameters are filled
When a route runs, Drafter fills its parameters in a fixed order:
- State comes first. If the first parameter is named
state, it receives the current state. (Drafter can usually still identify the state parameter if you name it something else, butstateis the convention and the docs use it everywhere.) - Form values bind by name. Each input component's
namemust match a parameter:TextBox("guess")fills a parameter calledguess. The parameter's type annotation controls conversion, soguess: intreceives a number. See Forms and input. - Button arguments bind by name too.
Argument("prize", "gold")attached to a button fills a parameter calledprize.
A required parameter that nothing fills stops the request with a missing parameter error; a value that cannot be converted to the annotated type stops it with a conversion error.
from drafter import *
from dataclasses import dataclass
@dataclass
class State:
record: str
@route
def index(state: State) -> Page:
return Page(state, [
"Last entry: " + state.record + "\n",
"Pet name:",
TextBox("pet_name"),
"\nAge:",
TextBox("pet_age"),
"\n",
Button("Save", "save")
])
@route
def save(state: State, pet_name: str, pet_age: int) -> Page:
state.record = pet_name + " (" + str(pet_age) + " years old)"
return index(state)
start_server(State("none yet"))
Notes
- Route names must be unique. If you define two functions with the same name, Python silently keeps only the second definition; this is Python's behavior, not Drafter's.
- Routes are ordinary functions. You can call one from another
(
return index(state)) and call them directly in tests (assert_state(save(State("x"), "Ada", 4), State("Ada (4 years old)"))). - Custom addresses from
@route("...")are normalized before matching: extra slashes are trimmed, and in the most heavily cleaned form, only letters, numbers, and underscores are kept. Buttons and links can target the custom address string. - Advanced injection (L4): parameters named
_server,_configuration, or_requestreceive framework objects instead of form values. You will not need these outside of the advanced material in Extend.
Related
- Page: what a route returns.
- Routes and pages: the concept.
- Add and connect pages: the how-to.
- Link or button points to an unknown route: the most common routing mistake.