This commit is contained in:
Waylon Walker 2023-06-08 09:02:43 -05:00
parent a9ee4a2bd8
commit d68cda91cf
No known key found for this signature in database
GPG key ID: 66E2BF2B4190EFE4
22 changed files with 824 additions and 55 deletions

View file

@ -1,19 +1,20 @@
# from learn_sql_model.config import config
from learn_sql_model.api.websocket_connection_manager import manager
from learn_sql_model.config import Config from learn_sql_model.config import Config
from learn_sql_model.config import get_config from learn_sql_model.config import get_config
from learn_sql_model.database import get_database
from learn_sql_model.console import console from learn_sql_model.console import console
from learn_sql_model.database import get_database
# models
from learn_sql_model.models.fast_model import FastModel
from learn_sql_model.models.hero import Hero
from learn_sql_model.models.hero import HeroCreate
from learn_sql_model.models.hero import HeroRead
from learn_sql_model.models.hero import HeroUpdate
from learn_sql_model.models.hero import HeroDelete
from learn_sql_model.factories.hero import HeroFactory from learn_sql_model.factories.hero import HeroFactory
from learn_sql_model.factories.pet import PetFactory from learn_sql_model.factories.pet import PetFactory
from learn_sql_model.models.fast_model import FastModel
from learn_sql_model.models.hero import Hero
from learn_sql_model.models.hero import HeroCreate
from learn_sql_model.models.hero import HeroDelete
from learn_sql_model.models.hero import HeroRead
from learn_sql_model.models.hero import HeroUpdate
from learn_sql_model.models.new import new
from learn_sql_model.models.new import newCreate
from learn_sql_model.models.new import newDelete
from learn_sql_model.models.new import newRead
from learn_sql_model.models.new import newUpdate
from learn_sql_model.models.pet import Pet from learn_sql_model.models.pet import Pet

42
client.py Normal file
View file

@ -0,0 +1,42 @@
import time
from rich.console import Console
from websocket import create_connection
from learn_sql_model.models.hero import Hero
def connect():
id = 1
url = f"ws://localhost:5000/ws/{id}"
Console().log(f"connecting to: {url}")
ws = create_connection(url)
Console().log(f"connected to: {url}")
return ws
data = []
def watch(ws):
while ws.connected:
try:
data.append(ws.recv())
if data[-1].startswith("{"):
Console().log(Hero.parse_raw(data[-1]))
else:
Console().log(data[-1])
except Exception as e:
Console().log("failed to recieve data")
Console().log(e)
if __name__ == "__main__":
while True:
try:
ws = connect()
watch(ws)
except Exception as e:
Console().log("failed to connect")
Console().log(e)
time.sleep(1)

14
client_sender.py Normal file
View file

@ -0,0 +1,14 @@
import time
from rich.console import Console
from websocket import create_connection
id = 1
url = f"ws://localhost:5000/ws/{id}"
Console().log(f"connecting to: {url}")
ws = create_connection(url)
data = []
while True:
ws.send("hello".encode())
time.sleep(1)

View file

@ -2,8 +2,24 @@ from fastapi import FastAPI
from learn_sql_model.api.hero import hero_router from learn_sql_model.api.hero import hero_router
from learn_sql_model.api.user import user_router from learn_sql_model.api.user import user_router
from learn_sql_model.api.websocket import web_socket_router
# from fastapi_socketio import SocketManager
app = FastAPI() app = FastAPI()
# socket_manager = SocketManager(app=app)
app.include_router(hero_router) app.include_router(hero_router)
app.include_router(user_router) app.include_router(user_router)
app.include_router(web_socket_router)
# @app.sio.on("join")
# def handle_join(sid, *args, **kwargs):
# app.sio.emit("lobby", "User joined")
# @app.sio.on("leave")
# def handle_leave(sid, *args, **kwargs):
# sm.emit("lobby", "User left")

View file

