Skip to content

How to use pydantic and sqlalchemy models with relationship #1645

@mxdev88

Description

@mxdev88

Hello,

I'm trying to pass pydantic models to sqlalchemy models based on sql-databases doc.

It works well for single models but fails to work with relationship. I'm expecting to receive an objet nesting several other objects in my endpoint.

Is there a way to do that using the BaseModel.dict() like in the documentation or do I need to map my pydantic model to my sqlalchemy model in order to achieve this?

Example

from typing import Optional, List

from fastapi import Depends, FastAPI, HTTPException

from pydantic import BaseModel

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, sessionmaker, relationship
from sqlalchemy import ForeignKey, Column, Integer, String

Base = declarative_base()


class Parent(Base):
    __tablename__ = "parents"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(255), index=True)

    children = relationship("Child", back_populates="parent")


class Child(Base):
    __tablename__ = "children"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(255), index=True)
    parent_id = Column('parent_id', Integer(), ForeignKey('parents.id'), nullable=False)

    parent = relationship("Parent", back_populates="children")


class ChildSchema(BaseModel):
    id: Optional[int]
    name: str

    class Config:
        orm_mode = True


class ParentSchema(BaseModel):
    id: Optional[int]
    name: str

    children: List[ChildSchema] = []

    class Config:
        orm_mode = True


SQLALCHEMY_DATABASE_URL = "sqlite:///./test_app.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base.metadata.create_all(bind=engine)

api = FastAPI()


def get_db():
    try:
        db = SessionLocal()
        yield db
    finally:
        db.close()


@api.post("/parents/", response_model=ParentSchema)
def post_parent(parent: ParentSchema, db: Session = Depends(get_db)):
    db_parent = Parent(**parent.dict())
    db.add(db_parent)
    db.commit()
    db.refresh(db_parent)
    return ParentSchema.from_orm(db_parent)


if __name__ == "__main__":
    from fastapi.testclient import TestClient

    client = TestClient(api)

    # this works
    data1 = """{"name": "string"}"""
    response = client.post("/parents/", data=data1)
    assert response.status_code == 200

    # this does not work
    data2 = """{"name": "string", "children": [{"name": "string"}]}"""
    response = client.post("/parents/", data=data2)
    assert response.status_code == 200

Description

data1 works well as expected.
data2 raise AttributeError: 'dict' object has no attribute '_sa_instance_state'

Environment

  • FastAPI Version: 0.58.0
  • Python version: Python 3.7.7

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions