sqlmodel icon indicating copy to clipboard operation
sqlmodel copied to clipboard

Is it possible to instantiate a SQLModel object with relationships from a `dict` type?

Open JLHasson opened this issue 3 years ago • 6 comments

First Check

  • [X] I added a very descriptive title to this issue.
  • [X] I used the GitHub search to find a similar issue and didn't find it.
  • [X] I searched the SQLModel documentation, with the integrated search.
  • [X] I already searched in Google "How to X in SQLModel" and didn't find any information.
  • [X] I already read and followed all the tutorial in the docs and didn't find an answer.
  • [X] I already checked if it is not related to SQLModel but to Pydantic.
  • [X] I already checked if it is not related to SQLModel but to SQLAlchemy.

Commit to Help

  • [X] I commit to help with one of those options 👆

Example Code

from typing import List, Optional

from sqlmodel import Field, Relationship, Session, SQLModel, create_engine

from pydantic import BaseModel


class Team(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)

    heroes: List["Hero"] = Relationship(back_populates="team")


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)

    team_id: Optional[int] = Field(default=None, foreign_key="team.id")
    team: Optional[Team] = Relationship(back_populates="heroes")


class Hero2(BaseModel):
    id: int
    team: Team


class Team2(BaseModel):
    id: int
    heroes: List["Hero2"]

# What I would like to be able to do
d = {"id": 123, "team": {"id": 124}}  # fails
# d = {"id": 123, "team": Team(**{"id": 124})}  # succeeds
h = Hero(**d)
print(h)

# The same idea but using vanilla Pydantic models
d2 = {"id": 123, "team": {"id": 124}}
h2 = Hero2(**d2)
print(h2)

Description

In Pydantic it is possible to instantiate objects directly from dicts (i.e. JSON) via ClassName(**dict). This also works for objects with nested objects (i.e. relationships in SQLModel). Is it possible to do the same in SQLModel? I would like to take a JSON like {"id": 123, "relationship_obj": {"id": 456, ...}} and have SQLModel correctly create the relationship model based on the type of the field & the key of the dict passed in.

The error received is:

  File "/.../venv/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", lin
e 1729, in emit_backref_from_scalar_set_event
    instance_state(child),
AttributeError: 'dict' object has no attribute '_sa_instance_state'

Operating System

Linux

Operating System Details

No response

SQLModel Version

0.0.6

Python Version

Python 3.9.5

Additional Context

No response

JLHasson avatar May 17 '22 04:05 JLHasson

One thing I don't understand here is how FastAPI instantiates SQLModel objects from the JSON request object. I think there must be support for what I want to achieve, it's just not easily accessible or well documented.

JLHasson avatar May 17 '22 22:05 JLHasson

Hm I thought it works, am using from_orm in our service too, but not sure if the relationships work in this - is discussed there, saying they would not work: https://github.com/tiangolo/sqlmodel/issues/224

I do have them in my return values.

antont avatar Jun 24 '22 07:06 antont

I am noticing a similar issue, even doing Hero(team=Team(...)).team will give a Hero object has no attribute team.. Any way to fix this?

hugolytics avatar Jul 08 '22 13:07 hugolytics

I just ran into this same issue. Drove me nuts trying to figure it out. I imagine many JSON posts to FastAPI have such nested statements so I'm surprised that this is an issue. I keep thinking I must be doing something wrong.

sychou avatar Jul 20 '22 02:07 sychou

I think this is essentially a duplicate of this:

  • #6

I agree that this is worthwhile, but I suggest closing this duplicate issue.

daniil-berg avatar Sep 07 '22 16:09 daniil-berg