@ -4,8 +4,15 @@ from fastapi import APIRouter, Depends
from sqlmodel import SQLModel from sqlmodel import SQLModel
from learn_sql_model.api.user import oauth2_scheme from learn_sql_model.api.user import oauth2_scheme
from learn_sql_model.api.websocket_connection_manager import manager
from learn_sql_model.config import Config, get_config from learn_sql_model.config import Config, get_config
from learn_sql_model.models.hero import Hero from learn_sql_model.models.hero import (
Hero,
HeroCreate,
HeroDelete,
HeroRead,
HeroUpdate,
)
hero_router = APIRouter() hero_router = APIRouter()
@ -21,30 +28,46 @@ async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
@hero_router.get("/hero/{id}") @hero_router.get("/hero/{id}")
def get_hero(id: int, config: Config = Depends(get_config)) -> Hero: async def get_hero(id: int, config: Config = Depends(get_config)) -> Hero:
"get one hero" "get one hero"
return Hero().get(id=id, config=config) return Hero().get(id=id, config=config)
@hero_router.get("/h/{id}") @hero_router.get("/h/{id}")
def get_h(id: int, config: Config = Depends(get_config)) -> Hero: async def get_h(id: int, config: Config = Depends(get_config)) -> Hero:
"get one hero" "get one hero"
return Hero().get(id=id, config=config) return Hero().get(id=id, config=config)
@hero_router.post("/hero/") @hero_router.post("/hero/")
def post_hero(hero: Hero, config: Config = Depends(get_config)) -> Hero: async def post_hero(hero: HeroCreate) -> HeroRead:
"read all the heros" "read all the heros"
hero.post(config=config) config = get_config()
hero = hero.post(config=config)
await manager.broadcast({hero.json()}, id=1)
return hero
@hero_router.patch("/hero/")
async def patch_hero(hero: HeroUpdate) -> HeroRead:
"read all the heros"
config = get_config()
hero = hero.update(config=config)
await manager.broadcast({hero.json()}, id=1)
return hero
@hero_router.delete("/hero/{hero_id}")
async def delete_hero(hero_id: int):
"read all the heros"
hero = HeroDelete(id=hero_id)
config = get_config()
hero = hero.delete(config=config)
await manager.broadcast(f"deleted hero {hero_id}", id=1)
return hero return hero
@hero_router.get("/heros/") @hero_router.get("/heros/")
def get_heros(config: Config = Depends(get_config)) -> list[Hero]: async def get_heros(config: Config = Depends(get_config)) -> list[Hero]:
"get all heros" "get all heros"
return Hero().get(config=config) return Hero().get(config=config)
# Alternatively
# with get_config().database.session as session:
# statement = select(Hero)
# results = session.exec(statement).all()
# return results

View file

@ -0,0 +1,45 @@
from typing import Annotated
from fastapi import APIRouter, Depends
from sqlmodel import SQLModel
from learn_sql_model.api.user import oauth2_scheme
from learn_sql_model.config import Config, get_config
from learn_sql_model.models.new import new
new_router = APIRouter()
@new_router.on_event("startup")
def on_startup() -> None:
SQLModel.metadata.create_all(get_config().database.engine)
@new_router.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
@new_router.get("/new/{id}")
def get_new(id: int, config: Config = Depends(get_config)) -> new:
"get one new"
return new().get(id=id, config=config)
@new_router.get("/h/{id}")
def get_h(id: int, config: Config = Depends(get_config)) -> new:
"get one new"
return new().get(id=id, config=config)
@new_router.post("/new/")
def post_new(new: new, config: Config = Depends(get_config)) -> new:
"read all the news"
new.post(config=config)
return new
@new_router.get("/news/")
def get_news(config: Config = Depends(get_config)) -> list[new]:
"get all news"
return new().get(config=config)

View file

