Now with async sqlalchemy

master
Drew Bednar 3 years ago
parent f0b7a90b40
commit a8eec38beb

@ -0,0 +1,4 @@
# Asyncio Learning
This repo contains a number for examples and experiments related to python3's asyncio functionality.

@ -10,7 +10,10 @@ services:
ports: ports:
- 8004:8000 - 8004:8000
environment: environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/foo # This is the synchronous uri we started with
# - DATABASE_URL=postgresql://postgres:postgres@db:5432/foo
# This is the async uri after installing asyncpg
- DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/foo
depends_on: depends_on:
- db - db

@ -1,22 +1,56 @@
import os import os
from sqlmodel import create_engine, SQLModel, Session from sqlmodel import SQLModel
# We used the SQLAlchemy constructs -- e.g., create_async_engine and AsyncSession
# -- since SQLModel 0.0.4 does not have wrappers for them as of writing.
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
DATABASE_URL = os.environ.get("DATABASE_URL") DATABASE_URL = os.environ.get("DATABASE_URL")
# The major differences between SQLModel's create_engine and SQLAlchemy's # The major differences between SQLModel's create_engine and SQLAlchemy's
# version is that the SQLModel version adds type annotations (for editor support) # version is that the SQLModel version adds type annotations (for editor support)
# and enables the SQLAlchemy "2.0" style of engines and connections. # and enables the SQLAlchemy "2.0" style of engines and connections.
engine = create_engine(DATABASE_URL, echo=True) # Will want a "debug mode where it doesn't echo for prod
# We need the 2.0 style for async support.
# engine = create_engine(DATABASE_URL, echo=True)
engine = create_async_engine(DATABASE_URL, echo=True, future=True) # Will want a "debug mode where it doesn't echo for prod
async def init_db():
"""
Our new Async init_db was
def init_db(): def init_db():
SQLModel.metadata.create_all(engine) SQLModel.metadata.create_all(engine)
metadata.create_all doesn't execute asynchronously,
so we used run_sync to execute it synchronously within
the async function.
"""
async with engine.begin() as conn:
# await conn.run_sync(SQLModel.metadata.drop_all)
await conn.run_sync(SQLModel.metadata.create_all)
async def get_session() -> AsyncSession:
"""
Our new async get_session
Was
def get_session(): def get_session():
"""A coroutine function for getting a database session."""
with Session(engine) as session: with Session(engine) as session:
yield session yield session
We disabled expire on commit behavior by passing in expire_on_commit=False.
I believe this is how flask-sqlalchemy also works
"""
async_session = sessionmaker(
engine, class_=AsyncSession, expire_on_commit=False
)
async with async_session() as session:
yield session

@ -1,6 +1,6 @@
from fastapi import Depends, FastAPI from fastapi import Depends, FastAPI
from sqlalchemy import select from sqlalchemy import select
from sqlmodel import Session from sqlalchemy.ext.asyncio import AsyncSession
from app.db import get_session, init_db from app.db import get_session, init_db
from app.models import Song, SongCreate from app.models import Song, SongCreate
@ -9,13 +9,13 @@ app = FastAPI()
@app.on_event("startup") @app.on_event("startup")
def on_startup(): async def on_startup():
""" """
It's worth noting that from app.models import Song is required. It's worth noting that from app.models import Song is required.
Without it, the song table will not be created. Kind of like Without it, the song table will not be created. Kind of like
how I have to do my flask sql alchemy models how I have to do my flask sql alchemy models
""" """
init_db() await init_db()
@app.get("/ping") @app.get("/ping")
@ -24,16 +24,16 @@ async def pong():
@app.get("/songs", response_model=list[Song]) @app.get("/songs", response_model=list[Song])
def get_songs(session: Session = Depends(get_session)): async def get_songs(session: AsyncSession = Depends(get_session)):
result = session.execute(select(Song)) result = await session.execute(select(Song))
songs = result.scalars().all() songs = result.scalars().all()
return [Song(name=song.name, artist=song.artist, id=song.id) for song in songs] return [Song(name=song.name, artist=song.artist, id=song.id) for song in songs]
@app.post("/songs") @app.post("/songs")
def add_song(song: SongCreate, session: Session = Depends(get_session)): async def add_song(song: SongCreate, session: AsyncSession = Depends(get_session)):
song = Song(name=song.name, artist=song.artist) song = Song(name=song.name, artist=song.artist)
session.add(song) session.add(song)
session.commit() await session.commit()
session.refresh(song) await session.refresh(song) # refreshed becuase of the async nature
return song return song

@ -1,3 +1,4 @@
asyncpg==0.24.0
fastapi==0.68.1 fastapi==0.68.1
psycopg2-binary==2.9.1 psycopg2-binary==2.9.1
sqlmodel==0.0.4 sqlmodel==0.0.4

Loading…
Cancel
Save