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:
- 8004:8000
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:
- db

@ -1,22 +1,56 @@
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")
# The major differences between SQLModel's create_engine and SQLAlchemy's
# version is that the SQLModel version adds type annotations (for editor support)
# 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():
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)
def init_db():
SQLModel.metadata.create_all(engine)
async def get_session() -> AsyncSession:
"""
Our new async get_session
def get_session():
"""A coroutine function for getting a database session."""
with Session(engine) as session:
Was
def get_session():
with Session(engine) as 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 sqlalchemy import select
from sqlmodel import Session
from sqlalchemy.ext.asyncio import AsyncSession
from app.db import get_session, init_db
from app.models import Song, SongCreate
@ -9,13 +9,13 @@ app = FastAPI()
@app.on_event("startup")
def on_startup():
async def on_startup():
"""
It's worth noting that from app.models import Song is required.
Without it, the song table will not be created. Kind of like
how I have to do my flask sql alchemy models
"""
init_db()
await init_db()
@app.get("/ping")
@ -24,16 +24,16 @@ async def pong():
@app.get("/songs", response_model=list[Song])
def get_songs(session: Session = Depends(get_session)):
result = session.execute(select(Song))
async def get_songs(session: AsyncSession = Depends(get_session)):
result = await session.execute(select(Song))
songs = result.scalars().all()
return [Song(name=song.name, artist=song.artist, id=song.id) for song in 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)
session.add(song)
session.commit()
session.refresh(song)
await session.commit()
await session.refresh(song) # refreshed becuase of the async nature
return song

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

Loading…
Cancel
Save