Example: Plant shop
A layout, client-side navigation, typed query parameters, page titles and a 404 page.
- 96 lines of Python
- 5 pages
- 0 server functions
- 0 B page JS (gzip)
This example needs its server (database, sessions or server functions). Run it locally:
Terminal
pyweb dev examples/site/app.pyweb#Source
examples/site/app.pyweb
"""A small multi-page site: a shared layout, client-side navigation, search
with typed query parameters, per-page titles and descriptions, and a 404 page.
"""
from pyweb import App, NotFound, head
app = App(title="Plant shop", stylesheets=["/static/app.css"], description="Houseplants, delivered.",
base_url="https://plants.example")
PLANTS = {
1: {"name": "Monstera", "price": 35, "light": "bright", "about": "Big split leaves; forgiving and fast-growing."},
2: {"name": "Snake plant", "price": 22, "light": "low", "about": "Upright leaves; thrives on neglect."},
3: {"name": "Pothos", "price": 15, "light": "low", "about": "Trailing vines for shelves and hanging pots."},
4: {"name": "Fiddle-leaf fig", "price": 48, "light": "bright", "about": "Glossy statement tree; likes a routine."},
5: {"name": "ZZ plant", "price": 28, "light": "low", "about": "Waxy leaves that store water."},
}
@app.layout
def Shell(children):
cart = 0
def add_to_cart():
cart += 1
<nav class="site-nav">
<a href="/">Home</a>
<a href="/plants">Plants</a>
<a href="/about">About</a>
<span class="spacer"></span>
<button id="cart" onclick={add_to_cart}>Cart: {cart}</button>
</nav>
{children}
<footer class="site-footer">The cart lives in the layout, so it survives moving between pages.</footer>
@app.page("/", title="Plant shop")
def Home():
<main>
<h1>Plants for every room</h1>
<p>Browse <a href="/plants">all plants</a>, or pick by light:
<a href="/plants?light=low">low light</a>,
<a href="/plants?light=bright">bright light</a>.</p>
</main>
@app.page("/plants", title="Plants", description="Every plant we sell.")
def Plants(q: str = "", light: str = "", max_price: int = 0):
found = [(pid, p) for pid, p in PLANTS.items()
if q.lower() in p["name"].lower() and (not light or p["light"] == light)
and (not max_price or p["price"] <= max_price)]
<main>
<h1>Plants</h1>
<form>
<input name="q" value={q} placeholder="Search plants" />
<button>Search</button>
</form>
<ul id="plants">
for pid, plant in found:
<li><a href={"/plants/" + str(pid)}>{plant["name"]}</a><span class="muted">${plant["price"]}</span></li>
</ul>
if not found:
<p class="empty">Nothing matches.</p>
</main>
@app.page("/plants/{plant_id}")
def Plant(plant_id: int):
if plant_id not in PLANTS:
raise NotFound()
plant = PLANTS[plant_id]
head(title=plant["name"] + " - Plant shop", description=plant["about"])
<main>
<p><a href="/plants">All plants</a></p>
<h1>{plant["name"]}</h1>
<p>{plant["about"]}</p>
<p class="muted">${plant["price"]} · {plant["light"]} light</p>
</main>
@app.page("/about", title="About", description="Who we are.")
def About():
<main>
<h1>About</h1>
<p>We are a small shop that loves plants.</p>
</main>
@app.error(404)
def Missing(path):
<main>
<h1>Not found</h1>
<p>There is nothing at <code>{path}</code>. Try <a href="/plants">the plant list</a>.</p>
</main>
#What runs where
Output of pyweb inspect: every page variable, handler and server function, where it runs and why.
Output
page Home route=/
page Plants route=/plants
browser found constant
page Plant route=/plants/{plant_id}
browser plant constant
page About route=/about
page Missing route=None#Generated JavaScript
The browser modules the compiler wrote (before minification). They import the shared runtime.
JavaScript
// no JavaScript: these pages are static