Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions demo/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from fasthtml.common import *
from fasthtml.pico import *

def render(item:'Todo'):
id = f'todo-{item.id}'
Expand Down
3 changes: 2 additions & 1 deletion demo/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
python-fasthtml
python-fasthtml
fastlite
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Examples

This directory contains some examples of the library in action, including a sequence of to-do list apps of increasing complexity (see `todos1.py` through `todos5.py`, along with `adv_app.py`). For more examples of different applications and components, see the separate [examples repository](https://github.com/AnswerDotAI/fasthtml-example).
This directory contains some examples of the library in action, including a sequence of to-do list apps of increasing complexity (see `todos1.py` through `todos4.py`, along with `adv_app.py`). Examples that open a database also need `fastlite` (`pip install fastlite`). It is not installed with `python-fasthtml` alone. For more examples of different applications and components, see the separate [examples repository](https://github.com/AnswerDotAI/fasthtml-example).

16 changes: 9 additions & 7 deletions examples/pep8_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# This fasthtml app includes functionality from fastcore, starlette, fastlite, and fasthtml itself.
# Run with: `python adv_app.py`
from fasthtml import common as fh
from fasthtml import pico
from fastlite import NotFoundError, database
from hmac import compare_digest
from dataclasses import dataclass
from fastcore.utils import patch
Expand All @@ -24,7 +26,7 @@ class Todo:
priority: int


db = fh.database('data/todos_p8.db')
db = database('data/todos_p8.db')
users = db.create(User, pk='name')
todos = db.create(Todo)

Expand Down Expand Up @@ -71,7 +73,7 @@ def login_post(login: Login, sess):
# Indexing into a MiniDataAPI table queries by primary key, which is `name` here.
try:
u = users[login.name]
except fh.NotFoundError:
except NotFoundError:
u = users.insert(login)
if not compare_digest(u.pwd.encode("utf-8"), login.pwd.encode("utf-8")): return login_redir
# Because the session is signed, we can securely add information to it.
Expand Down Expand Up @@ -108,16 +110,16 @@ def __ft__(self: Todo):
@app.get("/")
def get(auth):
title = f"{auth}'s Todo list"
top = fh.Grid(fh.H1(title), fh.Div(fh.A('logout', href='/logout'), style='text-align: right'))
top = pico.Grid(fh.H1(title), fh.Div(fh.A('logout', href='/logout'), style='text-align: right'))
new_inp = fh.Input(id="new-title", name="title", placeholder="New Todo")
grp = fh.Group(new_inp, fh.Button("Add"))
grp = pico.Group(new_inp, fh.Button("Add"))
add = fh.Form(grp, hx_post="/", target_id='todo-list', hx_swap="afterbegin")
# In the MiniDataAPI spec, treating a table as a callable (i.e with `todos(...)` here) queries the table.
frm = fh.Form(*todos(order_by='priority'), id='todo-list', cls='sortable', hx_post="/reorder", hx_trigger="end")
# We create an empty 'current-todo' Div at the bottom of our page, as a target for the details and editing views.
card = fh.Card(fh.Ul(frm), header=add, footer=fh.Div(id='current-todo'))
card = pico.Card(fh.Ul(frm), header=add, footer=fh.Div(id='current-todo'))
# PicoCSS uses `<Main class='container'>` page content; `Container` is a tiny function that generates that.
return fh.Title(title), fh.Container(top, card)
return fh.Title(title), pico.Container(top, card)


# This is the handler for the reordering of todos.
Expand Down Expand Up @@ -146,7 +148,7 @@ def delete(id: int):
def edit(id: int):
# The `hx_put` attribute tells HTMX to send a PUT request when the form is submitted.
res = fh.Form(hx_put="/", target_id=f'todo-{id}', id="edit")(
fh.Group(fh.Input(id="title"), fh.Button("Save")),
pico.Group(fh.Input(id="title"), fh.Button("Save")),
fh.Hidden(id="id"),
fh.CheckboxX(id="done", label='Done'),
fh.Textarea(id="details", name="details", rows=10),
Expand Down
1 change: 1 addition & 0 deletions examples/todos1.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from fasthtml.common import *
from fasthtml.pico import *

app,rt,todos,Todo = fast_app('data/todos.db', id=int, task=str, done=bool, pk='id')

Expand Down
1 change: 1 addition & 0 deletions examples/todos2.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from fasthtml.common import *
from fasthtml.pico import *

id_curr = 'current-todo'
id_list = 'todo-list'
Expand Down
1 change: 1 addition & 0 deletions examples/todos3.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Run with: python basic_app.py
from fasthtml.common import *
from fasthtml.pico import *

def render(todo):
show = AX(todo.task, f'/todos/{todo.id}', 'current-todo')
Expand Down
1 change: 1 addition & 0 deletions examples/todos4.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from fasthtml.common import *
from fasthtml.pico import *
from fastlite import *

db = database('data/todos.db')
Expand Down
1 change: 1 addition & 0 deletions examples/user_app.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Run with: python user_app.py
# At signin, enter a user/pw combination and if it doesn't exist, it will be created.
from fasthtml.common import *
from fasthtml.pico import *
from fastlite import *

db = database('data/utodos.db')
Expand Down
7 changes: 6 additions & 1 deletion nbs/tutorials/by_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@
":::{.callout-tip}\n",
"## Test the POST request\n",
"\n",
"You can test the POST request with `curl -X POST http://127.0.0.1:8000 -d \"some data\"`. This sends some data to the server, you should see the response \"got a post or put request\" printed in the terminal.\n",
"You can test the POST request with `curl -X POST http://127.0.0.1:5001 -d \"some data\"`. This sends some data to the server, you should see the response \"got a post or put request\" printed in the terminal.\n",
":::\n",
"\n",
"There are a few other ways you can specify the route+method - FastHTML has `.get`, `.post`, etc. as shorthand for `route(..., methods=['get'])`, etc."
Expand Down Expand Up @@ -586,6 +586,7 @@
"outputs": [],
"source": [
"from fasthtml.common import *\n",
"from fasthtml.pico import *\n",
"css = Style(':root {--pico-font-size:90%;--pico-font-family: Pacifico, cursive;}') # <1>\n",
"app = FastHTML(hdrs=(picolink, css)) # <2>\n",
"\n",
Expand Down Expand Up @@ -1014,6 +1015,8 @@
"At the moment everyone sees all images! How do we keep some sort of unique identifier tied to a user? Before going all the way to setting up users, login pages etc., let's look at a way to at least limit generations to the user's *session*. You could do this manually with cookies. For convenience and security, fasthtml (via Starlette) has a special mechanism for storing small amounts of data in the user's browser via the `session` argument to your route. This acts like a dictionary and you can set and get values from it. For example, here we look for a `session_id` key, and if it doesn't exist we generate a new one:\n",
"\n",
"```python\n",
"import uuid\n",
"\n",
"@app.get(\"/\")\n",
"def get(session):\n",
" if 'session_id' not in session: session['session_id'] = str(uuid.uuid4())\n",
Expand Down Expand Up @@ -1610,6 +1613,8 @@
"And this will setup a connection on the route `/ws` along with a form that will send a message to the websocket whenever the form is submitted. Let's go ahead and handle this route:\n",
"\n",
"```python\n",
"from asyncio import sleep\n",
"\n",
"@app.ws('/ws')\n",
"async def ws(msg:str, send):\n",
" await send(Div('Hello ' + msg, id=\"notifications\"))\n",
Expand Down
Loading