Skip to content

Working with Custom Views

Basic example

You might need to add custom views to the existing SQLAdmin-NG views, for example to create dashboards, show custom info or add new forms.

To add custom views to the Admin interface, you can use the BaseView included in SQLAdmin-NG. Here's an example to add custom views:

Example

```python from sqladmin import BaseView, expose

class ReportView(BaseView): name = "Report Page" icon = "fa-solid fa-chart-line"

@expose("/report", methods=["GET"])
async def report_page(self, request):
    return await self.templates.TemplateResponse(request, "report.html")

admin.add_view(ReportView) ```

This will assume there's a templates directory in your project and you have created a report.html in that directory.

If you want to use a custom directory name, you can change that with:

```python from sqladmin import Admin

admin = Admin(templates_dir="my_templates", ...) ```

Now visiting /admin/report you can render your report.html file.

It is also possible to use the expose decorator to add extra endpoints to a ModelView. The path is in this case prepended with the view's identity, in this case /admin/user/profile/{pk}.

Example

```python from sqladmin import ModelView, expose

class UserView(ModelView):

@expose("/profile/{pk}", methods=["GET"])
async def profile(self, request):
    user: User = await self.get_object_for_edit(request)
    return await self.templates.TemplateResponse(
        request, "user.html", {"user": user}
    )

admin.add_view(UserView) ```

Database access

The example above was very basic and you probably want to access database and SQLAlchemy models in your custom view. You can use sessionmaker the same way SQLAdmin-NG is using it to do so:

Example

```python from sqlalchemy import Column, Integer, String, select, func from sqlalchemy.orm import sessionmaker, declarative_base from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqladmin import Admin, BaseView, expose from starlette.applications import Starlette

Base = declarative_base() engine = create_async_engine("sqlite+aiosqlite:///test.db") Session = sessionmaker(bind=engine, class_=AsyncSession)

app = Starlette() admin = Admin(app=app, engine=engine)

class User(Base): tablename = "users"

id = Column(Integer, primary_key=True)
name = Column(String(length=16))

class ReportView(BaseView): name = "Report Page" icon = "fa-solid fa-chart-line"

@expose("/report", methods=["GET"])
async def report_page(self, request):
    # async with engine.begin() as conn:
    #     await conn.run_sync(Base.metadata.create_all)

    async with Session(expire_on_commit=False) as session:
        stmt = select(func.count(User.id))
        result = await session.execute(stmt)
        users_count = result.scalar_one()

    return await self.templates.TemplateResponse(
        request,
        "report.html",
        context={"users_count": users_count},
    )

admin.add_view(ReportView)

```

Next we update the report.html file in the templates directory with the following content:

Example

html {% extends "sqladmin/layout.html" %} {% block content %} <div class="col-12"> <div class="card"> <div class="card-header"> <h3 class="card-title">User reports</h3> </div> <div class="card-body border-bottom py-3"> Users count: {{ users_count }} </div> </div> </div> {% endblock %}

Now running your server you can head to /admin/report and you can see the number of users.