Py.Cafe

maartenbreddels/

fasthtml-todo

Todo List Management App - FastHTML Example

DocsPricing
  • _multiprocessing.py
  • app.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# TODO: this should happen automatically in the future
import jc.common
jc.common.patch_anyio()


from fasthtml.common import *

def render(todo):
    show = AX(todo.title, f'/todos/{todo.id}', 'current-todo')
    edit = AX('edit',     f'/edit/{todo.id}' , 'current-todo')
    dt = ' (done)' if todo.done else ''
    return Li(show, dt, ' | ', edit, id=f'todo-{todo.id}')

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

@rt("/")
def get():
    inp = Input(id="new-title", name="title", placeholder="New Todo")
    add = Form(Group(inp, Button("Add")), hx_post="/", target_id='todo-list', hx_swap="beforeend")
    card = Card(Ul(*todos(), id='todo-list'), header=add, footer=Div(id='current-todo')),
    # TODO: can fasthtml do this automatically?
    #   this will cause all urls, like '/' to be prefixed by the base url / root_path
    # following https://stackoverflow.com/questions/69456875/how-to-configure-base-url-for-all-requests-using-htmx
    script =  Script("""
                document.body.addEventListener('htmx:configRequest', (event) => {
                    event.detail.path = `""" +jc.asgi.ROOT_PATH +"""${event.detail.path}`
                })
                """)
    return Titled('Todo list', card, script)

@rt("/")
def post(todo:Todo):
    return todos.insert(todo), Input(id="new-title", name="title", placeholder="New Todo", hx_swap_oob='true')

@rt("/edit/{id}")
def get(id:int):
    res = Form(Group(Input(id="title"), Button("Save")),
        Hidden(id="id"), CheckboxX(id="done", label='Done'),
        hx_put="/", target_id=f'todo-{id}', id="edit")
    return fill_form(res, todos[id])

@rt("/")
def put(todo: Todo): return todos.upsert(todo), clear('current-todo')

@rt("/todos/{id}")
def get(id:int):
    todo = todos[id]
    btn = Button('delete', hx_delete=f'/todos/{todo.id}', target_id=f'todo-{id}', hx_swap="outerHTML")
    return Div(Div(todo.title), btn)

@rt("/todos/{id}")
def delete(id:int):
    todos.delete(id)
    return clear('current-todo')
requirements.txt
1
2
3
4
5
6
7
8
9
python-fasthtml

# if we do a fasthtml tile, we can by default install sqlite3 (unvendored by pyodide)
sqlite3

# these dependencies seem to be 'optional', so we can mock them it seems, we could do this if we have a special fasthtml tile
httptools==0.5.0 # mock
uvloop==0.15.2 # mock
watchfiles==0.14 # mock
_multiprocessing.py
1