Skip to main content
# NorthAdmin

Easy-to-setup PWA Admin Panel solution based on FastAPI, async SQLAlchemy and pre-render Swelte UI.

___

[PyPI](https://pypi.org/project/north_admin/) | [GitHub](https://github.com/OlenEnkeli/north_admin) | [Example App](https://github.com/OlenEnkeli/north_admin_testapp) | [Frontend](https://github.com/OlenEnkeli/north_admin_frontend)

___

**This is beta-version and frontend part (UI) currenly is in development.**

**Now in NorthAdmin available only backend/API part,**

## Requirements

- Python 3.11+
- [FastAPI](https://github.com/tiangolo/fastapi) and [SQLAlclhemy](https://github.com/sqlalchemy/sqlalchemy)
- Any async DB driver like [AsyncPG](https://github.com/MagicStack/asyncpg)
- Any ASGI Python server like [Uvicorn](https://github.com/encode/uvicorn)

## Key benefits

- **Easy to integrate** - The amount of code that needs to be completed for the integration of the admin panel is minimal.
- **Fast** - NorthAdmin using async DB drivers and Swelte as UI framework to make admin panel as fast as possible.
- **PWA** - Similar solutions are offered by SSR, which is slower, less convenient and creates additional load on the server.
- **Flexibility** - We can create almost any filters to expand the functionality of the admin panel.
- **Good-looking** - Modern Material UI-based interface thanks to [TailWindCSS](https://tailwindcss.com/)

## Example

Let's assume that we have project with:

- Postgres
- Some SQLAlchemy models

In this example we prefer to use AsyncPG as driver.

### Install NorthAdmin

**Poetry**:

```bash
poetry add sqlalchemy asyncpg uvicorn north_admin
```

**PIP**

```bash
pip3 install sqlalchemy asyncpg uvicorn north_admin
pip3 freezee -> requirements.txt
```

### Add NorthAdmin in projects

For example, this is our `User` table look like:

```python
class User(Base):
__tablename__ = 'users'

id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str] = mapped_column(unique=True, nullable=False, index=True)
password: Mapped[str] = mapped_column(nullable=False)
fullname: Mapped[str] = mapped_column(nullable=True)
is_active: Mapped[bool] = mapped_column(default=False)
is_admin: Mapped[bool] = mapped_column(default=False)
created_at: Mapped[dt] = mapped_column(default=dt.now, server_default=func.current_timestamp())
```


Suppose we need the ability:
- To view a list of users
- To get detailed information about each of them
- Soft delete (block) them
- We can do it only with non-admin users (User.user_type == UserType.user)

**This is an artificial one-line example.**

If you need a more complete production-ready example, see [NorthAdmin Test App](https://github.com/OlenEnkeli/north_admin_testapp)

So, here an out `admin.py` file:

```python
from datetime import datetime as dt
from enum import Enum

from fastapi import FastAPI
from sqlalchemy import bindparam, and_, select, func
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.dialects.postgresql import ENUM
from north_admin import (
NorthAdmin,
FilterGroup,
Filter,
FieldType,
AdminRouter,
AdminMethods,
AuthProvider,
setup_admin,
UserReturnSchema,
)
from north_admin.types import ModelType, QueryType


class Base(DeclarativeBase):
pass


class UserType(str, Enum):
USER = 'user'
ADMIN = 'admin'


class User(Base):
__tablename__ = 'users'

id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str] = mapped_column(unique=True, nullable=False, index=True)
password: Mapped[str] = mapped_column(nullable=False)
fullname: Mapped[str] = mapped_column(nullable=True)
is_active: Mapped[bool] = mapped_column(default=True)
user_type: Mapped[UserType] = mapped_column(ENUM(UserType, name='user_type'), default=UserType.USER)
created_at: Mapped[dt] = mapped_column(default=dt.now, server_default=func.current_timestamp())


class AdminAuthProvider(AuthProvider):
async def login(
self,
session: AsyncSession,
login: str,
password: str,
) -> User | None:
query = (
select(User)
.filter(User.email == login)
.filter(User.user_type == UserType.ADMIN)
)

return await session.scalar(query)

async def get_user_by_id(
self,
session: AsyncSession,
user_id: int | str,
) -> User | None:
query = (
select(User)
.filter(User.id == user_id)
.filter(User.user_type == UserType.ADMIN)
)

return await session.scalar(query)

async def to_user_scheme(
self,
user: User,
) -> UserReturnSchema:
return UserReturnSchema(
id=user.id,
login=user.email,
fullname=user.fullname,
)


admin_app = NorthAdmin(
sqlalchemy_uri='postgresql+asyncpg://postgres:@127.0.0.1:5432/north_admin_test_app',
jwt_secket_key='JNBjdejjn!w443@wer',
auth_provider=AdminAuthProvider,
)

user_get_columns = [
User.id,
User.email,
User.fullname,
User.user_type,
User.created_at,
]


def exclude_admin_users(query: QueryType) -> QueryType:
query = query.filter(User.user_type != UserType.ADMIN)
return query


admin_app.add_admin_routes(
AdminRouter(
model=User,
model_title='Users',
enabled_methods=[
AdminMethods.GET_ONE,
AdminMethods.GET_LIST,
AdminMethods.SOFT_DELETE,
],
process_query_method=exclude_admin_users,
pkey_column=User.id,
soft_delete_column=User.is_active,
get_columns=user_get_columns,
list_columns=user_get_columns,
filters=[
FilterGroup(
query=(
and_(
User.created_at > dt.now().replace(hour=0, minute=0, second=0),
bindparam('created_today_param'),
)
),
filters=[
Filter(
bindparam='created_today_param',
title='Created today',
field_type=FieldType.BOOLEAN,
),
],
),
FilterGroup(
query=(User.created_at > bindparam('created_after_gt')),
filters=[
Filter(
bindparam='created_after_gt',
title='Created after',
field_type=FieldType.DATETIME,
)
],
),
]
)
)

app = FastAPI()

setup_admin(
admin_app=admin_app,
app=app,
)
```

Don't forget to use `Alembic` or other migration tools or `DeclarativeBase.Meta.create_all()` to create `User` table in DB

Short Example with psycopg:

Add this in the end of `admin.py`:

```python
if __name__ == '__main__':
from sqlalchemy import create_engine


Base.metadata.create_all(
bind=create_engine(
'postgresql+psycopg://postgres:@127.10.0.1:5432/north_admin_test_app'
),
)
```

Install pscyopg and run it:

```bash
poetry add psycopg psycopg-binary
python3 -m admin
```



**Now we finally can run it**:

```bash
poetry run uvicorn north_admin.test_app:app --host 0.0.0.0 --port 8000 --reload
```

### Let's take a look at what's going on here

- Firstly, we create `AdminAuthProvider`, what must implement three methods - `login`, `get_user_id`, `to_user_scheme`

- `login` - get `User` by login and password
- `get_user_by_id` - get `User` by id
- `to_user_schema` - dump `User` model to `UserReturnSchema`.


- Next we create `NorthAdmin` application with 3 required parameters (`sqlalchemy_uri`, `jwt_secket_key` and `auth_provider` - we just created it before)


- Now we can add admin page to our admin panel (`add_admin_routes` methods)

### Admin Router parameters:

- `model` - SQLAlchemy model we want to administrate (**required**)

- `model_title` - Verbose title (displayed in UI). By default generate from `model`

- `emoji` - Emoji displayed near to model name in UI. By default - random emoji.

- `enabled_methods` - List of actions, awailable in admin panel (`GET_LIST`, `GET_ONE`, `CREATE`, `UPDATE`, `DELETE`, `SOFT_DELETE`). By default - all ot them.

- `process_query_method` - Function applied to SQLAlchemy query in (`GET_LIST`, `GET_ONE`, `UPDATE`, `DELETE`, `SOFT_DELETE`) methods. Feel free to excluding, filtering, etc.

- `pkey_column` - Primary Key column. By default `model.id`

- `list_columns` - Columns displayed when displaying a list of `model` items in UI. By default - all columns.

- `get_columns` - Similary for viewing one item. By default - all columns.

- `create_columns` - Similary for creatine new item. By default - all non-key columns.

- `update_columns` - Similary for updatings existing item. By default - all non-key columns.

- `soft_delete_column` - Boolean SQLAlchemy column. When soft deleting (blocking) item, it set to `False`, is restoring to `True`. **Required**, if you have `SOFT_DELETE` in `enabled_methods`

- `sortable_columns` - List of SQLAclehmy columns, A list of columns to which we can apply sorting in the UI. By default - all key column. (See `sort_by` params in list method)

- `filters` - list of `FilterGroup` object. By default no filters applied.


### Filters

Filters are represented by `FilterGroup` and `Filter` classes

`FilterGroup` contains SQLAlchemy query with some `bind_params`.

Value of `bind_params` is represented in `Filter` `title` field and this is exactly the parameter that the front-end will.

Also `Filter` object contains name of filter in UI (`title`) and UI type (`field_type`)

In our case, we will see two filters in the admin panel:

- Checkbox `Created Today`
- Date-picker `Created After`

Awailable `FieldType`: `INTEGER`, `BOOLEAN` (checkbox), `FLOAT`, `STRING`, `ENUM` (listpicker), `DATETIME` (datetime picker), `ARRAY` (several string input)


**Filter system it may seem confusing (overcomplex) at the beginning, but it gives unlimited variability of implemented filters.**

### Result

Restart FastAPI application and **admin panel is ready**.

E.q. we running app with Uvicorn on http://127.0.0.1 we have:

- Admin Panel UI in http://127.0.0.1/admin/
- Admin Panel API in http://127.0.0.1/admin/api/
- Swagger in http://127.0.0.1/admin/docs/

Create user with `user_type=UserType.Admin`, check out Swagger and **enjoy using your new admin panel**.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

north_admin-0.1.4.tar.gz (28.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

north_admin-0.1.4-py3-none-any.whl (27.7 kB view details)

Uploaded Python 3

File details

Details for the file north_admin-0.1.4.tar.gz.

File metadata

  • Download URL: north_admin-0.1.4.tar.gz
  • Upload date:
  • Size: 28.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.7.1 CPython/3.12.0 Darwin/23.1.0

File hashes

Hashes for north_admin-0.1.4.tar.gz
Algorithm Hash digest
SHA256 6aad61c9640672e625a66cad48aed9424232d78eb06444a5609bd36153ca4c7a
MD5 a10521040785d8c8e9f75a4d28199001
BLAKE2b-256 1fb85c928478aa973b964c18a789f63bc69595e27a982e9de6467a4c90c415af

See more details on using hashes here.

File details

Details for the file north_admin-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: north_admin-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 27.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.7.1 CPython/3.12.0 Darwin/23.1.0

File hashes

Hashes for north_admin-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 76f75472eab52c65ac96d554681818c93b5b17e23adfc84b4b8801a4663ec5b3
MD5 b6ff3f87202944b71014996512ffb485
BLAKE2b-256 72275b2f709efa76fa11c70b693f9806272d714e7c21673fe730b6a0ed2a0180

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.4 This release

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page