@ -0,0 +1,72 @@
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
from learn_sql_model.api.websocket_connection_manager import manager
web_socket_router = APIRouter()
html = """
<!DOCTYPE html>
<html>
<head>
<title>Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<form action="" onsubmit="sendMessage(event)">
<input type="text" id="messageText" autocomplete="off"/>
<button>Send</button>
</form>
<ul id='messages'>
</ul>
<script>
var ws = new WebSocket("ws://localhost:5000/ws");
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
var content = document.createTextNode(event.data)
message.appendChild(content)
messages.appendChild(message)
};
function sendMessage(event) {
var input = document.getElementById("messageText")
ws.send(input.value)
input.value = ''
event.preventDefault()
}
</script>
</body>
</html>
"""
@web_socket_router.get("/watch")
async def get():
return HTMLResponse(html)
@web_socket_router.websocket("/ws/{id}")
async def websocket_endpoint_connect(websocket: WebSocket, id: int):
await manager.connect(websocket, id)
try:
while True:
data = await websocket.receive_text()
await websocket.send_text(f"[gold]You Said: {data}")
await manager.broadcast(f"[blue]USER: {data}", id)
except WebSocketDisconnect:
manager.disconnect(websocket, id)
await manager.broadcast(f"Client #{id} left the chat", id)
@web_socket_router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
await manager.broadcast(f"[blue]USER: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket, id)
await manager.broadcast(f"Client #{client_id} left the chat", id)

View file

@ -0,0 +1,41 @@
from typing import Dict
from fastapi import WebSocket
class ConnectionManager:
def __init__(self):
self.active_connections: Dict[str, list[WebSocket]] = {}
async def connect(self, websocket: WebSocket, id: str):
print("connecting...", id)
if id not in self.active_connections:
self.active_connections[id] = []
await websocket.accept()
self.active_connections[id].append(websocket)
def disconnect(self, websocket: WebSocket, id: str):
if id not in self.active_connections:
return
self.active_connections[id].remove(websocket)
async def send_personal_message(self, message: str, websocket: WebSocket):
await websocket.send_text(message)
async def broadcast(self, message: str, id: str):
if id not in self.active_connections:
return
print(f"i go this message {message}")
print(
f"I am going to send it to {len(self.active_connections[id])} connections"
)
for connection in self.active_connections[id]:
print("sending it to ", connection)
try:
await connection.send_text(message)
except Exception:
self.disconnect(connection, id)
print("sent it to ", connection)
manager = ConnectionManager()

View file

@ -5,6 +5,7 @@ from typer.main import get_group
from learn_sql_model.cli.api import api_app from learn_sql_model.cli.api import api_app
from learn_sql_model.cli.config import config_app from learn_sql_model.cli.config import config_app
from learn_sql_model.cli.hero import hero_app from learn_sql_model.cli.hero import hero_app
from learn_sql_model.cli.model import model_app
app = typer.Typer( app = typer.Typer(
name="learn_sql_model", name="learn_sql_model",
@ -12,7 +13,7 @@ app = typer.Typer(
) )
app.add_typer(config_app, name="config") app.add_typer(config_app, name="config")
# app.add_typer(tui_app, name="tui") # app.add_typer(tui_app, name="tui")
# app.add_typer(model_app, name="model") app.add_typer(model_app, name="model")
app.add_typer(api_app, name="api") app.add_typer(api_app, name="api")
app.add_typer(hero_app, name="hero") app.add_typer(hero_app, name="hero")

View file

@ -2,6 +2,7 @@ import sys
from typing import List, Optional, Union from typing import List, Optional, Union
from engorgio import engorgio from engorgio import engorgio
import httpx
from rich.console import Console from rich.console import Console
import typer import typer
@ -58,17 +59,17 @@ def create(
config: Config = None, config: Config = None,
) -> Hero: ) -> Hero:
"read all the heros" "read all the heros"
# config.init()
hero = hero.post(config=config) r = httpx.post(
Console().print(hero) f"{config.api_client.url}/hero/",
return hero json=hero.dict(),
# config.init() )
# with Session(config.database.engine) as session: if r.status_code != 200:
# db_hero = Hero.from_orm(hero) raise RuntimeError(f"{r.status_code}:\n {r.text}")
# session.add(db_hero)
# session.commit() # hero = hero.post(config=config)
# session.refresh(db_hero) # Console().print(hero)
# return db_hero # return hero
@hero_app.command() @hero_app.command()
@ -78,9 +79,12 @@ def update(
config: Config = None, config: Config = None,
) -> Hero: ) -> Hero:
"read all the heros" "read all the heros"
hero = hero.update(config=config) r = httpx.patch(
Console().print(hero) f"{config.api_client.url}/hero/",
return hero json=hero.dict(),
)
if r.status_code != 200:
raise RuntimeError(f"{r.status_code}:\n {r.text}")
@hero_app.command() @hero_app.command()
@ -90,10 +94,11 @@ def delete(
config: Config = None, config: Config = None,
) -> Hero: ) -> Hero:
"read all the heros" "read all the heros"
# config.init() r = httpx.delete(
hero = hero.delete(config=config) f"{config.api_client.url}/hero/{hero.id}",
return hero )
# Console().print(hero) if r.status_code != 200:
raise RuntimeError(f"{r.status_code}:\n {r.text}")
@hero_app.command() @hero_app.command()

