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:
1 from ludic.attrs import Attrs 2 3 class PersonAttrs(Attrs): 4 id: str 5 name: str 6 email: str 7 active: bool 8 9 class PeopleAttrs(Attrs): 10 people: list[PersonAttrs]
Now we can use these attributes in our class-based endpoint:
1 from typing import override 2 3 from ludic.catalog.tables import Table, TableRow 4 from ludic.catalog.buttons import ButtonDanger 5 from ludic.components import Component 6 from ludic.web import Endpoint, LudicApp 7 8 from your_app.attrs import PersonAttrs 9 from your_app.database import db 10 11 app = LudicApp() 12 13 @app.endpoint("/people/{id}") 14 class PersonRow(Endpoint[PersonAttrs]): 15 @classmethod 16 def delete(cls, id: str) -> None: 17 try: 18 db.people.pop(id) 19 except KeyError: 20 raise NotFoundError("Person not found") 21 22 @override 23 def render(self) -> TableRow: 24 return TableRow( 25 self.attrs["name"], 26 self.attrs["email"], 27 "Active" if self.attrs["active"] else "Inactive", 28 ButtonDanger( 29 "Delete", 30 hx_delete=self.url_for(PersonRow), 31 classes=["small"] 32 ), 33 )
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:
1 from typing import override 2 3 from ludic.catalog.tables import Table, TableRow 4 from ludic.catalog.buttons import ButtonDanger 5 from ludic.components import Component 6 from ludic.web import Endpoint, LudicApp 7 8 from your_app.attrs import PeopleAttrs 9 from your_app.database import db 10 11 # ... the PersonRow class is omitted here 12 13 @app.endpoint("/people/") 14 class PeopleTable(Endpoint[PeopleAttrs]): 15 styles = { 16 "tr.htmx-swapping td": { 17 "opacity": "0", 18 "transition": "opacity 1s ease-out", 19 } 20 } 21 22 @classmethod 23 def get(cls) -> Self: 24 return cls(people=[person.dict() for person in db.people.values()]) 25 26 @override 27 def render(self) -> Table[TableHead, PersonRow]: 28 return Table( 29 TableHead("Name", "Email", "Active", ""), 30 *(PersonRow(**person) for person in self.attrs["people"]), 31 body_attrs=HtmxAttrs( 32 hx_confirm="Are you sure?", 33 hx_target="closest tr", 34 hx_swap="outerHTML swap:1s", 35 ), 36 classes=["text-align-center"], 37 )
We created a class-based endpoint with the following methods:
get– handles the GET request which returns an instance of thePeopleTablefilled with a list of people fetched from database.render– handles rendering of the table of people. We use a specialbody_attrsattribute to configure HTMX operations on thetbodyelement.
