diff --git a/Dockerfile b/Dockerfile index 6379386..56a258e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,9 +4,9 @@ WORKDIR /app Copy pyproject.toml /app COPY learn_sql_model/__about__.py /app/learn_sql_model/__about__.py COPY README.md /app -RUN pip3 install '.[api]' +RUN pip3 install '.[all]' COPY . /app -RUN pip3 install '.[api]' +RUN pip3 install '.[all]' EXPOSE 5000 diff --git a/learn_sql_model/api/hero.py b/learn_sql_model/api/hero.py index b8d20da..25c41d5 100644 --- a/learn_sql_model/api/hero.py +++ b/learn_sql_model/api/hero.py @@ -8,13 +8,13 @@ hero_router = APIRouter() @hero_router.on_event("startup") -def on_startup() -> None: +async def on_startup() -> None: # SQLModel.metadata.create_all(get_config().database.engine) ... @hero_router.get("/hero/{hero_id}") -def get_hero( +async def get_hero( *, session: Session = Depends(get_session), hero_id: int, @@ -27,7 +27,7 @@ def get_hero( @hero_router.post("/hero/") -def post_hero( +async def post_hero( *, session: Session = Depends(get_session), hero: HeroCreate, @@ -42,7 +42,7 @@ def post_hero( @hero_router.patch("/hero/") -def patch_hero( +async def patch_hero( *, session: Session = Depends(get_session), hero: HeroUpdate, @@ -61,7 +61,7 @@ def patch_hero( @hero_router.delete("/hero/{hero_id}") -def delete_hero( +async def delete_hero( *, session: Session = Depends(get_session), hero_id: int, @@ -77,11 +77,11 @@ def delete_hero( @hero_router.get("/heros/") -def get_heros( +async def get_heros( *, session: Session = Depends(get_session), ) -> Heros: "get all heros" statement = select(Hero) - heros = session.exec(statement).all() + heros = session.execute(statement).all() return Heros(__root__=heros) diff --git a/learn_sql_model/cli/api.py b/learn_sql_model/cli/api.py index 2dadd71..aca3703 100644 --- a/learn_sql_model/cli/api.py +++ b/learn_sql_model/cli/api.py @@ -29,6 +29,8 @@ def run( help="show the log messages", ), ): + import uvicorn + uvicorn.run(**get_config().api_server.dict()) diff --git a/learn_sql_model/cli/hero.py b/learn_sql_model/cli/hero.py index c855adc..40366ab 100644 --- a/learn_sql_model/cli/hero.py +++ b/learn_sql_model/cli/hero.py @@ -54,7 +54,7 @@ def list() -> Union[Hero, List[Hero]]: def clear() -> Union[Hero, List[Hero]]: "list many heros" heros = Heros.list() - for hero in heros.heros: + for hero in heros.__root__: HeroDelete.delete(id=hero.id) return hero diff --git a/learn_sql_model/config.py b/learn_sql_model/config.py index 4e9415a..07a7b4a 100644 --- a/learn_sql_model/config.py +++ b/learn_sql_model/config.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING from fastapi import Depends from pydantic import BaseModel, BaseSettings, validator from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker from sqlmodel import Session from learn_sql_model.standard_config import load @@ -18,6 +19,7 @@ class ApiServer(BaseModel): reload: bool = True log_level: str = "info" host: str = "0.0.0.0" + workers: int = 1 class ApiClient(BaseModel): @@ -42,7 +44,16 @@ class Database: @property def engine(self) -> "Engine": - return create_engine(self.config.database_url) + try: + return self._engine + except AttributeError: + self._engine = create_engine( + self.config.database_url, + connect_args={"check_same_thread": False}, + pool_recycle=3600, + pool_pre_ping=True, + ) + return self._engine @property def session(self) -> "Session": @@ -87,11 +98,20 @@ def get_config(overrides: dict = {}) -> Config: return config +config = get_config() +engine = create_engine( + config.database_url, + connect_args={"check_same_thread": False}, + pool_recycle=3600, + pool_pre_ping=True, +) + +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + + def get_session() -> "Session": - config = get_config() - engine = create_engine(config.database_url) with Session(engine) as session: - yield session + yield SessionLocal() async def reset_db_state(config: Config = None) -> None: diff --git a/learn_sql_model/models/hero.py b/learn_sql_model/models/hero.py index 93d1dac..67a19c0 100644 --- a/learn_sql_model/models/hero.py +++ b/learn_sql_model/models/hero.py @@ -1,13 +1,11 @@ -from typing import Dict, Optional +from typing import Dict import httpx from pydantic import BaseModel from sqlmodel import Field, SQLModel from learn_sql_model.config import config - from learn_sql_model.optional import optional -from learn_sql_model.models.pet import Pet class HeroBase(SQLModel, table=False): @@ -75,7 +73,7 @@ class HeroUpdate(HeroBase): def update(self) -> Hero: r = httpx.patch( f"{config.api_client.url}/hero/", - json=self.dict(), + json=self.dict(exclude_none=True), ) if r.status_code != 200: raise RuntimeError(f"{r.status_code}:\n {r.text}") diff --git a/migrations/versions/3555f61aaa79_add_x_and_y.py b/migrations/versions/3555f61aaa79_add_x_and_y.py index 0ff42c5..b6f112d 100644 --- a/migrations/versions/3555f61aaa79_add_x_and_y.py +++ b/migrations/versions/3555f61aaa79_add_x_and_y.py @@ -7,30 +7,26 @@ Create Date: 2023-06-22 15:03:27.338959 """ from alembic import op import sqlalchemy as sa -import sqlmodel -from learn_sql_model.er_diagram import generate_er_diagram, generate_er_markdown -from learn_sql_model.config import get_config - # revision identifiers, used by Alembic. -revision = '3555f61aaa79' -down_revision = '79972ec5f79d' +revision = "3555f61aaa79" +down_revision = "79972ec5f79d" branch_labels = None depends_on = None def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.add_column('hero', sa.Column('x', sa.Integer(), nullable=False)) - op.add_column('hero', sa.Column('y', sa.Integer(), nullable=False)) + op.add_column("hero", sa.Column("x", sa.Integer(), nullable=False)) + op.add_column("hero", sa.Column("y", sa.Integer(), nullable=False)) # ### end Alembic commands ### - generate_er_diagram(f'migrations/versions/{revision}_er_diagram.png') - generate_er_markdown(f'migrations/versions/{revision}_er_diagram.md', f'migrations/versions/er_diagram_{revision}.png') + # generate_er_diagram(f'migrations/versions/{revision}_er_diagram.png') + # generate_er_markdown(f'migrations/versions/{revision}_er_diagram.md', f'migrations/versions/er_diagram_{revision}.png') def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('hero', 'y') - op.drop_column('hero', 'x') + op.drop_column("hero", "y") + op.drop_column("hero", "x") # ### end Alembic commands ### diff --git a/migrations/versions/79972ec5f79d_int.py b/migrations/versions/79972ec5f79d_int.py index 5fd59b7..1ccca25 100644 --- a/migrations/versions/79972ec5f79d_int.py +++ b/migrations/versions/79972ec5f79d_int.py @@ -1,20 +1,17 @@ """int Revision ID: 79972ec5f79d -Revises: +Revises: Create Date: 2023-06-22 15:02:20.292322 """ from alembic import op import sqlalchemy as sa import sqlmodel -from learn_sql_model.er_diagram import generate_er_diagram, generate_er_markdown -from learn_sql_model.config import get_config - # revision identifiers, used by Alembic. -revision = '79972ec5f79d' +revision = "79972ec5f79d" down_revision = None branch_labels = None depends_on = None @@ -22,25 +19,27 @@ depends_on = None def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.create_table('hero', - sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('secret_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('id', sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint('id') + op.create_table( + "hero", + sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("secret_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("id", sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint("id"), ) - op.create_table('pet', - sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('birthday', sa.DateTime(), nullable=True), - sa.Column('id', sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint('id') + op.create_table( + "pet", + sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column("birthday", sa.DateTime(), nullable=True), + sa.Column("id", sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint("id"), ) # ### end Alembic commands ### - generate_er_diagram(f'migrations/versions/{revision}_er_diagram.png') - generate_er_markdown(f'migrations/versions/{revision}_er_diagram.md', f'migrations/versions/er_diagram_{revision}.png') + # generate_er_diagram(f'migrations/versions/{revision}_er_diagram.png') + # generate_er_markdown(f'migrations/versions/{revision}_er_diagram.md', f'migrations/versions/er_diagram_{revision}.png') def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('pet') - op.drop_table('hero') + op.drop_table("pet") + op.drop_table("hero") # ### end Alembic commands ###