Skip to content

Commit 8a16b48

Browse files
authored
Add FastAPI + SQLAlchemy 2.x example (#49)
1 parent 18aca90 commit 8a16b48

6 files changed

Lines changed: 1305 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,4 @@ dmypy.json
134134
.pyre/
135135

136136
*.code-workspace
137+
.idea/
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
all: install run
2+
3+
.PHONY: fastapi
4+
run:
5+
poetry run fastapi dev --reload --port=8090 example.py
6+
7+
.PHONY: install
8+
install:
9+
poetry install
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
## FastAPI + SQLAlchemy 2.x Example
2+
3+
- Uses in-memory SQLite 3 instance
4+
- Creates User mode
5+
- Creates "admin/admin" superuser
6+
7+
```bash
8+
make install # Creates virtualenv with Poetry
9+
make run # Runs fastapi dev
10+
```
11+
12+
So open `http://127.0.0.1:8090/admin/` and have a fun!
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from fastapi import FastAPI
2+
from sqlalchemy import Boolean, Integer, String, select
3+
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
4+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
5+
6+
from fastadmin import SqlAlchemyModelAdmin, register, fastapi_app as admin_app
7+
8+
9+
sqlalchemy_engine = create_async_engine("sqlite+aiosqlite:///:memory:", echo=True)
10+
sqlalchemy_sessionmaker = async_sessionmaker(sqlalchemy_engine, expire_on_commit=False)
11+
12+
13+
class Base(DeclarativeBase):
14+
pass
15+
16+
17+
class User(Base):
18+
__tablename__ = "user"
19+
20+
id: Mapped[int] = mapped_column(Integer, primary_key=True, nullable=False)
21+
username: Mapped[str] = mapped_column(String(length=255), nullable=False)
22+
password: Mapped[str] = mapped_column(String(length=255), nullable=False)
23+
is_superuser: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
24+
is_active: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
25+
26+
def __str__(self):
27+
return self.username
28+
29+
30+
async def init_db():
31+
async with sqlalchemy_engine.begin() as c:
32+
await c.run_sync(Base.metadata.drop_all)
33+
await c.run_sync(Base.metadata.create_all)
34+
35+
36+
async def create_superuser():
37+
async with sqlalchemy_sessionmaker() as s:
38+
user = User(
39+
username="admin",
40+
password="admin",
41+
is_superuser=True,
42+
is_active=True,
43+
)
44+
s.add(user)
45+
await s.commit()
46+
47+
48+
@register(User, sqlalchemy_sessionmaker=sqlalchemy_sessionmaker)
49+
class UserAdmin(SqlAlchemyModelAdmin):
50+
exclude = ("hash_password",)
51+
list_display = ("id", "username", "is_superuser", "is_active")
52+
list_display_links = ("id", "username")
53+
list_filter = ("id", "username", "is_superuser", "is_active")
54+
search_fields = ("username",)
55+
56+
async def authenticate(self, username, password):
57+
sessionmaker = self.get_sessionmaker()
58+
async with sessionmaker() as session:
59+
query = select(User).filter_by(
60+
username=username, password=password, is_superuser=True
61+
)
62+
result = await session.scalars(query)
63+
user = result.first()
64+
if not user:
65+
return None
66+
if password != user.password:
67+
return None
68+
return user.id
69+
70+
71+
app = FastAPI()
72+
73+
74+
@app.on_event("startup")
75+
async def startup():
76+
await init_db()
77+
await create_superuser()
78+
79+
80+
app.mount("/admin", admin_app)

0 commit comments

Comments
 (0)