View file

@ -1,7 +1,9 @@
from pathlib import Path
import alembic import alembic
import typer
from alembic.config import Config from alembic.config import Config
from copier import run_auto from copier import run_auto
import typer
from learn_sql_model.cli.common import verbose_callback from learn_sql_model.cli.common import verbose_callback
@ -26,18 +28,19 @@ def create(
callback=verbose_callback, callback=verbose_callback,
help="show the log messages", help="show the log messages",
), ),
template=Path('templates/model') ):
run_auto(template, Path('.')) template = Path("templates/model")
run_auto(str(template), ".")
@ model_app.command() @model_app.command()
def create_revision( def create_revision(
verbose: bool=typer.Option( verbose: bool = typer.Option(
False, False,
callback=verbose_callback, callback=verbose_callback,
help="show the log messages", help="show the log messages",
), ),
message: str=typer.Option( message: str = typer.Option(
prompt=True, prompt=True,
), ),
): ):
@ -51,23 +54,23 @@ def create_revision(
alembic.command.upgrade(config=alembic_cfg, revision="head") alembic.command.upgrade(config=alembic_cfg, revision="head")
@ model_app.command() @model_app.command()
def checkout( def checkout(
verbose: bool=typer.Option( verbose: bool = typer.Option(
False, False,
callback=verbose_callback, callback=verbose_callback,
help="show the log messages", help="show the log messages",
), ),
revision: str=typer.Option("head"), revision: str = typer.Option("head"),
): ):
alembic_cfg = Config("alembic.ini") alembic_cfg = Config("alembic.ini")
alembic.command.upgrade(config=alembic_cfg, revision="head") alembic.command.upgrade(config=alembic_cfg, revision="head")
@ model_app.command() @model_app.command()
def populate( def populate(
verbose: bool=typer.Option( verbose: bool = typer.Option(
False, False,
callback=verbose_callback, callback=verbose_callback,
help="show the log messages", help="show the log messages",

107
learn_sql_model/cli/new.py Normal file
View file

@ -0,0 +1,107 @@
import sys
from typing import List, Optional, Union
from engorgio import engorgio
from rich.console import Console
import typer
from learn_sql_model.config import Config, get_config
from learn_sql_model.factories.new import newFactory
from learn_sql_model.factories.pet import PetFactory
from learn_sql_model.models.new import (
new,
newCreate,
newDelete,
newRead,
newUpdate,
)
new_app = typer.Typer()
@new_app.callback()
def new():
"model cli"
@new_app.command()
@engorgio(typer=True)
def get(
id: Optional[int] = typer.Argument(default=None),
config: Config = None,
) -> Union[new, List[new]]:
"get one new"
config.init()
new = newRead.get(id=id, config=config)
Console().print(new)
return new
@new_app.command()
@engorgio(typer=True)
def list(
where: Optional[str] = None,
config: Config = None,
offset: int = 0,
limit: Optional[int] = None,
) -> Union[new, List[new]]:
"get one new"
new = newRead.list(config=config, where=where, offset=offset, limit=limit)
Console().print(new)
return new
@new_app.command()
@engorgio(typer=True)
def create(
new: newCreate,
config: Config = None,
) -> new:
"read all the news"
# config.init()
new = new.post(config=config)
Console().print(new)
return new
@new_app.command()
@engorgio(typer=True)
def update(
new: newUpdate,
config: Config = None,
) -> new:
"read all the news"
new = new.update(config=config)
Console().print(new)
return new
@new_app.command()
@engorgio(typer=True)
def delete(
new: newDelete,
config: Config = None,
) -> new:
"read all the news"
# config.init()
new = new.delete(config=config)
return new
@new_app.command()
@engorgio(typer=True)
def populate(
new: new,
n: int = 10,
) -> new:
"read all the news"
config = get_config()
if config.env == "prod":
Console().print("populate is not supported in production")
sys.exit(1)
for new in newFactory().batch(n):
pet = PetFactory().build()
new.pet = pet
Console().print(new)
new.post(config=config)

View file

@ -20,6 +20,13 @@ class ApiServer(BaseModel):
host: str = "0.0.0.0" host: str = "0.0.0.0"
class ApiClient(BaseModel):
host: str = "0.0.0.0"
port: int = 5000
protocol: str = "http"
url: str = f"{protocol}://{host}:{port}"
class Database: class Database:
def __init__(self, config: "Config" = None) -> None: def __init__(self, config: "Config" = None) -> None:
if config is None: if config is None:
@ -48,6 +55,7 @@ class Config(BaseSettings):
env: str = "dev" env: str = "dev"
database_url: str = "sqlite:///database.db" database_url: str = "sqlite:///database.db"
api_server: ApiServer = ApiServer() api_server: ApiServer = ApiServer()
api_client: ApiClient = ApiClient()
class Config: class Config:
extra = "ignore" extra = "ignore"

View file

@ -0,0 +1,14 @@
from faker import Faker
from polyfactory.factories.pydantic_factory import ModelFactory
from learn_sql_model.models.new import new
class newFactory(ModelFactory[new]):
__model__ = new
__faker__ = Faker(locale="en_US")
__set_as_default_factory_for_type__ = True
id = None
__random_seed__ = 10

View file

@ -0,0 +1,99 @@
from typing import Optional
from fastapi import HTTPException
from pydantic import BaseModel
from sqlmodel import Field, Relationship, SQLModel, Session, select
from learn_sql_model.config import Config
from learn_sql_model.models.pet import Pet
class newBase(SQLModel, table=False):
class new(newBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
class newCreate(newBase):
...
def post(self, config: Config) -> new:
config.init()
with Session(config.database.engine) as session:
db_new = new.from_orm(self)
session.add(db_new)
session.commit()
session.refresh(db_new)
return db_new
class newRead(newBase):
id: int
@classmethod
def get(
cls,
config: Config,
id: int,
) -> new:
with config.database.session as session:
new = session.get(new, id)
if not new:
raise HTTPException(status_code=404, detail="new not found")
return new
@classmethod
def list(
self,
config: Config,
where=None,
offset=0,
limit=None,
) -> new:
with config.database.session as session:
statement = select(new)
if where != "None":
from sqlmodel import text
statement = statement.where(text(where))
statement = statement.offset(offset).limit(limit)
newes = session.exec(statement).all()
return newes
class newUpdate(SQLModel):
# id is required to get the new
id: int
# all other fields, must match the model, but with Optional default None
def update(self, config: Config) -> new:
with Session(config.database.engine) as session:
db_new = session.get(new, self.id)
if not db_new:
raise HTTPException(status_code=404, detail="new not found")
new_data = self.dict(exclude_unset=True)
for key, value in new_data.items():
if value is not None:
setattr(db_new, key, value)
session.add(db_new)
session.commit()
session.refresh(db_new)
return db_new
class newDelete(BaseModel):
id: int
def delete(self, config: Config) -> new:
config.init()
with Session(config.database.engine) as session:
new = session.get(new, self.id)
if not new:
raise HTTPException(status_code=404, detail="new not found")
session.delete(new)
session.commit()
return {"ok": True}

View file

@ -24,7 +24,10 @@ classifiers = [
"Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: PyPy",
] ]
dependencies = [ dependencies = [
"python-socketio[client]",
"fastapi-socketio",
"anyconfig", "anyconfig",
"copier",
"engorgio", "engorgio",
"fastapi", "fastapi",
"httpx", "httpx",

View file

@ -0,0 +1,5 @@
from learn_sql_model.models.{{modelname.lower()}} import {{modelname}}
from learn_sql_model.models.{{modelname.lower()}} import {{modelname}}Create
from learn_sql_model.models.{{modelname.lower()}} import {{modelname}}Read
from learn_sql_model.models.{{modelname.lower()}} import {{modelname}}Update
from learn_sql_model.models.{{modelname.lower()}} import {{modelname}}Delete

View file

@ -3,5 +3,10 @@ _exclude:
- README.md - README.md
- .git - .git
- copier.yml - copier.yml
name: modelname:
type: str type: str
_tasks:
- "cat .pyflyby-{{modelname}} >> .pyflyby"
- "sort -u -o .pyflyby .pyflyby"
- "rm .pyflyby-{{modelname}}"

View file

@ -0,0 +1,45 @@
from typing import Annotated
from fastapi import APIRouter, Depends
from sqlmodel import SQLModel
from learn_sql_model.api.user import oauth2_scheme
from learn_sql_model.config import Config, get_config
from learn_sql_model.models.{{modelname.lower()}} import {{modelname}}
{{modelname.lower()}}_router = APIRouter()
@{{modelname.lower()}}_router.on_event("startup")
def on_startup() -> None:
SQLModel.metadata.create_all(get_config().database.engine)
@{{modelname.lower()}}_router.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
@{{modelname.lower()}}_router.get("/{{modelname.lower()}}/{id}")
def get_{{modelname.lower()}}(id: int, config: Config = Depends(get_config)) -> {{modelname}}:
"get one {{modelname.lower()}}"
return {{modelname}}().get(id=id, config=config)
@{{modelname.lower()}}_router.get("/h/{id}")
def get_h(id: int, config: Config = Depends(get_config)) -> {{modelname}}:
"get one {{modelname.lower()}}"
return {{modelname}}().get(id=id, config=config)
@{{modelname.lower()}}_router.post("/{{modelname.lower()}}/")
def post_{{modelname.lower()}}({{modelname.lower()}}: {{modelname}}, config: Config = Depends(get_config)) -> {{modelname.lower()}}:
"read all the {{modelname.lower()}}s"
{{modelname.lower()}}.post(config=config)
return {{modelname.lower()}}
@{{modelname.lower()}}_router.get("/{{modelname.lower()}}s/")
def get_{{modelname.lower()}}s(config: Config = Depends(get_config)) -> list[{{modelname}}]:
"get all {{modelname.lower()}}s"
return {{modelname}}().get(config=config)

View file

@ -0,0 +1,107 @@
import sys
from typing import List, Optional, Union
from engorgio import engorgio
from rich.console import Console
import typer
from learn_sql_model.config import Config, get_config
from learn_sql_model.factories.{{modelname.lower()}} import {{modelname}}Factory
from learn_sql_model.factories.pet import PetFactory
from learn_sql_model.models.{{modelname.lower()}} import (
{{modelname}},
{{modelname}}Create,
{{modelname}}Delete,
{{modelname}}Read,
{{modelname}}Update,
)
{{modelname.lower()}}_app = typer.Typer()
@{{modelname.lower()}}_app.callback()
def {{modelname.lower()}}():
"model cli"
@{{modelname.lower()}}_app.command()
@engorgio(typer=True)
def get(
id: Optional[int] = typer.Argument(default=None),
config: Config = None,
) -> Union[{{modelname}}, List[{{modelname.lower()}}]]:
"get one {{modelname.lower()}}"
config.init()
{{modelname.lower()}} = {{modelname}}Read.get(id=id, config=config)
Console().print({{modelname.lower()}})
return {{modelname.lower()}}
@{{modelname.lower()}}_app.command()
@engorgio(typer=True)
def list(
where: Optional[str] = None,
config: Config = None,
offset: int = 0,
limit: Optional[int] = None,
) -> Union[{{modelname}}, List[{{modelname.lower()}}]]:
"get one {{modelname.lower()}}"
{{modelname.lower()}} = {{modelname}}Read.list(config=config, where=where, offset=offset, limit=limit)
Console().print({{modelname.lower()}})
return {{modelname.lower()}}
@{{modelname.lower()}}_app.command()
@engorgio(typer=True)
def create(
{{modelname.lower()}}: {{modelname}}Create,
config: Config = None,
) -> {{modelname}}:
"read all the {{modelname.lower()}}s"
# config.init()
{{modelname.lower()}} = {{modelname.lower()}}.post(config=config)
Console().print({{modelname.lower()}})
return {{modelname.lower()}}
@{{modelname.lower()}}_app.command()
@engorgio(typer=True)
def update(
{{modelname.lower()}}: {{modelname}}Update,
config: Config = None,
) -> {{modelname}}:
"read all the {{modelname.lower()}}s"
{{modelname.lower()}} = {{modelname.lower()}}.update(config=config)
Console().print({{modelname.lower()}})
return {{modelname.lower()}}
@{{modelname.lower()}}_app.command()
@engorgio(typer=True)
def delete(
{{modelname.lower()}}: {{modelname}}Delete,
config: Config = None,
) -> {{modelname}}:
"read all the {{modelname.lower()}}s"
# config.init()
{{modelname.lower()}} = {{modelname.lower()}}.delete(config=config)
return {{modelname.lower()}}
@{{modelname.lower()}}_app.command()
@engorgio(typer=True)
def populate(
{{modelname.lower()}}: {{modelname}},
n: int = 10,
) -> {{modelname}}:
"read all the {{modelname.lower()}}s"
config = get_config()
if config.env == "prod":
Console().print("populate is not supported in production")
sys.exit(1)
for {{modelname.lower()}} in {{modelname}}Factory().batch(n):
pet = PetFactory().build()
{{modelname.lower()}}.pet = pet
Console().print({{modelname.lower()}})
{{modelname.lower()}}.post(config=config)

View file

@ -0,0 +1,14 @@
from faker import Faker
from polyfactory.factories.pydantic_factory import ModelFactory
from learn_sql_model.models.{{modelname.lower()}} import {{modelname}}
class {{modelname}}Factory(ModelFactory[{{modelname.lower()}}]):
__model__ = {{modelname}}
__faker__ = Faker(locale="en_US")
__set_as_default_factory_for_type__ = True
id = None
__random_seed__ = 10

View file

@ -0,0 +1,99 @@
from typing import Optional
from fastapi import HTTPException
from pydantic import BaseModel
from sqlmodel import Field, Relationship, SQLModel, Session, select
from learn_sql_model.config import Config
from learn_sql_model.models.pet import Pet
class {{modelname}}Base(SQLModel, table=False):
class {{modelname}}({{modelname.lower()}}Base, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
class {{modelname}}Create({{modelname.lower()}}Base):
...
def post(self, config: Config) -> {{modelname}}:
config.init()
with Session(config.database.engine) as session:
db_{{modelname.lower()}} = {{modelname}}.from_orm(self)
session.add(db_{{modelname.lower()}})
session.commit()
session.refresh(db_{{modelname.lower()}})
return db_{{modelname.lower()}}
class {{modelname}}Read({{modelname.lower()}}Base):
id: int
@classmethod
def get(
cls,
config: Config,
id: int,
) -> {{modelname}}:
with config.database.session as session:
{{modelname.lower()}} = session.get({{modelname}}, id)
if not {{modelname.lower()}}:
raise HTTPException(status_code=404, detail="{{modelname}} not found")
return {{modelname.lower()}}
@classmethod
def list(
self,
config: Config,
where=None,
offset=0,
limit=None,
) -> {{modelname}}:
with config.database.session as session:
statement = select({{modelname}})
if where != "None":
from sqlmodel import text
statement = statement.where(text(where))
statement = statement.offset(offset).limit(limit)
{{modelname.lower()}}es = session.exec(statement).all()
return {{modelname.lower()}}es
class {{modelname}}Update(SQLModel):
# id is required to get the {{modelname.lower()}}
id: int
# all other fields, must match the model, but with Optional default None
def update(self, config: Config) -> {{modelname}}:
with Session(config.database.engine) as session:
db_{{modelname.lower()}} = session.get({{modelname}}, self.id)
if not db_{{modelname.lower()}}:
raise HTTPException(status_code=404, detail="{{modelname}} not found")
{{modelname.lower()}}_data = self.dict(exclude_unset=True)
for key, value in {{modelname.lower()}}_data.items():
if value is not None:
setattr(db_{{modelname.lower()}}, key, value)
session.add(db_{{modelname.lower()}})
session.commit()
session.refresh(db_{{modelname.lower()}})
return db_{{modelname.lower()}}
class {{modelname}}Delete(BaseModel):
id: int
def delete(self, config: Config) -> {{modelname}}:
config.init()
with Session(config.database.engine) as session:
{{modelname.lower()}} = session.get({{modelname}}, self.id)
if not {{modelname.lower()}}:
raise HTTPException(status_code=404, detail="{{modelname}} not found")
session.delete({{modelname.lower()}})
session.commit()
return {"ok": True}