-
-
Notifications
You must be signed in to change notification settings - Fork 799
Description
First Check
- I added a very descriptive title to this issue.
- I used the GitHub search to find a similar issue and didn't find it.
- I searched the SQLModel documentation, with the integrated search.
- I already searched in Google "How to X in SQLModel" and didn't find any information.
- I already read and followed all the tutorial in the docs and didn't find an answer.
- I already checked if it is not related to SQLModel but to Pydantic.
- I already checked if it is not related to SQLModel but to SQLAlchemy.
Commit to Help
- I commit to help with one of those options 👆
Example Code
from typing import Optional, Union
from fastapi import FastAPI, Depends, HTTPException
from sqlmodel import Field, Session, SQLModel, create_engine, select
class ActiveRecord(SQLModel):
@classmethod
def by_id(cls, id: int, session):
obj = session.get(cls, id)
if obj is None:
raise HTTPException(status_code=404, detail=f"{cls.__name__} with id {id} not found")
return obj
@classmethod
def all(cls, session):
return session.exec(select(cls)).all()
@classmethod
def create(cls, source: Union[dict, SQLModel], session):
if isinstance(source, SQLModel):
obj = cls.from_orm(source)
elif isinstance(source, dict):
obj = cls.parse_obj(source)
session.add(obj)
session.commit()
session.refresh(obj)
return obj
def save(self, session):
session.add(self)
session.commit()
session.refresh(self)
def update(self, source: Union[dict, SQLModel], session):
if isinstance(source, SQLModel):
source = source.dict(exclude_unset=True)
for key, value in source.items():
setattr(self, key, value)
self.save(session)
def delete(self, session):
session.delete(self)
session.commit()
class HeroBase(SQLModel):
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
class HeroCreate(HeroBase):
pass
class HeroRead(HeroBase):
id: int
class HeroUpdate(SQLModel):
name: Optional[str] = None
secret_name: Optional[str] = None
age: Optional[int] = None
class Hero(HeroBase, ActiveRecord, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, echo=True, connect_args=connect_args)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
app = FastAPI()
@app.on_event("startup")
def on_startup():
create_db_and_tables()
@app.post("/heroes/", response_model=HeroRead)
def create_hero(hero: HeroCreate, session: Session = Depends(get_session)):
return Hero.create(hero, session)
@app.get("/heroes/", response_model=list[HeroRead])
def read_heroes(session: Session = Depends(get_session)):
return Hero.all(session)
@app.get("/heroes/{id}", response_model=HeroRead)
def read_heroes(id: int, session: Session = Depends(get_session)):
return Hero.by_id(id, session)
@app.patch("/heroes/{id}", response_model=HeroRead)
def read_heroes(id: int, hero: HeroUpdate, session: Session = Depends(get_session)):
db_hero = Hero.by_id(id, session)
db_hero.update(hero, session)
return db_heroDescription
Hi all,
I'm fairly new to FastAPI and SQLModel, but I really like these libraries. After porting two Flask+SQLAlchemy projects to FastAPI+SQLModel, I noticed that I write lots of boilerplate code to implement basic model functions like create, save, delete or update. I was surprised that the examples in the documentation implement CRUD functionality with functions rather than member functions of the models.
So I wrote a class called ActiveRecord to be used as a mixin for SQLModel classes, which adds these functions and can be reused for different models and projects. In my code above you see it being used with the multi-model Hero example in the documentation, which shortens the remaining code (compare https://sqlmodel.tiangolo.com/tutorial/fastapi/multiple-models)
Using something like this in all of my SQLModel classes where table=True seems so obvious, that I suspect that there's either something I'm missing here, or this is functionality that's really missing in SQLModel. Why doesn't the SQLModel class have such functions? How do you do that? Do you think this is worth contributing? I guess, at least I could clean it up and share it as gist.
Thanks for any feedback,
Boris
Operating System
macOS
Operating System Details
No response
SQLModel Version
0.0.6
Python Version
3.9.5
Additional Context
No response