fastapi – MissingGreenlet when trying to insert value in db [SQLAlchemy]

I’m new to ORMs and SQLAlchemy and I’m trying to insert some values ​​in the database but this error comes up

sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call await_() here. Was IO attempted in an unexpected place? (Background on this error at: https://sqlalche.me/e/14/xd2s)

This is my fastapi route

@router.post("", response_model=AnalysisRequestSchema, status_code=201)
    async def create_analysis_request(
    request_in: AnalysisRequestCreateSchema,
    session: AsyncSession = Depends(get_async_session),
    user: User = Depends(current_user),
) -> Any:

    analysis_request = AnalysisRequest(
        user_id=user.id,
        request_timestamp=func.now(),
    )

    session.add(analysis_request)
    await session.commit()

    # MOCKED DATA
    script_return_value = {
        "type": request_in.analysis.type,
        "status": "COMPLETED",
        "url": "https://www.google.com",
    }

    if script_return_value["status"] == "COMPLETED":

        analysis: Analysis = Analysis(
            type=script_return_value["type"],
            url=script_return_value["url"],
            analysis_request_id=analysis_request.id,
            creation_timestamp=func.now(),
            gp_year=request_in.analysis.gp_year,
            gp_round=request_in.analysis.gp_round,
            gp_session=request_in.analysis.gp_session,
        )

        session.add(analysis)
        await session.commit()

    for analysis_info in request_in.analysis.analysis_infos:
        info: AnalysisInfo = AnalysisInfo(
            type=analysis_info.type,
            value=analysis_info.value,
            analysis_id=analysis.id,
        )
        session.add(info)

    await session.commit()
    return analysis_request

ps: session.commit() is called to get the id of the object added the line before (Am I doing the right way?, using session.refresh(analysis_request) throw an error (object is not persitent))

This is how the connection is handled


async_engine = create_async_engine(settings.ASYNC_DATABASE_URL)
async_session_maker = sessionmaker(
    async_engine,
    class_=AsyncSession,
    expire_on_commit=False,
    autocommit=False,
    autoflush=False,
)

async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    async with async_session_maker() as session:
        yield session
 

These are my models

class AnalysisRequest(Base):
    __tablename__ = "analysis_request"

    __mapper_args__ = {"eager_defaults": True}

    id = Column(Integer, primary_key=True)
    user_id = Column(fastapi_users_db_sqlalchemy.GUID, ForeignKey("users.id"))
    request_timestamp = sa.Column(sa.DateTime(timezone=True), server_default=func.now())

    analysis = relationship("Analysis", backref="analysis_request", lazy="joined")

class Analysis(Base):
    __tablename__ = "analysis"

    # add this so that it can be accessed
    __mapper_args__ = {"eager_defaults": True}

    id = Column(Integer, primary_key=True)
    type = sa.Column(
        sa.Enum(AnalysisType),
        name="type",
        nullable=False,
        default=AnalysisType.TELEMETRY_COMPARISON,
    )
    url = Column(String)
    analysis_request_id = Column(Integer, ForeignKey("analysis_request.id"))
    creation_timestamp = sa.Column(sa.DateTime(timezone=True), server_default=func.now())

    # gp info
    gp_year = Column(Integer, nullable=False)
    gp_round = Column(String, nullable=False)
    gp_session = Column(String)

    analysis_infos = relationship("AnalysisInfo", backref="analysis", lazy="joined")

class AnalysisInfo(Base):
    __tablename__ = "analysis_info"

    id = Column(Integer, primary_key=True)
    type = sa.Column(
        sa.Enum(AnalysisInfoType),
        name="type",
        nullable=False,
        default=AnalysisInfoType.DRIVER_1,
    )

    value = Column(String)

    analysis_id = Column(Integer, ForeignKey("analysis.id"))

These are my pydantic schemas


# AnalysisRequest
class AnalysisRequestCreate(BaseModel):
    analysis: AnalysisCreate

    @validator("analysis")
    def validate_type(cls, v):
        return v


class AnalysisRequestUpdate(AnalysisRequestCreate):
    pass


class AnalysisRequest(AnalysisRequestCreate):
    id: int
    user_id: UUID3 | UUID4
    request_timestamp: datetime

    class Config:
        orm_mode = True
        arbitrary_types_allowed = True


# Analysis
class AnalysisCreate(BaseModel):
    type: AnalysisType
    gp_year: int
    gp_round: str
    gp_session: str
    analysis_infos: list[AnalysisInfoCreate]

    @validator("type")
    def validate_type(cls, v):
        if v.value not in AnalysisType.__members__:
            raise ValueError("Invalid type")
        return v

    @validator("gp_year")
    def validate_gp_year(cls, v, values, **kwargs):
        current_year = date.today().year
        try:
            year = int(v)
            if year < 1950 or year > int(current_year):
                raise ValueError("Invalid year")
        except ValueError:
            raise ValueError(
                f"Year must be an integer between 1950 and {current_year}, you entered {{v}}"
            )
        return v

    @validator("gp_round")
    def validate_gp_round(cls, v):
        try:
            round = int(v)
            if round < 1 or round > 25:
                raise ValueError("Invalid round")
        except ValueError:
            # could be a string
            if not isinstance(v, str):
                raise TypeError("You must enter a valid round")
            # check for valid round
            return v
        return v

    @validator("gp_session")
    def validate_gp_session(cls, v):
        if v not in ["FP1", "FP2", "FP3", "Q", "R"]:
            raise ValueError("Invalid session")
        return v

    @validator("analysis_infos")
    def validate_analysis_info(cls, v):
        """Validate that the list of analysis_infos is not empty."""
        if not v:
            raise ValueError("You must enter at least one analysis info")
        return v


class AnalysisUpdate(AnalysisCreate):
    pass


class Analysis(AnalysisCreate):
    id: int
    url: str
    request_id: int
    creation_timestamp: datetime

    class Config:
        orm_mode = True
        arbitrary_types_allowed = True


# AnalysisInfo
class AnalysisInfoCreate(BaseModel):
    type: AnalysisInfoType
    value: str

    @validator("type")
    def validate_type(cls, v):
        if v.value not in AnalysisInfoType.__members__:
            raise ValueError("Invalid type")
        return v

    @validator("value")
    def validate_value(cls, v):
        if not isinstance(v, str):
            raise TypeError(f"Info value must be a valid string, you entered {v}")
        return v


class AnalysisInfoUpdate(AnalysisInfoCreate):
    pass


class AnalysisInfo(AnalysisInfoCreate):
    id: int
    analysis_id: int

    class Config:
        orm_mode = True
        arbitrary_types_allowed = True


I spent litteraly hours reading docs but I just can’t make it work, any help would be much appreciated

Leave a Comment