Skip to content

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.

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