Remix.run Logo
youdontknowjuli 10 hours ago

> (and some cool hackery to make SQLAlchemy feel very typed and work nicely with Pydantic).

Sounds interesting. Can you elaborate on the cool hackery? We introduced SQLModel recently but struggle in a few cases (e.g. multi-level joins). Do you know reference projects for SQLAlchemy and pydantic?

tasn 4 hours ago | parent [-]

My info is maybe a bit dated, as it's been a while since we wrote this hackery. We also adopted SQLModel at some point but we had to patch it to work well (I think some of my contributions are now in upstream). As for some of the hacks:

    def c(prop: t.Any) -> sa.Column:  # type: ignore
        return prop
To make it possible to access sqlmodel properties as columns for doing things like `in_` but still maintaining type safety.

Added types ourselves to the base model like this:

    __table__: t.ClassVar[sa.Table]
Added functions that help with typing like this:

    @classmethod
    async def _fetch_one(cls: t.Type[BaseT], db: BaseReadOnlySqlSession, query: Select) -> t.Optional[BaseT]:
        try:
            return (await db.execute(query)).scalar_one()
        except NoResultFound:
            return None
and stuff like this for relationships:

    def ezrelationship(
        model: t.Type[T_],
        id_our: t.Union[str, sa.Column],  # type: ignore
        id_other: t.Optional[t.Union[t.Any, sa.Column]] = None,  # type: ignore
    ) -> T_:
        if id_other is None:
            id_other = model.id
        return sqlm.Relationship(sa_relationship=relationship(model, primaryjoin=f"foreign({id_our}) == {id_other}"))


    def ezrelationship_back(
        id_our: t.Union[str, sa.Column],  # type: ignore
        id_other: t.Union[str, sa.Column],  # type: ignore
    ) -> t.Any:
        model, only_id2 = id_other.split(".")
        return sqlm.Relationship(
            sa_relationship=relationship(
                model,
                primaryjoin=f"foreign({id_our}) == {id_other}_id",
                back_populates=only_id2,
            )
        )

I hope this helps, I don't have time to find all the stuff, but we also hacked on SQLAlchemy a bit, and in other places.