First Check
Commit to Help
Example Code
# Sudo Code Based on Examples in DocsclassTeam(SQLModel, table=True):
id: Optional[int] =Field(default=None, primary_key=True)
name: strheadquarters: strheroes: List["Hero"] =Relationship(back_populates="team")
classHero(SQLModel, table=True):
id: Optional[int] =Field(default=None, primary_key=True)
name: strsecret_name: strage: Optional[int] =Noneteam_id: Optional[int] =Field(default=None, foreign_key="team.id")
team: Optional[Team] =Relationship(back_populates="heroes")
payload= {
"name": "Team Name",
"headquarters": "Whereever".
"heroes": [
"name": "Name 1"//OtherRequiedFields... 👇
]
}
withSession(engine) assession:
Team.create_all_nested(session, payload) # or something?Description
I would like to do what is described in FastAPI issue #2194
How to make nested sqlalchemy models from nested pydantic models (or python dicts) in a generic way and write them to the database in "one shot".
In the example above, I'd like to pass in the payload to a method and the following to occur.
- Create new Team entry
- Create Hero entry and/or Relate the existing Hero to the Team
Similarly, I'd like the same to happen on update. Effectively making writing to the SQL database akin to writing to MongoDB
I don't believe this is supported or haven't gotten it to work, but my main questions are.
- Is this supported?
- If no, is this a use-case you've thought of?
- Are you interested in a PR to support this either as a utility method or some sort of decorator?
Loving working with this so far, thanks for all your hard work!
Operating System
macOS
Operating System Details
No response
SQLModel Version
0.0.3
Python Version
3.9.6
Additional Context
I have accomplished this with SQLAlchemy in the past by using an auto_init decarator.
fromfunctoolsimportwrapsfromtypingimportUnionfromsqlalchemy.ormimportMANYTOMANY, MANYTOONE, ONETOMANYdefhandle_one_to_many_list(relation_cls, all_elements: list[dict]):
elems_to_create= []
updated_elems= []
foreleminall_elements:
elem_id=elem.get("id", None)
existing_elem=relation_cls.get_ref(match_value=elem_id)
ifexisting_elemisNone:
elems_to_create.append(elem)
else:
forkey, valueinelem.items():
setattr(existing_elem, key, value)
updated_elems.append(existing_elem)
new_elems= []
foreleminelems_to_create:
new_elems= [relation_cls(**elem) foreleminall_elements]
returnnew_elemsdefauto_init(exclude: Union[set, list] =None): # sourcery no-metrics"""Wraps the `__init__` method of a class to automatically set the common attributes. Args: exclude (Union[set, list], optional): [description]. Defaults to None. """exclude=excludeorset()
exclude.add("id")
defdecorator(init):
@wraps(init)defwrapper(self, *args, **kwargs): # sourcery no-metrics""" Custom initializer that allows nested children initialization. Only keys that are present as instance's class attributes are allowed. These could be, for example, any mapped columns or relationships. Code inspired from GitHub. Ref: https://github.com/tiangolo/fastapi/issues/2194 """cls=self.__class__model_columns=self.__mapper__.columnsrelationships=self.__mapper__.relationshipssession=kwargs.get("session", None)
forkey, valinkwargs.items():
ifkeyinexclude:
continueifnothasattr(cls, key):
continue# raise TypeError(f"Invalid keyword argument: {key}")ifkeyinmodel_columns:
setattr(self, key, val)
continueifkeyinrelationships:
relation_dir=relationships[key].direction.namerelation_cls=relationships[key].mapper.entityuse_list=relationships[key].uselistifrelation_dir==ONETOMANY.nameanduse_list:
instances=handle_one_to_many_list(relation_cls, val)
setattr(self, key, instances)
ifrelation_dir==ONETOMANY.nameandnotuse_list:
instance=relation_cls(**val)
setattr(self, key, instance)
elifrelation_dir==MANYTOONE.nameandnotuse_list:
ifisinstance(val, dict):
val=val.get("id")
ifvalisNone:
raiseValueError(f"Expected 'id' to be provided for {key}")
ifisinstance(val, (str, int)):
instance=relation_cls.get_ref(match_value=val, session=session)
setattr(self, key, instance)
elifrelation_dir==MANYTOMANY.name:
ifnotisinstance(val, list):
raiseValueError(f"Expected many to many input to be of type list for {key}")
iflen(val) >0andisinstance(val[0], dict):
val= [elem.get("id") foreleminval]
instances= [relation_cls.get_ref(elem, session=session) foreleminval]
setattr(self, key, instances)
returninit(self, *args, **kwargs)
returnwrapperreturndecoratorUsage
classAdminModel(SqlAlchemyBase, BaseMixins):
name=Column(String, index=True)
email=Column(String, unique=True, index=True)
password=Column(String)
is_superuser=Column(Boolean(), default=False)
@auto_init(exclude={'is_superuser'})def__init__(self, **_):
this.is_superuser=false@classmethoddefget_ref(cls, match_value: str, match_attr: str="id"):
withSessionLocal() assession:
eff_ref=getattr(cls, match_attr)
returnsession.query(cls).filter(eff_ref==match_value).one_or_none()
```decorator
First Check
Commit to Help
Example Code
Description
I would like to do what is described in FastAPI issue #2194
In the example above, I'd like to pass in the payload to a method and the following to occur.
Similarly, I'd like the same to happen on update. Effectively making writing to the SQL database akin to writing to MongoDB
I don't believe this is supported or haven't gotten it to work, but my main questions are.
Loving working with this so far, thanks for all your hard work!
Operating System
macOS
Operating System Details
No response
SQLModel Version
0.0.3
Python Version
3.9.6
Additional Context
I have accomplished this with SQLAlchemy in the past by using an
auto_initdecarator.Usage