Registering Admin Views¶
Turning a SQLAlchemy model into a full CRUD screen (index/show/new/edit/ delete) -- this is the core "declare a model, get a full admin panel" leverage kloudmint gives you.
1. Basic view¶
# admin_views.py
from kloudmint import RBACModelView
from models import Product
class ProductAdmin(RBACModelView, model=Product):
name_plural = "Products"
icon = "fa-solid fa-box"
column_list = [Product.id, Product.name, Product.price]
column_searchable_list = [Product.name]
form_columns = [Product.name, Product.price]
That's the entire config needed for:
- an index page with a searchable, paginated table
- a "New Product" form with a name text field and a price number field
- edit / view-details / delete actions on each row
- an "Export" button
2. Register it with the engine¶
# main.py
admin.register(ProductAdmin)
Registering also feeds the automatic Permission dropdown -- see RoleAndPermissionManagement.md.
3. Common column options¶
| Attribute | What it does |
|---|---|
column_list |
Columns shown on the index table |
column_searchable_list |
Columns the search box filters on |
column_sortable_list |
Columns clickable to sort by |
form_columns |
Fields shown on the New/Edit forms |
form_overrides |
Swap a field's widget (e.g. PasswordField for a password column) |
form_args |
Extra kwargs for a field (e.g. {"choices": [...]} for a dropdown) |
can_export |
Set False to hide the Export button for this view |
4. Handling a field that needs transformation before save (e.g. a password)¶
Use on_model_change, which runs right before the insert/update:
from wtforms import PasswordField
from kloudmint import RBACModelView
from models import User
class UserAdmin(RBACModelView, model=User):
form_columns = [User.email, User.password_hash, User.role]
form_overrides = {"password_hash": PasswordField}
form_args = {"password_hash": {"label": "Password"}}
async def on_model_change(self, data: dict, model, is_created: bool, request) -> None:
raw_password = data.get("password_hash")
if raw_password:
temp = User()
temp.set_password(raw_password)
data["password_hash"] = temp.password_hash
elif is_created:
raise ValueError("Password is required")
else:
data.pop("password_hash", None) # editing without touching password -- keep existing hash
See examples/basic_app/admin_views.py
for the complete pattern.
5. Displaying a related record legibly¶
If column_list includes a foreign-key relationship (e.g.
Order.customer), define __str__ on the related model -- otherwise the
column renders as a raw Python object repr instead of a name:
class Customer(Base):
...
def __str__(self) -> str:
return self.full_name