Allow a larger group.
Before
validate_max=4After
validate_max=6Enter five guests: the nightly price becomes €165, the total €495 and the deposit €149. Seven guests are still rejected.
Download this edited Page →A form with relationships worth understanding
A room, a few guests, a longer stay. Seven formulas turn three choices into a detailed estimate. Follow the dependencies, then change a rule yourself.
Recorded from the real Python page. Silent, with optional step captions and a cursor highlight.
Plan your stay
Illustrative prices. This example calculates an estimate; it does not reserve a room or take payment.
What depends on what?
Each formula names its inputs and writes its result to Data. The next formula can use that result. The page needs no separate change handler on every field.
Superior €140 + one extra guest €25 = €165.
€165 × 7 nights = €1,155.
Seven nights earn €115.50 off. Total: €1,039.50.
30% of the total, rounded to euros: €312.
Change seven nights to six: the discount disappears, the total becomes €990, and the deposit becomes €297. One edit reaches every dependent result.
Validation happens before accepted Data changes. An empty field, five guests or 31 nights is rejected; the visible draft stays in the field while the calculation keeps using the previous accepted values.
Inspect · edit Python · reload
Follow the real Data values in the inspector. Change the discount threshold in the Python file from seven to five nights, save, then reload and compare the same inputs.
Real Python gallery recordings, with the inspector enlarged and cursor highlighted. The before/after code comparison comes from the saved Python files. Silent, with optional captions. Use fullscreen on smaller screens.
nights >= 7 to nights >= 5 in the discount formula. Update the accompanying explanatory text, save, and let the development server reload.The inspector reads Data throughout. The formula is changed in the Python file and loaded with the new page; it is not edited or activated in live Source.
Make the same Python change yourself →The complete Python page
42 nonblank Python lines, including the class, fields, seven formulas, breakdown and controller. The short expressions run as JavaScript in the browser. CSS and host setup are provided separately.
from gramlot import Page as BasePage
class Page(BasePage):
title = "Plan your stay"
devices = ("desktop", "tablet", "phone")
css = ("/themes/gramlot-base/theme.css",)
def main(self, root):
pane = root.main(datapath="estimate", class_="estimate-page")
pane.h1("Plan your stay")
pane.p("Choose a room, guests and nights. Follow the calculation below.")
fields = pane.formlet(columns=1, lbl_position="TL", box_c_width="100%")
fields.filteringSelect(value="^.room", lbl="Room", id="room",
values="classic:Classic,superior:Superior", validate_notnull=True)
fields.numberTextBox(value="^.guests", lbl="Guests", id="guests", dtype="L",
validate_notnull=True, validate_min=1, validate_max=4)
fields.numberTextBox(value="^.nights", lbl="Nights", id="nights", dtype="L",
validate_notnull=True, validate_min=1, validate_max=30)
pane.dataFormula(".rate", "room === 'superior' ? 140 : 90", room="^.room", _init=True)
pane.dataFormula(".extra", "Math.max(guests - 2, 0) * 25", guests="^.guests", _init=True)
pane.dataFormula(".nightly", "rate + extra", rate="^.rate", extra="^.extra", _init=True)
pane.dataFormula(".subtotal", "nightly * nights",
nightly="^.nightly", nights="^.nights", _init=True)
pane.dataFormula(".discount", "nights >= 7 ? subtotal * 0.10 : 0",
nights="^.nights", subtotal="^.subtotal", _init=True)
pane.dataFormula(".total", "subtotal - discount",
subtotal="^.subtotal", discount="^.discount", _init=True)
pane.dataFormula(".deposit", "Math.round(total * 0.30)", total="^.total", _init=True)
costs = pane.section(class_="estimate-costs", aria_label="Calculation breakdown")
costs.p("^.rate", mask="Room / night · €{}", id="rate")
costs.p("^.extra", mask="Extra guests / night · €{}", id="extra")
costs.p("^.nightly", mask="Nightly price · €{}", id="nightly")
costs.p("^.subtotal", mask="Stay subtotal · €{}", id="subtotal")
costs.p("^.discount", mask="Long-stay discount · −€{}", id="discount")
costs.p("^.total", mask="Stay total · €{}", id="total")
costs.p("^.deposit", mask="30% deposit · €{}", id="deposit")
pane.button("Prepare summary", id="prepare").dataController(
"this.SET('.summary', 'Stay estimate: €' + total)", total="=.total")
pane.p("^.summary", id="summary", aria_live="polite")
pane.p("Two guests included; €25 per extra guest/night. Seven nights: 10% off. Deposit rounded to euros.")
pane.p("Invalid drafts do not change accepted Data. Summary updates on click. Illustrative prices only.")
pane.dataSetter(".room", "classic")
pane.dataSetter(".guests", 2)
pane.dataSetter(".nights", 3)
The separate stylesheet controls appearance. app.py activates the standard input/layout collections and the Python host. Both are in the ZIP; the line count refers to the Page, not the installation.
^ makes formula inputs reactive. The controller reads =.total when its button fires: changing a field updates the calculation, while the prepared summary stays until the next click.
Make the next change without an LLM
Three independent exercises. Start from Classic, two guests and three nights. Edit one rule, save and refresh. Update the explanatory text too when it repeats a changed price or threshold.
Before
validate_max=4After
validate_max=6Enter five guests: the nightly price becomes €165, the total €495 and the deposit €149. Seven guests are still rejected.
Download this edited Page →Before
nights >= 7 ? subtotal * 0.10 : 0After
nights >= 5 ? subtotal * 0.10 : 0Enter five nights: the €450 subtotal now earns €45 off. Total: €405. Deposit: €122. No other formula needs to change.
Download this edited Page →Before
Math.max(guests - 2, 0) * 25After
Math.max(guests - 2, 0) * 15Enter three guests: the nightly price becomes €105, the total €315 and the deposit €95. The downstream formulas use the new result.
Download this edited Page →Run and modify it locally
Unzip the project and open its folder in a terminal. Python 3.11 or later is required.
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
uvicorn app:application --reload --reload-dir pagesOpen http://127.0.0.1:8000/. Edit pages/index.py for the exercises. The project also contains runnable copies at /guest_limit, /discount and /supplement. No packages are pinned; the dependencies provide the framework and the standard collection bundles.
With or without an LLM
The value is not just fewer lines. It is being able to find a business rule, understand which results depend on it, and make the next change without asking someone else to reconstruct the application.