Your own HTML pages with Python behind them: routes, pages built from data, forms, saving to a file and JSON, ending with the same API in FastAPI.
Each lesson works two ways. Students can work through the online tutorial at their own pace (it checks their answers and saves their progress in their browser), or you can teach it to the whole class from the slides, then send students into the tutorial for the Modify and Make steps.
Every lesson guide has plain-language notes, the answers to every check, a solution for every task, and slides and a worksheet for Waypoints Prepare. These guide pages are not linked from student pages, but they are not secret either: keep printed Teacher editions for answers you do not want students to find.
Whole-course cheat sheet: Open to print (every command, grouped by unit: a handout for the final challenge)
Routes join addresses to functions; templates build pages from data.
A web app makes its pages with code. The browser asks for an address (/about); a program on the server decides what to send back. In Flask, a route joins an address to a function: @app.route("/about") above def about():. Whatever the function returns, usually HTML text, is the page. An address with no route gets the standard 404 Not Found.
A template is HTML with gaps, filled in from data: {{ p["name"] }} shows a value and {% for p in players %} ... {% endfor %} repeats a part for each item. render_template_string(PAGE, players=players) fills it in; the template sees only what is passed to it. A route can have a part that changes: /player/<name> passes whatever is in that place to the function.
A restaurant: the address is the order, the route is the menu line that says which cook makes it, and the function is the cook. A template is a form letter with blanks. It breaks because the "cook" answers instantly and identically every time, and forgets everything between orders unless it writes it down (Lesson 4).
/about, then /missing./player/Zoe and read the reply.python app.py serves it at http://127.0.0.1:5000, and hosting it for others needs a server (a teacher job).templates folder, loaded with render_template("club.html"). The {{ }} and {% %} work the same.Cheat sheet: Open to print (A4, with a picture of what each command draws) · Markdown
Take answers from a form, check them, and save them to a file so they last.
Forms. <form method="post" action="/join"> sends the form to /join when the button is pressed. Only boxes with a name attribute are sent, under that name. A route with methods=["POST"] reads them: request.form["name"]. After changing the data, redirect("/") sends the browser back to a page that shows the change. The server checks what it gets (a blank name, a repeat), because anyone can send anything to an address.
Saving. Variables last only while the app runs. Data that must last goes in a file: each sign-up adds a row to members.csv with open(..., "a") and csv.writer, and every page reads the file with csv.DictReader. "a" adds to the end; "w" empties the file first. Changing one row means reading every row, changing it, and writing them all back with "w", header first.
A sign-up sheet on a clipboard: the form is the sheet, the route is the person who reads it and copies each name into the club book (the file). It breaks because the person never guesses: a box without a name is invisible to them, and they will happily copy a blank line unless told to check.
members.csv in the Files tab.name="name" from the input in Lesson 3 and press Join: 400 Bad Request.app.py.Cheat sheet: Open to print (A4, with a picture of what each command draws) · Markdown
Send data as JSON for other programs, then the same API in FastAPI.
A page is for people; an API is for programs. An API route sends data, usually as JSON: in Flask, a route that returns a dictionary sends it as JSON, which almost any language reads straight back into a dictionary or list. A query string (/api/members?name=ad) asks a question; Flask reads it with request.args.get("name", "").
FastAPI (the optional last lesson) builds APIs with types: def next_year(age: int): makes FastAPI read age from the address, turn it into a whole number, and reply 422 with an explanation when it cannot, before your code runs. Without the type, the value would be text. Routes are @api.get("/address"), the changing part of an address is {group}, and uvicorn.run(api) starts it.
A library's catalogue terminal: you ask a question in a fixed form and get a printed list back, not a decorated poster. FastAPI's types are the librarian who refuses "twelve" in a box marked "number". It breaks because an API answers anyone who asks, so what it shares must be chosen carefully.
/api/members/Players./api/members/Nobody: an empty answer, not an error./next-year?age=twelve and read the 422 reply.Cheat sheet: Open to print (A4, with a picture of what each command draws) · Markdown