Skip to content
\n

Description

\n

I am trying to work out what the best method is for creating SQLModel objects with nested objects beneath them.
\nThe example code in the tutorial shows reading data (such as with HeroPublicWithTeam) but not creating data.
\nMy code above seems to work ok - it is just the Models with Relationships in FastAPI code with the HeroCreate class modified to optionally allow a Team to also be created within the same API call - and team_id moved from HeroBase to Hero as it doesn't make sense for HeroCreate to have a team and a team_id in it.

\n

Is this the best way to achieve this?
\nOr am I missing a simpler way to do this that doesn't involve an AfterValidator which seems like an odd way to achieve this?
\nJust having team: TeamCreate | None = None leads to 'TeamCreate' object has no attribute '_sa_instance_state'
\nJust having team: Team | None = None means that id is part of the API for creating a team within the hero creation leading to the inevitable sqlalchemy.exc.IntegrityError: (psycopg.errors.UniqueViolation) duplicate key value violates unique constraint \"team_pkey\"

\n

Operating System

\n

Linux

\n

Operating System Details

\n

python-slim docker container

\n

SQLModel Version

\n

0.0.22

\n

Python Version

\n

3.12.6

\n

Additional Context

\n

No response

","upvoteCount":3,"answerCount":2,"acceptedAnswer":{"@type":"Answer","text":"

I ran into the same issue and was inspired by this thread. Here’s my refinement:

\n

I introduced Helper models that sit between the Create schemas and the table=True ORM models. These helpers use @field_validator(..., mode=\"after\") to recursively convert nested data into ORM-compatible objects — without mutating the original input schemas or embedding transformation logic into the ORM models.

\n

This approach keeps input validation clean while making the transformation layer explicit, type-safe, and scalable.

\n

✅ Minimal working example

\n
from sqlmodel import SQLModel, Field, Relationship\nfrom pydantic import field_validator\n\n# --- Input Schemas ---\nclass AddressCreate(SQLModel):\n    city: str\n\nclass UserCreate(SQLModel):\n    name: str\n    address: AddressCreate\n\n# --- ORM Models ---\nclass AddressModel(SQLModel, table=True):\n    id: int | None = Field(default=None, primary_key=True)\n    city: str\n\nclass UserModel(SQLModel, table=True):\n    id: int | None = Field(default=None, primary_key=True)\n    name: str\n    address_id: int | None = Field(default=None, foreign_key=\"addressmodel.id\")\n    address: AddressModel = Relationship()\n\n# --- Helper Models ---\nclass AddressCreateHelper(AddressCreate):\n    @field_validator(\"city\", mode=\"after\")\n    @classmethod\n    def to_model(cls, v, values):\n        return AddressModel(city=v)\n\nclass UserCreateHelper(UserCreate):\n    @field_validator(\"address\", mode=\"after\")\n    @classmethod\n    def to_model(cls, v):\n        return AddressModel.model_validate(AddressCreateHelper.model_validate(v))\n\n# --- Usage ---\npayload = {\"name\": \"Alice\", \"address\": {\"city\": \"Berlin\"}}\n\n# In the FastAPI route or controller layer\nuser_input = UserCreate.model_validate(payload)\n\n# In the transformation layer (e.g., service or use case layer)\nuser_helper = UserCreateHelper.model_validate(user_input)\n\n# In the persistence layer\nuser_model = UserModel.model_validate(user_helper)
\n

This keeps UserCreate, UserModel, and UserCreateHelper cleanly separated for API input, database persistence, and transformation logic — making the system easier to maintain and reason about.

","upvoteCount":2,"url":"https://github.com/fastapi/sqlmodel/discussions/1116#discussioncomment-13550625"}}}

Best way to add nested model with relationship in Create #1116

Answered by gzu300
jonyscathe asked this question in Questions
Discussion options

You must be logged in to vote

I ran into the same issue and was inspired by this thread. Here’s my refinement:

I introduced Helper models that sit between the Create schemas and the table=True ORM models. These helpers use @field_validator(..., mode="after") to recursively convert nested data into ORM-compatible objects — without mutating the original input schemas or embedding transformation logic into the ORM models.

This approach keeps input validation clean while making the transformation layer explicit, type-safe, and scalable.

✅ Minimal working example

from sqlmodel import SQLModel, Field, Relationship
from pydantic import field_validator

# --- Input Schemas ---
class AddressCreate(SQLModel):
    city: str

class 

Replies: 2 comments 2 replies

Comment options

You must be logged in to vote
0 replies
Comment options

You must be logged in to vote
2 replies
@jonyscathe
Comment options

@space-romn
Comment options

Answer selected by jonyscathe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
4 participants