Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Add example to use PydanticFactory with SQLAlchemy persistence #504

Open
WilliamDEdwards opened this issue Feb 25, 2024 · 5 comments
Labels
documentation Improvements or additions to documentation

Comments

@WilliamDEdwards
Copy link

WilliamDEdwards commented Feb 25, 2024

Summary

Hi,

I use FastAPI with a 'typical' structure: Pydantic schemas are converted to SQLAlchemy models.

For tests, currently, SQLAlchemy models are created in fixtures. Obviously, as the amount of models and validations grows, this doesn't scale. Therefore, I will use per-test objects created by factories.

polyfactory looks ideal, but one thing is missing: I would like to pass a Pydantic schema (PydanticFactory), then convert it to an SQLAlchemy model and create it, using a custom (CRUD layer) implementation.

Reason: Pydantic schemas validate business logic that should not be violated in tests (which happens by creating SQLAlchemy models with only data validation).

This looks relatively easy to implement by passing a custom handler to __sync_persistence__ on a factory (https://polyfactory.litestar.dev/latest/usage/configuration.html#persistence-handlers). (Still need to figure out how to subclass my factories from a base class so I don't have to repeat that...)

Instead of writing the implementation myself, would the maintainers be interested in adding a first-party supported example to the documentation? I could imagine this use case is quite common, as this is relevant for basically all larger FastAPI/Pydantic/SQLAlchemy projects.


Note

While we are open for sponsoring on GitHub Sponsors and
OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.

Check out all issues funded or available for funding on our Polar.sh dashboard

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
  • This, along with engagement in the community, helps us know which features are a priority to our users.
Fund with Polar
@WilliamDEdwards WilliamDEdwards added the documentation Improvements or additions to documentation label Feb 25, 2024
@WilliamDEdwards WilliamDEdwards changed the title Docs: Add example to use Pydantic + SQLAlchemy Docs: Add example to use PydanticFactory with SQLAlchemy persistence Feb 25, 2024
@guacs
Copy link
Member

guacs commented Feb 26, 2024

If my understanding is correct, you want a way to convert pydantic models into sqla models? Could you show an example of what it is that you're trying to achieve?

@WilliamDEdwards
Copy link
Author

If my understanding is correct, you want a way to convert pydantic models into sqla models? Could you show an example of what it is that you're trying to achieve?

I would like to use PydanticFactory, then save the corresponding SQLAlchemy model to the database. According to the documentation, this can be achieved by adding a persistence handler to PydanticFactory. I would then convert and save in the save method.

@guacs
Copy link
Member

guacs commented Feb 27, 2024

Aah gotcha. And how is that translation from pydantic model to SQL model happening?

@WilliamDEdwards
Copy link
Author

WilliamDEdwards commented Feb 27, 2024

Aah gotcha. And how is that translation from pydantic model to SQL model happening?

For SQLAlchemy to Pydantic: https://docs.pydantic.dev/latest/concepts/models/#arbitrary-class-instances

The other way around, which is what I'm looking for:

from pydantic import BaseModel, ConfigDict

from sqlalchemy import create_engine
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import Integer
from sqlalchemy.orm import declarative_base, sessionmaker

engine = create_engine("mysql+pymysql://username:password@host/database")
database_session = sessionmaker(bind=engine)()

Base = declarative_base()

class TableOrm(Base):
    __tablename__ = "table"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)

class TablePydantic(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: int

pydantic_schema = TablePydantic(id=1)
pydantic_schema_as_dict = pydantic_schema.model_dump(mode="json")  # mode ensures only JSON-serializable types
sqlalchemy_object = TableOrm(**pydantic_schema_as_dict)

database_session.add(sqlalchemy_object)
database_session.commit()
database_session.refresh(sqlalchemy_object)

@guacs
Copy link
Member

guacs commented Feb 28, 2024

Hm...I'm not a 100% sure on whether we should add this to the documentation. That being said, I do see the benefit of this and agree that it may be a relatively common use case (especially for those using FastAPI like you said). So I'm kind of 50/50 on this. Anyone have any opinions on this, @litestar-org/members?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

2 participants