Delete Row
This example shows how to implement a delete button that removes a table row upon completion. For this example, we use have a sample database of people which we display in a table. Each row in the table can be deleted by clicking a button and a confirmation prompt.
Demo
#Implementation
#Since we want to display people as rows in a table, we can create a dedicated class-based endpoint which can render itself like a component. Before we do that, we also need to declare what kind of data this endpoint handles:
from ludic.attrs import Attrs class PersonAttrs(Attrs): id: str name: str email: str active: bool class PeopleAttrs(Attrs): people: list[PersonAttrs]
Now we can use these attributes in our class-based endpoint:
from typing import override from ludic.catalog.tables import Table, TableRow from ludic.catalog.buttons import ButtonDanger from ludic.components import Component from ludic.web import Endpoint, LudicApp from your_app.attrs import PersonAttrs from your_app.database import db app = LudicApp() @app.endpoint("/people/{id}") class PersonRow(Endpoint[PersonAttrs]): @classmethod def delete(cls, id: str) -> None: try: db.people.pop(id) except KeyError: raise NotFoundError("Person not found") @override def render(self) -> TableRow: return TableRow( self.attrs["name"], self.attrs["email"], "Active" if self.attrs["active"] else "Inactive", ButtonDanger( "Delete", hx_delete=self.url_for(PersonRow), classes=["small"] ), )
We created a class-based endpoint with the following methods:
delete
– handles the DELETE request which removes a person from the database. We don't need to return anything in this case.render
– handles rendering of the table. Apart from the textual columns, we also render an action button to delete a row which is hooked to thePersonRow.delete()
method.
The last remaining part is the table with people itself. We can create another class-based endpoint for this task:
from typing import override from ludic.catalog.tables import Table, TableRow from ludic.catalog.buttons import ButtonDanger from ludic.components import Component from ludic.web import Endpoint, LudicApp from your_app.attrs import PeopleAttrs from your_app.database import db # ... the PersonRow class is omitted here @app.endpoint("/people/") class PeopleTable(Endpoint[PeopleAttrs]): styles = { "tr.htmx-swapping td": { "opacity": "0", "transition": "opacity 1s ease-out", } } @classmethod def get(cls) -> Self: return cls(people=[person.dict() for person in db.people.values()]) @override def render(self) -> Table[TableHead, PersonRow]: return Table( TableHead("Name", "Email", "Active", ""), *(PersonRow(**person) for person in self.attrs["people"]), body_attrs=HtmxAttrs( hx_confirm="Are you sure?", hx_target="closest tr", hx_swap="outerHTML swap:1s", ), classes=["text-align-center"], )
We created a class-based endpoint with the following methods:
get
– handles the GET request which returns an instance of thePeopleTable
filled with a list of people fetched from database.render
– handles rendering of the table of people. We use a specialbody_attrs
attribute to configure HTMX operations on thetbody
element.